TI has a card that supports different HT caps according to operating
mode. Allow overriding the per-band HT capabilities in struct wiphy
with HT-caps in the vif, to be set by the driver on the mac80211 callback
add_interface.
The GET_WIPHY command is modified to consider the interface when
advertising HW capabilities to usermode. Luckily, today wpa_s sends
the interface as well so no modifications are required there.
Currently "iw phy" will list a card using this as a 11g card. This
will be addressed at a later date (probably by a new command).
Arik Nemtsov (4):
nl80211: accept optional netdev in send_wiphy
cfg80211: support unused HT-cap-per-band configuration
cfg80211: allow driver to implement HT capabilities per netdev
mac80211: allow setting HT capabilities per vif
include/net/cfg80211.h | 21 ++++++++++-
include/net/mac80211.h | 4 ++
net/mac80211/cfg.c | 11 ++++++
net/mac80211/ht.c | 19 ++++++----
net/mac80211/ibss.c | 11 ++++--
net/mac80211/ieee80211_i.h | 6 ++-
net/mac80211/iface.c | 10 +++--
net/mac80211/main.c | 2 +-
net/mac80211/mesh.c | 9 +++--
net/mac80211/mlme.c | 20 +++++++---
net/mac80211/scan.c | 5 ++-
net/mac80211/util.c | 31 +++++++++++++---
net/wireless/chan.c | 10 +++--
net/wireless/core.c | 3 +-
net/wireless/nl80211.c | 87 +++++++++++++++++++++++++++++++++++---------
net/wireless/wext-compat.c | 4 +-
16 files changed, 194 insertions(+), 59 deletions(-)
--
1.7.9.5
On Thu, Jul 5, 2012 at 2:48 PM, Arik Nemtsov <[email protected]> wrote:
> On Thu, Jul 5, 2012 at 10:42 AM, Johannes Berg
> <[email protected]> wrote:
>> On Wed, 2012-07-04 at 18:58 +0300, Arik Nemtsov wrote:
>>
>>> void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled);
>>> + struct ieee80211_sta_ht_cap *(*get_ht_cap)(struct wiphy *wiphy,
>>> + struct net_device *dev,
>>> + enum ieee80211_band band);
>>
>> Ok so ... I really don't like this whole concept to start with, but you
>> convinced me it was needed. This is just ugly though. Now you can
>> suddenly change the HT caps randomly at runtime? That's ... distressing
>> for userspace!
>>
>> Is there any chance this can just be a pointer inside the wdev struct?
>> Then at least we can verify it in NETDEV_UP if you had invalid HT caps
>> originally, etc.
>
> Yea I guess it can be confusing. Sure we can make it part of the wdev
> and add some verification. Propagate the pointers from the mac80211
> vif in ieee80211_do_open().
> But does cfg80211 code even have a hook on NETDEV_UP?
I found it cfg80211_netdev_notifier_call(), sorry for the hassle. I'll
change to wdev.
When a specific interface is requested in a GET_WIPHY command, propagate
the interface to the send_wiphy. This allows interface specific abilities
to be sent up.
This change requires us to implement a new internal flag for NL commands
for optionally getting the netdev. This flag is used for GET_WIPHY.
Signed-off-by: Arik Nemtsov <[email protected]>
---
net/wireless/nl80211.c | 38 +++++++++++++++++++++++++++++++++-----
1 file changed, 33 insertions(+), 5 deletions(-)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 77102e6..010ff47 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -791,7 +791,8 @@ nla_put_failure:
}
static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
- struct cfg80211_registered_device *dev)
+ struct cfg80211_registered_device *dev,
+ struct net_device *netdev)
{
void *hdr;
struct nlattr *nl_bands, *nl_band;
@@ -1207,7 +1208,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
continue;
if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
- dev) < 0) {
+ dev, NULL) < 0) {
idx--;
break;
}
@@ -1223,12 +1224,14 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
{
struct sk_buff *msg;
struct cfg80211_registered_device *dev = info->user_ptr[0];
+ struct net_device *netdev = info->user_ptr[1];
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
- if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
+ if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev,
+ netdev) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -6648,6 +6651,7 @@ static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
+#define NL80211_FLAG_OPT_NETDEV_UP 0x10 /* get netdev if given and up */
#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
NL80211_FLAG_CHECK_NETDEV_UP)
@@ -6688,6 +6692,30 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
}
info->user_ptr[0] = rdev;
info->user_ptr[1] = dev;
+ } else if (ops->internal_flags & NL80211_FLAG_OPT_NETDEV_UP) {
+ if (info->attrs[NL80211_ATTR_IFINDEX]) {
+ err = get_rdev_dev_by_ifindex(genl_info_net(info),
+ info->attrs, &rdev,
+ &dev);
+ /* only get the netdev if up */
+ if (!err && !netif_running(dev)) {
+ dev_put(dev);
+ dev = NULL;
+ }
+ } else {
+ dev = NULL;
+ rdev = cfg80211_get_dev_from_info(genl_info_net(info),
+ info);
+ err = IS_ERR(rdev) ? PTR_ERR(rdev) : 0;
+ }
+ if (err) {
+ if (rtnl)
+ rtnl_unlock();
+ return err;
+ }
+
+ info->user_ptr[0] = rdev;
+ info->user_ptr[1] = dev; /* might be null */
}
return 0;
@@ -6711,7 +6739,7 @@ static struct genl_ops nl80211_ops[] = {
.dumpit = nl80211_dump_wiphy,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
- .internal_flags = NL80211_FLAG_NEED_WIPHY,
+ .internal_flags = NL80211_FLAG_OPT_NETDEV_UP,
},
{
.cmd = NL80211_CMD_SET_WIPHY,
@@ -7262,7 +7290,7 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
if (!msg)
return;
- if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
+ if (nl80211_send_wiphy(msg, 0, 0, 0, rdev, NULL) < 0) {
nlmsg_free(msg);
return;
}
--
1.7.9.5
On Fri, 2012-07-06 at 10:41 +0300, Arik Nemtsov wrote:
> >> You mean move out ht_supported?
> >
> > Yeah, I think that'd make more sense? Have it in the sband directly.
>
> It does make more sense. But I was thinking there might be code
> setting this dynamically in some drivers, so the changes would be
> non-trivial. The union was sort of a compromise.
Well the thing is ... it'll always stay that way if we don't fix it now.
I don't think it's a huge deal to change all the drivers, though I'll
admit that it might be a little tricky in some drivers.
> Maybe move it out for vht_cap for now? :)
Here's to hoping you fix the chips in the future instead! ;-)
> > I think the function should restrict to NO_HT in case it doesn't have
> > caps, otherwise it can't properly restrict. I suppose that means you
> > won't be able to do HT monitoring with your device, but I can live with
> > that.
>
> Actually the ht_supported flag is still global, so HT monitoring is
> possible, just not HT40 monitoring I think. We'll fix that if the need
> arises (or just boot the driver with fixed caps via a module param).
Yeah I guess that's true.
johannes
On Wed, 2012-07-04 at 18:58 +0300, Arik Nemtsov wrote:
> struct ieee80211_sta_ht_cap {
> + bool ht_supported; /* this must be first */
> u16 cap; /* use IEEE80211_HT_CAP_ */
> - bool ht_supported;
> - struct ieee80211_sta_ht_cap ht_cap;
> + union {
> + struct ieee80211_sta_ht_cap ht_cap;
> + bool ht_supported;
> + };
> + bool ht_cap_invalid;
Yuck. Why use the union at all? You could just put the ht_cap_invalid
into the ht_cap struct, and it wouldn't even take space, there's
padding :-)
> @@ -30,13 +31,16 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
> chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
> return NULL;
>
> - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
> + sband = rdev->wiphy.bands[chan->band];
> + ht_cap = &sband->ht_cap;
>
> if (channel_type != NL80211_CHAN_NO_HT) {
> - if (!ht_cap->ht_supported)
> + if (!sband->ht_supported)
> return NULL;
>
> - if (channel_type != NL80211_CHAN_HT20 &&
> + /* this check is ignored when per-band HT caps are not used */
> + if (!sband->ht_cap_invalid &&
> + channel_type != NL80211_CHAN_HT20 &&
This is also major confusion, it seems you should roll 2/3 into one
patch?
johannes
On Fri, Jul 6, 2012 at 9:59 AM, Johannes Berg <[email protected]> wrote:
> On Thu, 2012-07-05 at 14:25 +0300, Arik Nemtsov wrote:
>
>> > Yuck. Why use the union at all? You could just put the ht_cap_invalid
>> > into the ht_cap struct, and it wouldn't even take space, there's
>> > padding :-)
>>
>> The point is - we still want to use ht_cap.ht_supported, even when
>> ht_cap is invalid. I was afraid there would be some confusion, hence
>> one can set:
>> band.ht_supported = true;
>> band.ht_cap_invalid = false;
>>
>> And the union means we don't have to change old drivers.
>>
>> Also putting ht_cap_invalid inside the struct creates strange corner
>> cases. One could mark the vif ht_cap as invalid as well, but the code
>> would just ignore it.
>
> Good point. But then let's just move it out and change drivers ...
> that's much cleaner I think.
You mean move out ht_supported?
>
>
>> >> @@ -30,13 +31,16 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
>> >> chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
>> >> return NULL;
>> >>
>> >> - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
>> >> + sband = rdev->wiphy.bands[chan->band];
>> >> + ht_cap = &sband->ht_cap;
>> >>
>> >> if (channel_type != NL80211_CHAN_NO_HT) {
>> >> - if (!ht_cap->ht_supported)
>> >> + if (!sband->ht_supported)
>> >> return NULL;
>> >>
>> >> - if (channel_type != NL80211_CHAN_HT20 &&
>> >> + /* this check is ignored when per-band HT caps are not used */
>> >> + if (!sband->ht_cap_invalid &&
>> >> + channel_type != NL80211_CHAN_HT20 &&
>> >
>> > This is also major confusion, it seems you should roll 2/3 into one
>> > patch?
>>
>> I'm not sure that would help. rdev_freq_to_chan doesn't get the
>> net_device, so it can't get the HT caps. I guess I can add the
>> net_device as an optional argument where it is used and check the
>> caps. OTOH it seem like a sanity check we can do without?
>
> I don't think we should do without that check? If you request say AP
> operation on 40 MHz then you absolutely rely on that happening or
> returning an error, not randomly operating in legacy mode!
Sure. I'll send the netdev to the function where possible (I think
everywhere except the virtual monitor).
On Thu, Jul 5, 2012 at 10:42 AM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2012-07-04 at 18:58 +0300, Arik Nemtsov wrote:
>
>> void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled);
>> + struct ieee80211_sta_ht_cap *(*get_ht_cap)(struct wiphy *wiphy,
>> + struct net_device *dev,
>> + enum ieee80211_band band);
>
> Ok so ... I really don't like this whole concept to start with, but you
> convinced me it was needed. This is just ugly though. Now you can
> suddenly change the HT caps randomly at runtime? That's ... distressing
> for userspace!
>
> Is there any chance this can just be a pointer inside the wdev struct?
> Then at least we can verify it in NETDEV_UP if you had invalid HT caps
> originally, etc.
Yea I guess it can be confusing. Sure we can make it part of the wdev
and add some verification. Propagate the pointers from the mac80211
vif in ieee80211_do_open().
But does cfg80211 code even have a hook on NETDEV_UP?
On Fri, Jul 6, 2012 at 10:34 AM, Johannes Berg
<[email protected]> wrote:
> On Fri, 2012-07-06 at 10:30 +0300, Arik Nemtsov wrote:
>
>> >> Also putting ht_cap_invalid inside the struct creates strange corner
>> >> cases. One could mark the vif ht_cap as invalid as well, but the code
>> >> would just ignore it.
>> >
>> > Good point. But then let's just move it out and change drivers ...
>> > that's much cleaner I think.
>>
>> You mean move out ht_supported?
>
> Yeah, I think that'd make more sense? Have it in the sband directly.
It does make more sense. But I was thinking there might be code
setting this dynamically in some drivers, so the changes would be
non-trivial. The union was sort of a compromise.
Maybe move it out for vht_cap for now? :)
>
>
>> >> >> @@ -30,13 +31,16 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
>> >> >> chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
>> >> >> return NULL;
>> >> >>
>> >> >> - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
>> >> >> + sband = rdev->wiphy.bands[chan->band];
>> >> >> + ht_cap = &sband->ht_cap;
>> >> >>
>> >> >> if (channel_type != NL80211_CHAN_NO_HT) {
>> >> >> - if (!ht_cap->ht_supported)
>> >> >> + if (!sband->ht_supported)
>> >> >> return NULL;
>> >> >>
>> >> >> - if (channel_type != NL80211_CHAN_HT20 &&
>> >> >> + /* this check is ignored when per-band HT caps are not used */
>> >> >> + if (!sband->ht_cap_invalid &&
>> >> >> + channel_type != NL80211_CHAN_HT20 &&
>> >> >
>> >> > This is also major confusion, it seems you should roll 2/3 into one
>> >> > patch?
>> >>
>> >> I'm not sure that would help. rdev_freq_to_chan doesn't get the
>> >> net_device, so it can't get the HT caps. I guess I can add the
>> >> net_device as an optional argument where it is used and check the
>> >> caps. OTOH it seem like a sanity check we can do without?
>> >
>> > I don't think we should do without that check? If you request say AP
>> > operation on 40 MHz then you absolutely rely on that happening or
>> > returning an error, not randomly operating in legacy mode!
>>
>> Sure. I'll send the netdev to the function where possible (I think
>> everywhere except the virtual monitor).
>
> I think the function should restrict to NO_HT in case it doesn't have
> caps, otherwise it can't properly restrict. I suppose that means you
> won't be able to do HT monitoring with your device, but I can live with
> that.
Actually the ht_supported flag is still global, so HT monitoring is
possible, just not HT40 monitoring I think. We'll fix that if the need
arises (or just boot the driver with fixed caps via a module param).
Allow drivers not to use the HT capabilities given per band in struct
wiphy. This can be indicated by setting ht_cap_invalid to true for the
wiphy.
In any case the band will contain a boolean value indicating if HT is
supported.
Signed-off-by: Arik Nemtsov <[email protected]>
---
include/net/cfg80211.h | 14 ++++++++++++--
net/wireless/chan.c | 10 +++++++---
net/wireless/core.c | 3 ++-
net/wireless/wext-compat.c | 4 ++--
4 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0b564e8..7b24d88 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -205,8 +205,8 @@ struct ieee80211_rate {
* @mcs: Supported MCS rates
*/
struct ieee80211_sta_ht_cap {
+ bool ht_supported; /* this must be first */
u16 cap; /* use IEEE80211_HT_CAP_ */
- bool ht_supported;
u8 ampdu_factor;
u8 ampdu_density;
struct ieee80211_mcs_info mcs;
@@ -243,6 +243,12 @@ struct ieee80211_sta_vht_cap {
* rates" IE, i.e. CCK rates first, then OFDM.
* @n_bitrates: Number of bitrates in @bitrates
* @ht_cap: HT capabilities in this band
+ * @ht_supported: Gives general HT support status in case HT capabilities
+ * are set per &struct net_device, and @ht_cap is not used.
+ * @ht_cap_invalid: Indicates the @ht_cap field of the band is not used.
+ * This should be set for devices implementing the get_ht_cap() op
+ * for changing the HT capabilities per interface.
+ * @vht_cap: VHT capabilities in this band
*/
struct ieee80211_supported_band {
struct ieee80211_channel *channels;
@@ -250,7 +256,11 @@ struct ieee80211_supported_band {
enum ieee80211_band band;
int n_channels;
int n_bitrates;
- struct ieee80211_sta_ht_cap ht_cap;
+ union {
+ struct ieee80211_sta_ht_cap ht_cap;
+ bool ht_supported;
+ };
+ bool ht_cap_invalid;
struct ieee80211_sta_vht_cap vht_cap;
};
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 434c56b..09b0012 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -16,6 +16,7 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
{
struct ieee80211_channel *chan;
struct ieee80211_sta_ht_cap *ht_cap;
+ struct ieee80211_supported_band *sband;
chan = ieee80211_get_channel(&rdev->wiphy, freq);
@@ -30,13 +31,16 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
return NULL;
- ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
+ sband = rdev->wiphy.bands[chan->band];
+ ht_cap = &sband->ht_cap;
if (channel_type != NL80211_CHAN_NO_HT) {
- if (!ht_cap->ht_supported)
+ if (!sband->ht_supported)
return NULL;
- if (channel_type != NL80211_CHAN_HT20 &&
+ /* this check is ignored when per-band HT caps are not used */
+ if (!sband->ht_cap_invalid &&
+ channel_type != NL80211_CHAN_HT20 &&
(!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
return NULL;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index e13365f..f8c48e6 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -482,10 +482,11 @@ int wiphy_register(struct wiphy *wiphy)
* Since cfg80211_disable_40mhz_24ghz is global, we can
* modify the sband's ht data even if the driver uses a
* global structure for that.
+ * Don't do anything if the driver uses per-vif HT caps.
*/
if (cfg80211_disable_40mhz_24ghz &&
band == IEEE80211_BAND_2GHZ &&
- sband->ht_cap.ht_supported) {
+ sband->ht_cap.ht_supported && !sband->ht_cap_invalid) {
sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
}
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 7df42f5..f3781ef 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -34,7 +34,7 @@ int cfg80211_wext_giwname(struct net_device *dev,
sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ];
if (sband) {
is_a = true;
- is_ht |= sband->ht_cap.ht_supported;
+ is_ht |= sband->ht_supported;
}
sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ];
@@ -47,7 +47,7 @@ int cfg80211_wext_giwname(struct net_device *dev,
if (sband->bitrates[i].bitrate == 60)
is_g = true;
}
- is_ht |= sband->ht_cap.ht_supported;
+ is_ht |= sband->ht_supported;
}
strcpy(name, "IEEE 802.11");
--
1.7.9.5
On Thu, 2012-07-05 at 14:25 +0300, Arik Nemtsov wrote:
> > Yuck. Why use the union at all? You could just put the ht_cap_invalid
> > into the ht_cap struct, and it wouldn't even take space, there's
> > padding :-)
>
> The point is - we still want to use ht_cap.ht_supported, even when
> ht_cap is invalid. I was afraid there would be some confusion, hence
> one can set:
> band.ht_supported = true;
> band.ht_cap_invalid = false;
>
> And the union means we don't have to change old drivers.
>
> Also putting ht_cap_invalid inside the struct creates strange corner
> cases. One could mark the vif ht_cap as invalid as well, but the code
> would just ignore it.
Good point. But then let's just move it out and change drivers ...
that's much cleaner I think.
> >> @@ -30,13 +31,16 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
> >> chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
> >> return NULL;
> >>
> >> - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
> >> + sband = rdev->wiphy.bands[chan->band];
> >> + ht_cap = &sband->ht_cap;
> >>
> >> if (channel_type != NL80211_CHAN_NO_HT) {
> >> - if (!ht_cap->ht_supported)
> >> + if (!sband->ht_supported)
> >> return NULL;
> >>
> >> - if (channel_type != NL80211_CHAN_HT20 &&
> >> + /* this check is ignored when per-band HT caps are not used */
> >> + if (!sband->ht_cap_invalid &&
> >> + channel_type != NL80211_CHAN_HT20 &&
> >
> > This is also major confusion, it seems you should roll 2/3 into one
> > patch?
>
> I'm not sure that would help. rdev_freq_to_chan doesn't get the
> net_device, so it can't get the HT caps. I guess I can add the
> net_device as an optional argument where it is used and check the
> caps. OTOH it seem like a sanity check we can do without?
I don't think we should do without that check? If you request say AP
operation on 40 MHz then you absolutely rely on that happening or
returning an error, not randomly operating in legacy mode!
johannes
On Thu, Jul 5, 2012 at 10:39 AM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2012-07-04 at 18:58 +0300, Arik Nemtsov wrote:
>
>> struct ieee80211_sta_ht_cap {
>> + bool ht_supported; /* this must be first */
>> u16 cap; /* use IEEE80211_HT_CAP_ */
>> - bool ht_supported;
>
>
>> - struct ieee80211_sta_ht_cap ht_cap;
>> + union {
>> + struct ieee80211_sta_ht_cap ht_cap;
>> + bool ht_supported;
>> + };
>> + bool ht_cap_invalid;
>
>
> Yuck. Why use the union at all? You could just put the ht_cap_invalid
> into the ht_cap struct, and it wouldn't even take space, there's
> padding :-)
The point is - we still want to use ht_cap.ht_supported, even when
ht_cap is invalid. I was afraid there would be some confusion, hence
one can set:
band.ht_supported = true;
band.ht_cap_invalid = false;
And the union means we don't have to change old drivers.
Also putting ht_cap_invalid inside the struct creates strange corner
cases. One could mark the vif ht_cap as invalid as well, but the code
would just ignore it.
>
>
>> @@ -30,13 +31,16 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
>> chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
>> return NULL;
>>
>> - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
>> + sband = rdev->wiphy.bands[chan->band];
>> + ht_cap = &sband->ht_cap;
>>
>> if (channel_type != NL80211_CHAN_NO_HT) {
>> - if (!ht_cap->ht_supported)
>> + if (!sband->ht_supported)
>> return NULL;
>>
>> - if (channel_type != NL80211_CHAN_HT20 &&
>> + /* this check is ignored when per-band HT caps are not used */
>> + if (!sband->ht_cap_invalid &&
>> + channel_type != NL80211_CHAN_HT20 &&
>
> This is also major confusion, it seems you should roll 2/3 into one
> patch?
I'm not sure that would help. rdev_freq_to_chan doesn't get the
net_device, so it can't get the HT caps. I guess I can add the
net_device as an optional argument where it is used and check the
caps. OTOH it seem like a sanity check we can do without?
Arik
On Wed, 2012-07-04 at 18:58 +0300, Arik Nemtsov wrote:
> void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled);
> + struct ieee80211_sta_ht_cap *(*get_ht_cap)(struct wiphy *wiphy,
> + struct net_device *dev,
> + enum ieee80211_band band);
Ok so ... I really don't like this whole concept to start with, but you
convinced me it was needed. This is just ugly though. Now you can
suddenly change the HT caps randomly at runtime? That's ... distressing
for userspace!
Is there any chance this can just be a pointer inside the wdev struct?
Then at least we can verify it in NETDEV_UP if you had invalid HT caps
originally, etc.
johannes
Add an array of HT-cap pointers per band to the vif struct. When an HT-cap
is set in the vif, use it. Otherwise resort to the per-band HT cap in the
wiphy struct.
Signed-off-by: Arik Nemtsov <[email protected]>
---
include/net/mac80211.h | 4 ++++
net/mac80211/cfg.c | 11 +++++++++++
net/mac80211/ht.c | 19 +++++++++++--------
net/mac80211/ibss.c | 11 +++++++----
net/mac80211/ieee80211_i.h | 6 +++++-
net/mac80211/iface.c | 10 +++++++---
net/mac80211/main.c | 2 +-
net/mac80211/mesh.c | 9 ++++++---
net/mac80211/mlme.c | 20 ++++++++++++++------
net/mac80211/scan.c | 5 +++--
net/mac80211/util.c | 31 ++++++++++++++++++++++++++-----
11 files changed, 95 insertions(+), 33 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index e3fa90c..2dc5b8e 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -897,6 +897,8 @@ enum ieee80211_vif_flags {
* at runtime, mac80211 will never touch this field
* @hw_queue: hardware queue for each AC
* @cab_queue: content-after-beacon (DTIM beacon really) queue, AP mode only
+ * @ht_cap: per-band HT capabilities for this vif, used if non-NULL. These
+ * need to be set when the interface is added.
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *).
*/
@@ -911,6 +913,8 @@ struct ieee80211_vif {
u32 driver_flags;
+ struct ieee80211_sta_ht_cap *ht_cap[IEEE80211_NUM_BANDS];
+
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index ccbe241..6a40a83 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2997,6 +2997,16 @@ static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled)
}
#endif
+static struct ieee80211_sta_ht_cap *
+ieee80211_get_ht_cap(struct wiphy *wiphy,
+ struct net_device *dev,
+ enum ieee80211_band band)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ return ieee80211_sdata_get_ht_cap(sdata, band);
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -3071,4 +3081,5 @@ struct cfg80211_ops mac80211_config_ops = {
.get_et_sset_count = ieee80211_get_et_sset_count,
.get_et_stats = ieee80211_get_et_stats,
.get_et_strings = ieee80211_get_et_strings,
+ .get_ht_cap = ieee80211_get_ht_cap,
};
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 4b4538d..1a507a2 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -93,12 +93,14 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
{
u8 ampdu_info, tx_mcs_set_cap;
int i, max_tx_streams;
+ struct ieee80211_sta_ht_cap *local_ht_cap;
BUG_ON(!ht_cap);
memset(ht_cap, 0, sizeof(*ht_cap));
- if (!ht_cap_ie || !sband->ht_cap.ht_supported)
+ local_ht_cap = ieee80211_sdata_get_ht_cap(sdata, sband->band);
+ if (!ht_cap_ie || !local_ht_cap->ht_supported)
return;
ht_cap->ht_supported = true;
@@ -110,7 +112,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
* we mask them out.
*/
ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) &
- (sband->ht_cap.cap |
+ (local_ht_cap->cap |
~(IEEE80211_HT_CAP_LDPC_CODING |
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
@@ -121,9 +123,9 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
* The STBC bits are asymmetric -- if we don't have
* TX then mask out the peer's RX and vice versa.
*/
- if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
+ if (!(local_ht_cap->cap & IEEE80211_HT_CAP_TX_STBC))
ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC;
- if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC))
+ if (!(local_ht_cap->cap & IEEE80211_HT_CAP_RX_STBC))
ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC;
ampdu_info = ht_cap_ie->ampdu_params_info;
@@ -133,7 +135,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
/* own MCS TX capabilities */
- tx_mcs_set_cap = sband->ht_cap.mcs.tx_params;
+ tx_mcs_set_cap = local_ht_cap->mcs.tx_params;
/* Copy peer MCS TX capabilities, the driver might need them. */
ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params;
@@ -159,17 +161,18 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
*/
for (i = 0; i < max_tx_streams; i++)
ht_cap->mcs.rx_mask[i] =
- sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
+ local_ht_cap->mcs.rx_mask[i] &
+ ht_cap_ie->mcs.rx_mask[i];
if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
i < IEEE80211_HT_MCS_MASK_LEN; i++)
ht_cap->mcs.rx_mask[i] =
- sband->ht_cap.mcs.rx_mask[i] &
+ local_ht_cap->mcs.rx_mask[i] &
ht_cap_ie->mcs.rx_mask[i];
/* handle MCS rate 32 too */
- if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
+ if (local_ht_cap->mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
ht_cap->mcs.rx_mask[32/8] |= 1;
/*
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 5746d62..7b215ff7 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -52,6 +52,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
enum nl80211_channel_type channel_type;
+ struct ieee80211_sta_ht_cap *local_ht_cap;
lockdep_assert_held(&ifibss->mtx);
@@ -155,19 +156,21 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
memcpy(skb_put(skb, ifibss->ie_len),
ifibss->ie, ifibss->ie_len);
+ local_ht_cap = ieee80211_sdata_get_ht_cap(sdata, sband->band);
+
/* add HT capability and information IEs */
- if (channel_type && sband->ht_cap.ht_supported) {
+ if (channel_type && local_ht_cap->ht_supported) {
pos = skb_put(skb, 4 +
sizeof(struct ieee80211_ht_cap) +
sizeof(struct ieee80211_ht_operation));
- pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
- sband->ht_cap.cap);
+ pos = ieee80211_ie_build_ht_cap(pos, local_ht_cap,
+ local_ht_cap->cap);
/*
* Note: According to 802.11n-2009 9.13.3.1, HT Protection
* field and RIFS Mode are reserved in IBSS mode, therefore
* keep them at 0
*/
- pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
+ pos = ieee80211_ie_build_ht_oper(pos, local_ht_cap,
chan, channel_type, 0);
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e28a5b1..b0d45b5 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1452,7 +1452,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg,
u8 *extra, size_t extra_len, const u8 *bssid,
const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
-int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer,
const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
u8 channel);
@@ -1491,11 +1491,15 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic);
int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic);
+struct ieee80211_sta_ht_cap *
+ieee80211_sdata_get_ht_cap(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_band band);
/* virtual monitor */
int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
+
/* channel management */
enum ieee80211_chan_mode {
CHAN_MODE_UNDEFINED,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index fbef7a1..10c22a8 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1433,13 +1433,17 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sband = local->hw.wiphy->bands[i];
sdata->rc_rateidx_mask[i] =
sband ? (1 << sband->n_bitrates) - 1 : 0;
- if (sband)
+ if (sband) {
+ struct ieee80211_sta_ht_cap *local_ht_cap;
+ local_ht_cap = ieee80211_sdata_get_ht_cap(sdata,
+ sband->band);
memcpy(sdata->rc_rateidx_mcs_mask[i],
- sband->ht_cap.mcs.rx_mask,
+ local_ht_cap->mcs.rx_mask,
sizeof(sdata->rc_rateidx_mcs_mask[i]));
- else
+ } else {
memset(sdata->rc_rateidx_mcs_mask[i], 0,
sizeof(sdata->rc_rateidx_mcs_mask[i]));
+ }
}
ieee80211_set_default_queues(sdata);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index c794101..0da0f8c 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -749,7 +749,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (max_bitrates < sband->n_bitrates)
max_bitrates = sband->n_bitrates;
- supp_ht = supp_ht || sband->ht_cap.ht_supported;
+ supp_ht = supp_ht || sband->ht_supported;
supp_vht = supp_vht || sband->vht_cap.vht_supported;
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 764593d..5ec05376 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -370,18 +370,20 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
+ struct ieee80211_sta_ht_cap *ht_cap;
u8 *pos;
sband = local->hw.wiphy->bands[local->oper_channel->band];
- if (!sband->ht_cap.ht_supported ||
+ if (!sband->ht_supported ||
local->_oper_channel_type == NL80211_CHAN_NO_HT)
return 0;
if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
return -ENOMEM;
+ ht_cap = ieee80211_sdata_get_ht_cap(sdata, sband->band);
pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap));
- ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap);
+ ieee80211_ie_build_ht_cap(pos, ht_cap, ht_cap->cap);
return 0;
}
@@ -394,9 +396,10 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
enum nl80211_channel_type channel_type = local->_oper_channel_type;
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[channel->band];
- struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
+ struct ieee80211_sta_ht_cap *ht_cap;
u8 *pos;
+ ht_cap = ieee80211_sdata_get_ht_cap(sdata, sband->band);
if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT)
return 0;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 4e53711..60978e9 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -268,9 +268,10 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
u16 cap;
struct ieee80211_sta_ht_cap ht_cap;
- BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
+ memcpy(&ht_cap,
+ ieee80211_sdata_get_ht_cap(sdata, sband->band),
+ sizeof(ht_cap));
- memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
/* determine capability flags */
@@ -3013,6 +3014,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
const u8 *ht_oper_ie;
const struct ieee80211_ht_operation *ht_oper = NULL;
struct ieee80211_supported_band *sband;
+ struct ieee80211_sta_ht_cap *local_ht_cap;
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
return -EINVAL;
@@ -3035,10 +3037,11 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
/* switch to the right channel */
sband = local->hw.wiphy->bands[cbss->channel->band];
+ local_ht_cap = ieee80211_sdata_get_ht_cap(sdata, sband->band);
ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
- if (sband->ht_cap.ht_supported) {
+ if (local_ht_cap->ht_supported) {
ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
cbss->information_elements,
cbss->len_information_elements);
@@ -3070,7 +3073,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
if (ht_oper) {
channel_type = NL80211_CHAN_HT20;
- if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ if (local_ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
switch (ht_oper->ht_param &
IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
@@ -3260,6 +3263,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband;
const u8 *ssidie, *ht_ie;
int i, err;
+ struct ieee80211_sta_ht_cap *local_ht_cap;
ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
if (!ssidie)
@@ -3321,8 +3325,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* Also disable HT if we don't support it or the AP doesn't use WMM */
sband = local->hw.wiphy->bands[req->bss->channel->band];
- if (!sband->ht_cap.ht_supported ||
- local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
+ local_ht_cap = ieee80211_sdata_get_ht_cap(sdata, sband->band);
+
+ /* Also disable HT if we don't support it or the AP doesn't use WMM */
+ if (!local_ht_cap->ht_supported ||
+ local->hw.queues < IEEE80211_NUM_ACS ||
+ !bss->wmm_used) {
ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
netdev_info(sdata->dev,
"disabling HT as WMM/QoS is not supported\n");
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 379f178..94eaca9 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -264,7 +264,8 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
local->hw_scan_req->n_channels = n_chans;
- ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
+ ielen = ieee80211_build_preq_ies(local->scan_sdata,
+ (u8 *)local->hw_scan_req->ie,
req->ie, req->ie_len, band,
req->rates[band], 0);
local->hw_scan_req->ie_len = ielen;
@@ -939,7 +940,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
}
local->sched_scan_ies.len[i] =
- ieee80211_build_preq_ies(local,
+ ieee80211_build_preq_ies(sdata,
local->sched_scan_ies.ie[i],
req->ie, req->ie_len, i,
(u32) -1, 0);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 5715e7b..787529b 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -977,18 +977,38 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
ieee80211_tx_skb(sdata, skb);
}
-int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+struct ieee80211_sta_ht_cap *
+ieee80211_sdata_get_ht_cap(struct ieee80211_sub_if_data *sdata,
+ enum ieee80211_band band)
+{
+ struct wiphy *wiphy = sdata->local->hw.wiphy;
+
+ if (WARN_ON(!wiphy->bands[band]))
+ return NULL;
+
+ if (sdata->vif.ht_cap[band]) {
+ /* the HT cap must be marked invalid in struct wiphy */
+ WARN_ON(!wiphy->bands[band]->ht_cap_invalid);
+ return sdata->vif.ht_cap[band];
+ }
+
+ return &wiphy->bands[band]->ht_cap;
+}
+
+int ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer,
const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
u8 channel)
{
struct ieee80211_supported_band *sband;
+ struct ieee80211_local *local = sdata->local;
u8 *pos;
size_t offset = 0, noffset;
int supp_rates_len, i;
u8 rates[32];
int num_rates;
int ext_rates_len;
+ struct ieee80211_sta_ht_cap *local_ht_cap;
sband = local->hw.wiphy->bands[band];
@@ -1056,9 +1076,10 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
offset = noffset;
}
- if (sband->ht_cap.ht_supported)
- pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
- sband->ht_cap.cap);
+ local_ht_cap = ieee80211_sdata_get_ht_cap(sdata, sband->band);
+ if (local_ht_cap->ht_supported)
+ pos = ieee80211_ie_build_ht_cap(pos, local_ht_cap,
+ local_ht_cap->cap);
/*
* If adding more here, adjust code in main.c
@@ -1108,7 +1129,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
chan = ieee80211_frequency_to_channel(
local->hw.conf.channel->center_freq);
- buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len,
+ buf_len = ieee80211_build_preq_ies(sdata, buf, ie, ie_len,
local->hw.conf.channel->band,
ratemask, chan);
--
1.7.9.5
On Fri, 2012-07-06 at 10:30 +0300, Arik Nemtsov wrote:
> >> Also putting ht_cap_invalid inside the struct creates strange corner
> >> cases. One could mark the vif ht_cap as invalid as well, but the code
> >> would just ignore it.
> >
> > Good point. But then let's just move it out and change drivers ...
> > that's much cleaner I think.
>
> You mean move out ht_supported?
Yeah, I think that'd make more sense? Have it in the sband directly.
> >> >> @@ -30,13 +31,16 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
> >> >> chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
> >> >> return NULL;
> >> >>
> >> >> - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
> >> >> + sband = rdev->wiphy.bands[chan->band];
> >> >> + ht_cap = &sband->ht_cap;
> >> >>
> >> >> if (channel_type != NL80211_CHAN_NO_HT) {
> >> >> - if (!ht_cap->ht_supported)
> >> >> + if (!sband->ht_supported)
> >> >> return NULL;
> >> >>
> >> >> - if (channel_type != NL80211_CHAN_HT20 &&
> >> >> + /* this check is ignored when per-band HT caps are not used */
> >> >> + if (!sband->ht_cap_invalid &&
> >> >> + channel_type != NL80211_CHAN_HT20 &&
> >> >
> >> > This is also major confusion, it seems you should roll 2/3 into one
> >> > patch?
> >>
> >> I'm not sure that would help. rdev_freq_to_chan doesn't get the
> >> net_device, so it can't get the HT caps. I guess I can add the
> >> net_device as an optional argument where it is used and check the
> >> caps. OTOH it seem like a sanity check we can do without?
> >
> > I don't think we should do without that check? If you request say AP
> > operation on 40 MHz then you absolutely rely on that happening or
> > returning an error, not randomly operating in legacy mode!
>
> Sure. I'll send the netdev to the function where possible (I think
> everywhere except the virtual monitor).
I think the function should restrict to NO_HT in case it doesn't have
caps, otherwise it can't properly restrict. I suppose that means you
won't be able to do HT monitoring with your device, but I can live with
that.
johannes
Add a callback allowing drivers to return custom HT capabilities per
interface (netdev). This callback is used when per-band HT capabilities
are marked as invalid.
Use the new callback to return per-netdev HT capabilities where
need by NL80211 commands - namely, GET_WIPHY and SET_TX_BITRATE_MASK.
Signed-off-by: Arik Nemtsov <[email protected]>
---
include/net/cfg80211.h | 7 +++++++
net/wireless/nl80211.c | 49 +++++++++++++++++++++++++++++++++++-------------
2 files changed, 43 insertions(+), 13 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 7b24d88..b27ffe1 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1619,6 +1619,10 @@ struct cfg80211_gtk_rekey_data {
* @get_et_strings: Ethtool API to get a set of strings to describe stats
* and perhaps other supported types of ethtool data-sets.
* See @ethtool_ops.get_strings
+ *
+ * @get_ht_cap: Get the HT capabilities of the given interface on the given
+ * band. If not implemented, the per-band HT capabilities from struct
+ * wiphy are used.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -1827,6 +1831,9 @@ struct cfg80211_ops {
u32 sset, u8 *data);
void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled);
+ struct ieee80211_sta_ht_cap *(*get_ht_cap)(struct wiphy *wiphy,
+ struct net_device *dev,
+ enum ieee80211_band band);
};
/*
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 010ff47..e737d8c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -790,6 +790,19 @@ nla_put_failure:
return -ENOBUFS;
}
+static const struct ieee80211_sta_ht_cap *
+nl80211_get_ht_cap(struct cfg80211_registered_device *dev,
+ struct net_device *netdev, enum ieee80211_band band)
+{
+ WARN_ON(!dev->wiphy.bands[band]);
+
+ if (netdev && dev->ops->get_ht_cap &&
+ dev->wiphy.bands[band]->ht_cap_invalid)
+ return dev->ops->get_ht_cap(&dev->wiphy, netdev, band);
+
+ return &dev->wiphy.bands[band]->ht_cap;
+}
+
static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct cfg80211_registered_device *dev,
struct net_device *netdev)
@@ -902,6 +915,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
goto nla_put_failure;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ const struct ieee80211_sta_ht_cap *local_ht_cap;
if (!dev->wiphy.bands[band])
continue;
@@ -909,17 +923,19 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (!nl_band)
goto nla_put_failure;
+ local_ht_cap = nl80211_get_ht_cap(dev, netdev, band);
+
/* add HT info */
- if (dev->wiphy.bands[band]->ht_cap.ht_supported &&
+ if (local_ht_cap->ht_supported &&
(nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
- sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
- &dev->wiphy.bands[band]->ht_cap.mcs) ||
+ sizeof(local_ht_cap->mcs),
+ &local_ht_cap->mcs) ||
nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
- dev->wiphy.bands[band]->ht_cap.cap) ||
+ local_ht_cap->cap) ||
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
- dev->wiphy.bands[band]->ht_cap.ampdu_factor) ||
+ local_ht_cap->ampdu_factor) ||
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
- dev->wiphy.bands[band]->ht_cap.ampdu_density)))
+ local_ht_cap->ampdu_density)))
goto nla_put_failure;
/* add VHT info */
@@ -5793,7 +5809,7 @@ static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
return mask;
}
-static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
+static bool ht_rateset_to_mask(const struct ieee80211_sta_ht_cap *ht_cap,
u8 *rates, u8 rates_len,
u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
{
@@ -5812,7 +5828,7 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
return false;
/* check availability */
- if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
+ if (ht_cap->mcs.rx_mask[ridx] & rbit)
mcs[ridx] |= rbit;
else
return false;
@@ -5838,6 +5854,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
struct net_device *dev = info->user_ptr[1];
struct nlattr *tx_rates;
struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_ht_cap *ht_cap;
if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
return -EINVAL;
@@ -5851,13 +5868,15 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
sband = rdev->wiphy.bands[i];
mask.control[i].legacy =
sband ? (1 << sband->n_bitrates) - 1 : 0;
- if (sband)
+ if (sband) {
+ ht_cap = nl80211_get_ht_cap(rdev, dev, i);
memcpy(mask.control[i].mcs,
- sband->ht_cap.mcs.rx_mask,
+ ht_cap->mcs.rx_mask,
sizeof(mask.control[i].mcs));
- else
+ } else {
memset(mask.control[i].mcs, 0,
sizeof(mask.control[i].mcs));
+ }
}
/*
@@ -5884,9 +5903,10 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
nla_len(tb[NL80211_TXRATE_LEGACY]))
return -EINVAL;
}
+ ht_cap = nl80211_get_ht_cap(rdev, dev, band);
if (tb[NL80211_TXRATE_MCS]) {
if (!ht_rateset_to_mask(
- sband,
+ ht_cap,
nla_data(tb[NL80211_TXRATE_MCS]),
nla_len(tb[NL80211_TXRATE_MCS]),
mask.control[band].mcs))
@@ -5894,9 +5914,12 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
}
if (mask.control[band].legacy == 0) {
+ const struct ieee80211_sta_ht_cap *ht_cap;
+ ht_cap = nl80211_get_ht_cap(rdev, dev, band);
+
/* don't allow empty legacy rates if HT
* is not even supported. */
- if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
+ if (!ht_cap->ht_supported)
return -EINVAL;
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
--
1.7.9.5