Check if the requested HT mode can be used.
Extend cfg80211 IBSS struct for HT mode.
Signed-off-by: Alexander Simon <[email protected]>
---
As there were no replies for my RFC post, i am resending this as PATCH.
include/net/cfg80211.h | 1 +
net/wireless/chan.c | 2 +-
net/wireless/core.h | 3 +++
net/wireless/nl80211.c | 34 +++++++++++++++++++++++++++++++---
4 files changed, 36 insertions(+), 4 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index d17f47f..738db17 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1040,6 +1040,7 @@ struct cfg80211_ibss_params {
u8 *ssid;
u8 *bssid;
struct ieee80211_channel *channel;
+ enum nl80211_channel_type channel_type;
u8 *ie;
u8 ssid_len, ie_len;
u16 beacon_interval;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 17cd0c0..cd630e1 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -44,7 +44,7 @@ rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
return chan;
}
-static bool can_beacon_sec_chan(struct wiphy *wiphy,
+bool can_beacon_sec_chan(struct wiphy *wiphy,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type)
{
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 8672e02..a7d8e59 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -441,6 +441,9 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
struct ieee80211_channel *
rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
int freq, enum nl80211_channel_type channel_type);
+bool can_beacon_sec_chan(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type);
int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int freq,
enum nl80211_channel_type channel_type);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 28d2aa1..7f3f46c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4346,12 +4346,40 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
- ibss.channel = ieee80211_get_channel(wiphy,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ enum nl80211_channel_type channel_type;
+
+ channel_type = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+ if (channel_type != NL80211_CHAN_NO_HT &&
+ channel_type != NL80211_CHAN_HT20 &&
+ channel_type != NL80211_CHAN_HT40PLUS &&
+ channel_type != NL80211_CHAN_HT40MINUS)
+ return -EINVAL;
+ ibss.channel_type = channel_type;
+ } else {
+ ibss.channel_type = NL80211_CHAN_NO_HT;
+ }
+
+ ibss.channel = rdev_freq_to_chan(rdev,
+ nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
+ ibss.channel_type);
if (!ibss.channel ||
+ ibss.channel->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
- ibss.channel->flags & IEEE80211_CHAN_DISABLED)
+ ibss.channel->flags & IEEE80211_CHAN_RADAR)
+ return -EINVAL;
+
+ /* Both channels should be able to initiate communication */
+ if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
+ ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
+ !can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
+ ibss.channel_type)) {
+ printk(KERN_DEBUG
+ "cfg80211: Secondary channel not "
+ "allowed to initiate communication\n");
return -EINVAL;
+ }
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
--
1.7.3.4
On Mon, 2011-08-08 at 14:09 +0200, Alexander Simon wrote:
> +enum nl80211_channel_type ieee80211_ht_info_to_channel_type(
> + struct ieee80211_ht_info *ht_info)
This isn't used in this patch so you should put it into the other patch.
johannes
these can also be used from existing code.
Signed-off-by: Alexander Simon <[email protected]>
---
net/mac80211/ieee80211_i.h | 8 +++
net/mac80211/util.c | 116 +++++++++++++++++++++++++++++++++++++------
net/mac80211/work.c | 29 +-----------
3 files changed, 108 insertions(+), 45 deletions(-)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 400c09b..bfb820c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1401,6 +1401,12 @@ void ieee80211_recalc_smps(struct ieee80211_local *local);
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
const u8 *ids, int n_ids, size_t offset);
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
+u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_supported_band *sband,
+ u16 cap);
+u8 *ieee80211_ie_build_ht_info(u8 *pos,
+ struct ieee80211_sta_ht_cap *ht_cap,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type channel_type);
/* internal work items */
void ieee80211_work_init(struct ieee80211_local *local);
@@ -1429,6 +1435,8 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,
bool ieee80211_set_channel_type(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum nl80211_channel_type chantype);
+enum nl80211_channel_type ieee80211_ht_info_to_channel_type(
+ struct ieee80211_ht_info *ht_info);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index ddeb1b9..b62fc4e 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -983,23 +983,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
offset = noffset;
}
- if (sband->ht_cap.ht_supported) {
- u16 cap = sband->ht_cap.cap;
- __le16 tmp;
-
- *pos++ = WLAN_EID_HT_CAPABILITY;
- *pos++ = sizeof(struct ieee80211_ht_cap);
- memset(pos, 0, sizeof(struct ieee80211_ht_cap));
- tmp = cpu_to_le16(cap);
- memcpy(pos, &tmp, sizeof(u16));
- pos += sizeof(u16);
- *pos++ = sband->ht_cap.ampdu_factor |
- (sband->ht_cap.ampdu_density <<
- IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
- memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
- pos += sizeof(sband->ht_cap.mcs);
- pos += 2 + 4 + 1; /* ext info, BF cap, antsel */
- }
+ if (sband->ht_cap.ht_supported)
+ pos = ieee80211_ie_build_ht_cap(pos, sband, sband->ht_cap.cap);
/*
* If adding more here, adjust code in main.c
@@ -1522,3 +1507,100 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif)
_ieee80211_enable_rssi_reports(sdata, 0, 0);
}
EXPORT_SYMBOL(ieee80211_disable_rssi_reports);
+
+u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_supported_band *sband,
+ u16 cap)
+{
+ __le16 tmp;
+
+ *pos++ = WLAN_EID_HT_CAPABILITY;
+ *pos++ = sizeof(struct ieee80211_ht_cap);
+ memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+ /* capability flags */
+ tmp = cpu_to_le16(cap);
+ memcpy(pos, &tmp, sizeof(u16));
+ pos += sizeof(u16);
+
+ /* AMPDU parameters */
+ *pos++ = sband->ht_cap.ampdu_factor |
+ (sband->ht_cap.ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+ /* MCS set */
+ memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+ pos += sizeof(sband->ht_cap.mcs);
+
+ /* extended capabilities */
+ pos += sizeof(__le16);
+
+ /* BF capabilities */
+ pos += sizeof(__le32);
+
+ /* antenna selection */
+ pos += sizeof(u8);
+
+ return pos;
+}
+
+u8 *ieee80211_ie_build_ht_info(u8 *pos,
+ struct ieee80211_sta_ht_cap *ht_cap,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type channel_type)
+{
+ struct ieee80211_ht_info *ht_info;
+ /* Build HT Information */
+ *pos++ = WLAN_EID_HT_INFORMATION;
+ *pos++ = sizeof(struct ieee80211_ht_info);
+ ht_info = (struct ieee80211_ht_info *)pos;
+ ht_info->control_chan =
+ ieee80211_frequency_to_channel(channel->center_freq);
+ switch (channel_type) {
+ case NL80211_CHAN_HT40MINUS:
+ ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ break;
+ case NL80211_CHAN_HT20:
+ default:
+ ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ }
+ if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+ ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+ ht_info->operation_mode = 0x0000;
+ ht_info->stbc_param = 0x0000;
+
+ /* It seems that Basic MCS set and Supported MCS set
+ are identical for the first 10 bytes */
+ memset(&ht_info->basic_set, 0, 16);
+ memcpy(&ht_info->basic_set, &ht_cap->mcs, 10);
+
+ return pos + sizeof(struct ieee80211_ht_info);
+}
+
+enum nl80211_channel_type ieee80211_ht_info_to_channel_type(
+ struct ieee80211_ht_info *ht_info)
+{
+ enum nl80211_channel_type channel_type;
+
+ if (!ht_info)
+ return NL80211_CHAN_NO_HT;
+
+ switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_NONE:
+ channel_type = NL80211_CHAN_HT20;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ channel_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ channel_type = NL80211_CHAN_HT40MINUS;
+ break;
+ default:
+ channel_type = NL80211_CHAN_NO_HT;
+ }
+
+ return channel_type;
+}
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 380b9a7..8d9251c 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -103,7 +103,6 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
u8 *pos;
u32 flags = channel->flags;
u16 cap = sband->ht_cap.cap;
- __le16 tmp;
if (!sband->ht_cap.ht_supported)
return;
@@ -154,34 +153,8 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,
}
/* reserve and fill IE */
-
pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
- *pos++ = WLAN_EID_HT_CAPABILITY;
- *pos++ = sizeof(struct ieee80211_ht_cap);
- memset(pos, 0, sizeof(struct ieee80211_ht_cap));
-
- /* capability flags */
- tmp = cpu_to_le16(cap);
- memcpy(pos, &tmp, sizeof(u16));
- pos += sizeof(u16);
-
- /* AMPDU parameters */
- *pos++ = sband->ht_cap.ampdu_factor |
- (sband->ht_cap.ampdu_density <<
- IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
-
- /* MCS set */
- memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
- pos += sizeof(sband->ht_cap.mcs);
-
- /* extended capabilities */
- pos += sizeof(__le16);
-
- /* BF capabilities */
- pos += sizeof(__le32);
-
- /* antenna selection */
- pos += sizeof(u8);
+ ieee80211_ie_build_ht_cap(pos, sband, cap);
}
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
--
1.7.3.4
> Well, I would keep the wrapper but make it an inline, not a separate
> exported function, like cfg80211_get_ibss() (which really is quite
> pointless, does anything still use it?)
>
> johannes
Yep, i've already done so. Additionally, i also changed the wrapper
cfg80211_get_ibss to use cfg80211_get_bss_ht.
Alex
On Mon, 2011-08-08 at 14:09 +0200, Alexander Simon wrote:
> these can also be used from existing code.
So you should say that it's a refactoring. Generally, your commit logs
could really be a tad more verbose :-)
I'm not going to really review the code you move here -- hopefully you
did only move it?
johannes
On Wed, 2011-08-10 at 19:50 +0200, Alexander Simon wrote:
> The problem I'm facing here is that I really *need* to check the HT mode
> _inside_ the loop. Hence i need a _ht version.
> OR I could change cfg80211_get_bss only, causing to add the new
> parameters in *all* calls of cfg80211_get_bss... Whatever you prefer.
Well, I would keep the wrapper but make it an inline, not a separate
exported function, like cfg80211_get_ibss() (which really is quite
pointless, does anything still use it?)
johannes
On Mon, 2011-08-08 at 14:02 +0200, Alexander Simon wrote:
> Check if the requested HT mode can be used.
> Extend cfg80211 IBSS struct for HT mode.
>
> Signed-off-by: Alexander Simon <[email protected]>
> ---
>
> As there were no replies for my RFC post, i am resending this as PATCH.
I was on vacation ;-)
> + ibss.channel = rdev_freq_to_chan(rdev,
> + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
> + ibss.channel_type);
> if (!ibss.channel ||
> + ibss.channel->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
> ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
> - ibss.channel->flags & IEEE80211_CHAN_DISABLED)
> + ibss.channel->flags & IEEE80211_CHAN_RADAR)
> + return -EINVAL;
> +
> + /* Both channels should be able to initiate communication */
> + if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
> + ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
> + !can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
> + ibss.channel_type)) {
> + printk(KERN_DEBUG
> + "cfg80211: Secondary channel not "
> + "allowed to initiate communication\n");
remove the printk please
johannes
On Wed, 2011-08-10 at 17:48 +0200, Alexander Simon wrote:
> Am Mittwoch, den 10.08.2011, 15:53 +0200 schrieb Johannes Berg:
> > On Mon, 2011-08-08 at 14:04 +0200, Alexander Simon wrote:
> >
> > > +struct cfg80211_bss *cfg80211_get_bss_ht(struct wiphy *wiphy,
> > > + struct ieee80211_channel *channel,
> > > + const u8 *bssid,
> > > + const u8 *ssid, size_t ssid_len,
> > > + u16 capa_mask, u16 capa_val,
> > > + enum nl80211_channel_type channel_type);
> >
> > indentation? Also, you really need kernel-doc for this.
> Pardon me, but I don't know what you mean... I just browsed
> Documentation/networking. Could you point me the exact file, please?
Documentation/kernel-doc-nano-HOWTO.txt maybe?
> > You've got to be kidding, if you export it you really need to give it a
> > decent name. Besides, you don't have to export it in this patch.
> I intended to export this instead of writing ieee80211_can_use_ext_chan
> first.
> Ok, to summarize this: I should use this instead of my own
> ieee80211_can_use_ext_chan in mac80211 (as in your reply in 4/4), but
> when I export this I need to rename it. Does cfg80211_can_use_ext_chan
> sound well?
Ok but then I guess you forgot to remove the export? Yeah, please rename
it and export it for mac80211, that seems fine.
> > I don't like the semantics of this. Why is NO_HT indicating "don't care
> > about HT"? How then do you really find a NO_HT BSS?
> >
> > Also, this raises a more fundamental question -- didn't you earlier say
> > you wanted to be able to connect with any IBSS, even non-HT ones, but
> > now it must match?
> This is only used for fixed channel mode. As we would only join a
> matching HT mode, I have to pass it here.
> How about second parameter, "bool match_ht"?
Ahh, ok, that explains it, sorry for the confusion.
johannes
On Mon, 2011-08-08 at 14:10 +0200, Alexander Simon wrote:
> This means setting channel into HT mode and adding HT IEs in beacons.
> Check if regdom allows this first.
>
> If joining another HT network, use its HT mode.
> If joining a non-HT network, use HT mode given by iw.
> Join as legacy if neither iw nor remote beacon set an HT mode.
Do we really want this? I'm wondering if we shouldn't always do HT IBSS
when joining, even if you didn't ask for it? Not really sure though.
Thoughts? Are we joining as HT20 if you didn't ask for HT40? And are we
joining as HT20 if you asked for HT40- but the network is HT40+?
> If we receive a beacon from another station within our BSSID with a different HT
> mode than us, we switch to its HT mode. If there are several stations with
> different HT modes, it may take some time until all will have the same mode.
> There is no way we can distinguish the "older" station.
Actually, you can ... the highest TSF should win.
> Except for fixed channel mode, HT mode is also fixed. Join or merge ONLY if
> channel AND HT mode matches.
makes sense.
> +++ b/net/mac80211/ibss.c
> @@ -35,6 +35,76 @@
>
> #define IEEE80211_IBSS_MAX_STA_ENTRIES 128
>
> +static bool ieee80211_can_use_ext_chan(struct ieee80211_sub_if_data *sdata,
> + struct ieee80211_channel *channel,
> + enum nl80211_channel_type channel_type)
> +{
> + /* check if we are legally allowed to use HT extension channel */
> + if ((channel_type == NL80211_CHAN_HT40PLUS) ||
> + (channel_type == NL80211_CHAN_HT40MINUS)) {
> + int sec_freq = channel->center_freq +
> + (channel_type == NL80211_CHAN_HT40PLUS ? 20 : -20);
> + struct ieee80211_channel *sec_chan =
> + ieee80211_get_channel(sdata->wdev.wiphy, sec_freq);
> + if (!sec_chan || sec_chan->flags & (IEEE80211_CHAN_DISABLED |
> + IEEE80211_CHAN_PASSIVE_SCAN |
> + IEEE80211_CHAN_NO_IBSS |
> + IEEE80211_CHAN_RADAR)) {
> + return false;
> + }
> + }
> + return true;
> +}
Isn't that already a function in cfg80211 that you could rename &
export?
> +static void ieee80211_update_ht_elems(struct ieee80211_sub_if_data *sdata,
> + struct ieee80211_mgmt *mgmt,
> + struct ieee80211_ht_info *ht_info)
> +{
> + struct ieee80211_local *local = sdata->local;
> + struct ieee80211_supported_band *sband =
> + local->hw.wiphy->bands[local->oper_channel->band];
> + enum nl80211_channel_type channel_type =
> + ieee80211_ht_info_to_channel_type(ht_info);
> +
> + if (!ieee80211_can_use_ext_chan(sdata, local->oper_channel, channel_type))
> + channel_type = NL80211_CHAN_HT20;
> +
> + if (channel_type != local->_oper_channel_type) {
> + struct sk_buff *skb = rcu_dereference_protected(
> + sdata->u.ibss.presp,
> + lockdep_is_held(&ifibss->mtx));
> + struct sk_buff *nskb;
> + u8 *ht_ie;
> +
> + /* update HT IE. If not yet existing, create one */
> + nskb = skb_copy(skb, GFP_ATOMIC);
> + ht_ie = (u8 *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
> + (const u8 *)(nskb->data + 24 +
> + sizeof(mgmt->u.beacon)),
> + nskb->len - 24 -
> + sizeof(mgmt->u.beacon));
> + if (!ht_ie)
> + ht_ie = skb_put(nskb, 4 +
> + sizeof(struct ieee80211_ht_cap) +
> + sizeof(struct ieee80211_ht_info));
> +
> + ht_ie = ieee80211_ie_build_ht_cap(ht_ie, sband,
> + sband->ht_cap.cap);
> + ht_ie = ieee80211_ie_build_ht_info(ht_ie, &sband->ht_cap,
> + local->oper_channel, channel_type);
> + rcu_assign_pointer(sdata->u.ibss.presp, nskb);
> + kfree_skb(skb);
RCU race here.
> @@ -104,8 +175,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
>
> sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
>
> + /* entering a legacy IBSS. Use given HT configuration. */
> + if (channel_type == NL80211_CHAN_NO_HT)
> + channel_type = ifibss->channel_type;
> local->oper_channel = chan;
> - WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
> +
> + /* if phy is on a different extension channel, setting ht40 will fail */
> + if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
> + channel_type = NL80211_CHAN_HT20;
> + WARN_ON(!ieee80211_set_channel_type(local, sdata,
> + channel_type));
> + }
indentation
> @@ -404,7 +525,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
> ieee80211_sta_join_ibss(sdata, bss);
> supp_rates = ieee80211_sta_get_rates(local, elems, band);
> ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
> - supp_rates, GFP_KERNEL);
> + supp_rates, elems->ht_cap_elem, GFP_KERNEL);
indentation
johannes
Keep cfg80211_get_bss for compatibility but make use of this new function
by matching any HT configuration.
Signed-off-by: Alexander Simon <[email protected]>
---
include/net/cfg80211.h | 6 ++++++
net/wireless/chan.c | 1 +
net/wireless/scan.c | 37 ++++++++++++++++++++++++++++++++++---
3 files changed, 41 insertions(+), 3 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 738db17..8276b19 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2581,6 +2581,12 @@ struct cfg80211_bss *cfg80211_get_bss(struct
wiphy *wiphy,
const u8 *bssid,
const u8 *ssid, size_t ssid_len,
u16 capa_mask, u16 capa_val);
+struct cfg80211_bss *cfg80211_get_bss_ht(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ u16 capa_mask, u16 capa_val,
+ enum nl80211_channel_type channel_type);
static inline struct cfg80211_bss *
cfg80211_get_ibss(struct wiphy *wiphy,
struct ieee80211_channel *channel,
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index cd630e1..046b113 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -75,6 +75,7 @@ bool can_beacon_sec_chan(struct wiphy *wiphy,
return true;
}
+EXPORT_SYMBOL(can_beacon_sec_chan);
int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int freq,
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 2936cb8..ff5b736 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -364,6 +364,19 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy
*wiphy,
const u8 *ssid, size_t ssid_len,
u16 capa_mask, u16 capa_val)
{
+ /* call HT version with no HT requirements */
+ return cfg80211_get_bss_ht(wiphy, channel, bssid, ssid, ssid_len,
+ capa_mask, capa_val, NL80211_CHAN_NO_HT);
+}
+EXPORT_SYMBOL(cfg80211_get_bss);
+
+struct cfg80211_bss *cfg80211_get_bss_ht(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ const u8 *bssid,
+ const u8 *ssid, size_t ssid_len,
+ u16 capa_mask, u16 capa_val,
+ enum nl80211_channel_type require_ht)
+{
struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
struct cfg80211_internal_bss *bss, *res = NULL;
unsigned long now = jiffies;
@@ -373,8 +386,26 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy
*wiphy,
list_for_each_entry(bss, &dev->bss_list, list) {
if ((bss->pub.capability & capa_mask) != capa_val)
continue;
- if (channel && bss->pub.channel != channel)
- continue;
+ if (channel) {
+ if (bss->pub.channel != channel)
+ continue;
+ if (require_ht != NL80211_CHAN_NO_HT) {
+ struct ieee80211_ht_info *ht_info;
+ ht_info = (struct ieee80211_ht_info *)
+ ieee80211_bss_get_ie(&bss->pub,
+ WLAN_EID_HT_INFORMATION);
+ if (!ht_info)
+ continue;
+ if (require_ht == NL80211_CHAN_HT40MINUS &&
+ !(ht_info->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW))
+ continue;
+ if (require_ht == NL80211_CHAN_HT40PLUS &&
+ !(ht_info->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE))
+ continue;
+ }
+ }
/* Don't get expired BSS structs */
if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
!atomic_read(&bss->hold))
@@ -391,7 +422,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy
*wiphy,
return NULL;
return &res->pub;
}
-EXPORT_SYMBOL(cfg80211_get_bss);
+EXPORT_SYMBOL(cfg80211_get_bss_ht);
struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
struct ieee80211_channel *channel,
--
1.7.3.4
Am Mittwoch, den 10.08.2011, 15:53 +0200 schrieb Johannes Berg:
> On Mon, 2011-08-08 at 14:04 +0200, Alexander Simon wrote:
>
> > +struct cfg80211_bss *cfg80211_get_bss_ht(struct wiphy *wiphy,
> > + struct ieee80211_channel *channel,
> > + const u8 *bssid,
> > + const u8 *ssid, size_t ssid_len,
> > + u16 capa_mask, u16 capa_val,
> > + enum nl80211_channel_type channel_type);
>
> indentation? Also, you really need kernel-doc for this.
Pardon me, but I don't know what you mean... I just browsed
Documentation/networking. Could you point me the exact file, please?
>
> > +++ b/net/wireless/chan.c
> > @@ -75,6 +75,7 @@ bool can_beacon_sec_chan(struct wiphy *wiphy,
> >
> > return true;
> > }
> > +EXPORT_SYMBOL(can_beacon_sec_chan);
>
> You've got to be kidding, if you export it you really need to give it a
> decent name. Besides, you don't have to export it in this patch.
I intended to export this instead of writing ieee80211_can_use_ext_chan
first.
Ok, to summarize this: I should use this instead of my own
ieee80211_can_use_ext_chan in mac80211 (as in your reply in 4/4), but
when I export this I need to rename it. Does cfg80211_can_use_ext_chan
sound well?
>
> > int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
> > struct wireless_dev *wdev, int freq,
> > diff --git a/net/wireless/scan.c b/net/wireless/scan.c
> > index 2936cb8..ff5b736 100644
> > --- a/net/wireless/scan.c
> > +++ b/net/wireless/scan.c
> > @@ -364,6 +364,19 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy
> > *wiphy,
> > const u8 *ssid, size_t ssid_len,
> > u16 capa_mask, u16 capa_val)
> > {
> > + /* call HT version with no HT requirements */
> > + return cfg80211_get_bss_ht(wiphy, channel, bssid, ssid, ssid_len,
> > + capa_mask, capa_val, NL80211_CHAN_NO_HT);
> > +}
> > +EXPORT_SYMBOL(cfg80211_get_bss);
>
> I don't like the semantics of this. Why is NO_HT indicating "don't care
> about HT"? How then do you really find a NO_HT BSS?
>
> Also, this raises a more fundamental question -- didn't you earlier say
> you wanted to be able to connect with any IBSS, even non-HT ones, but
> now it must match?
This is only used for fixed channel mode. As we would only join a
matching HT mode, I have to pass it here.
How about second parameter, "bool match_ht"?
>
> johannes
>
> Ok, that makes sense. So you need the get_bss_ht() only for fixed mode I
> guess.
Exactly.
>
> > This should answer your last questions:
> > When not fixed, the BSSID configuration always is preferred. Thus, we
> > would join as HT40 even though not requested.
> > So, when we requsted HT40-, but the IBSS is HT40+, we would work on -.
>
> ? I think I know what you mean but your example seems .. the wrong way
> around?
Damn, you're right. We would work on HT+, not -, as the BSS config is
preferred.
>
> > I wanted to have the opportunity to start HT on an existing IBSS.
>
> Well that should always be OK?
>
> > The problem is that legacy station may "kill" the HT configuration:
> > If STA A starts in HT IBSS and lets say Windows STA B joins, B would
> > advertise that IBSS as non-HT as it ignores our HT IE.
> > Then, if STA A dies and STA C joins, it will be non-HT.
>
> mac80211 is STA C? Would it be a problem to use HT if the IBSS was
> previously non-HT? The old members will ignore it, but other new HT
> members may work OK? IOW -- is it not possible to have a mixed HT/non-HT
> IBSS?
Basically, as to my knowledge, the only difference is that we add HI IEs. Non-
HT stations will ignore these, of course.
A mixed network should be no problem - as long as Greenfield is turned off
(and i don't turn in on anywhere).
To proceed with basics: I follow a very simple approach (for non-fixed freq):
Use current BSS HT settings. If none exists, use iw config. If no iw config,
no HT.
But there may be a chance that a N-Node joins a mixed IBSS without an iw HT
setting.
If it joins from a G-Node's beacon, it won't see or use HT.
That is the reason I inspect later incoming beacons for HT settings and
reconfigure our beacon skb, when received one. That also resolves the "windows
example" above.
>
> > No, pretty sure not. We *are* already joined. That means the TSFs of all
> > stations *within* our BSSID match. TSFs can only tell whitch BSSID is
> > older...
> > So there is no way to distinguish which station first joined.
> > Or am i wrong? That would be good in that case :)
>
> Oh, no, you're right of course.
As said above, I take *any* HT configuration from *any* beacon and reconfigure
our node (first beacon wins).
For two different HT configs it is normally distributed which config would
"win". Identifying the first node would help here...
If there were many stations, with different HT configs, joining at the same
time, it would take several beacons until the configurations would tilt into a
homogeneous one.
If you have a good idea - I'll be eager to hear :)
>
> johannes
I hope I could make my thoughts somewhat more understandable...
Alex
On Wed, 2011-08-10 at 17:31 +0200, Alexander Simon wrote:
> In short: The HT parameter shall behave similar to the frequency:
> Non-fixed mode: Only used when starting an IBSS or starting HT in an
> non-IBSS.
> Fixed mode: Only join when HT modes match.
Ok, that makes sense. So you need the get_bss_ht() only for fixed mode I
guess.
> This should answer your last questions:
> When not fixed, the BSSID configuration always is preferred. Thus, we
> would join as HT40 even though not requested.
> So, when we requsted HT40-, but the IBSS is HT40+, we would work on -.
? I think I know what you mean but your example seems .. the wrong way
around?
> I wanted to have the opportunity to start HT on an existing IBSS.
Well that should always be OK?
> The problem is that legacy station may "kill" the HT configuration:
> If STA A starts in HT IBSS and lets say Windows STA B joins, B would
> advertise that IBSS as non-HT as it ignores our HT IE.
> Then, if STA A dies and STA C joins, it will be non-HT.
mac80211 is STA C? Would it be a problem to use HT if the IBSS was
previously non-HT? The old members will ignore it, but other new HT
members may work OK? IOW -- is it not possible to have a mixed HT/non-HT
IBSS?
> No, pretty sure not. We *are* already joined. That means the TSFs of all
> stations *within* our BSSID match. TSFs can only tell whitch BSSID is
> older...
> So there is no way to distinguish which station first joined.
> Or am i wrong? That would be good in that case :)
Oh, no, you're right of course.
johannes
On 2011-08-12 8:25 AM, Alexander Simon wrote:
>> Ok, that makes sense. So you need the get_bss_ht() only for fixed mode I
>> guess.
> Exactly.
>>
>> > This should answer your last questions:
>> > When not fixed, the BSSID configuration always is preferred. Thus, we
>> > would join as HT40 even though not requested.
>> > So, when we requsted HT40-, but the IBSS is HT40+, we would work on -.
>>
>> ? I think I know what you mean but your example seems .. the wrong way
>> around?
> Damn, you're right. We would work on HT+, not -, as the BSS config is
> preferred.
>>
>> > I wanted to have the opportunity to start HT on an existing IBSS.
>>
>> Well that should always be OK?
>>
>> > The problem is that legacy station may "kill" the HT configuration:
>> > If STA A starts in HT IBSS and lets say Windows STA B joins, B would
>> > advertise that IBSS as non-HT as it ignores our HT IE.
>> > Then, if STA A dies and STA C joins, it will be non-HT.
>>
>> mac80211 is STA C? Would it be a problem to use HT if the IBSS was
>> previously non-HT? The old members will ignore it, but other new HT
>> members may work OK? IOW -- is it not possible to have a mixed HT/non-HT
>> IBSS?
> Basically, as to my knowledge, the only difference is that we add HI IEs. Non-
> HT stations will ignore these, of course.
> A mixed network should be no problem - as long as Greenfield is turned off
> (and i don't turn in on anywhere).
>
> To proceed with basics: I follow a very simple approach (for non-fixed freq):
> Use current BSS HT settings. If none exists, use iw config. If no iw config,
> no HT.
What's the point of disabling HT in that case. Since it's a superset of
normal A/B/G mode, we could just leave it enabled (defaulting to HT20)
instead of reconfiguring based on beacons at a later point in time.
- Felix
Am Mittwoch, den 10.08.2011, 18:05 +0200 schrieb Johannes Berg:
> On Wed, 2011-08-10 at 17:48 +0200, Alexander Simon wrote:
> > Am Mittwoch, den 10.08.2011, 15:53 +0200 schrieb Johannes Berg:
> > > On Mon, 2011-08-08 at 14:04 +0200, Alexander Simon wrote:
> > >
> > > > +struct cfg80211_bss *cfg80211_get_bss_ht(struct wiphy *wiphy,
> > > > + struct ieee80211_channel *channel,
> > > > + const u8 *bssid,
> > > > + const u8 *ssid, size_t ssid_len,
> > > > + u16 capa_mask, u16 capa_val,
> > > > + enum nl80211_channel_type channel_type);
> > >
> > > indentation? Also, you really need kernel-doc for this.
> > Pardon me, but I don't know what you mean... I just browsed
> > Documentation/networking. Could you point me the exact file, please?
>
> Documentation/kernel-doc-nano-HOWTO.txt maybe?
Uh, okay...
I thought as there is no documentation for cfg80211_get_bss, there was
no need to document the _ht version...
The problem I'm facing here is that I really *need* to check the HT mode
_inside_ the loop. Hence i need a _ht version.
OR I could change cfg80211_get_bss only, causing to add the new
parameters in *all* calls of cfg80211_get_bss... Whatever you prefer.
To conclude this:
* I'll write a _ht version that has a check_ht bool
* if (check_ht == 1), match channel_type parameter
* _ht version calls non-ht with check_ht = 0
* documentation for _ht (for non-ht also???)
Indentation will be fixed, of course.
Alex
Am Mittwoch, den 10.08.2011, 16:03 +0200 schrieb Johannes Berg:
> On Mon, 2011-08-08 at 14:10 +0200, Alexander Simon wrote:
> > This means setting channel into HT mode and adding HT IEs in beacons.
> > Check if regdom allows this first.
> >
> > If joining another HT network, use its HT mode.
> > If joining a non-HT network, use HT mode given by iw.
> > Join as legacy if neither iw nor remote beacon set an HT mode.
>
> Do we really want this? I'm wondering if we shouldn't always do HT IBSS
> when joining, even if you didn't ask for it? Not really sure though.
> Thoughts? Are we joining as HT20 if you didn't ask for HT40? And are we
> joining as HT20 if you asked for HT40- but the network is HT40+?
In short: The HT parameter shall behave similar to the frequency:
Non-fixed mode: Only used when starting an IBSS or starting HT in an
non-IBSS.
Fixed mode: Only join when HT modes match.
This should answer your last questions:
When not fixed, the BSSID configuration always is preferred. Thus, we
would join as HT40 even though not requested.
So, when we requsted HT40-, but the IBSS is HT40+, we would work on -.
I wanted to have the opportunity to start HT on an existing IBSS.
The problem is that legacy station may "kill" the HT configuration:
If STA A starts in HT IBSS and lets say Windows STA B joins, B would
advertise that IBSS as non-HT as it ignores our HT IE.
Then, if STA A dies and STA C joins, it will be non-HT.
> > If we receive a beacon from another station within our BSSID with a different HT
> > mode than us, we switch to its HT mode. If there are several stations with
> > different HT modes, it may take some time until all will have the same mode.
> > There is no way we can distinguish the "older" station.
>
> Actually, you can ... the highest TSF should win.
No, pretty sure not. We *are* already joined. That means the TSFs of all
stations *within* our BSSID match. TSFs can only tell whitch BSSID is
older...
So there is no way to distinguish which station first joined.
Or am i wrong? That would be good in that case :)
>
> > Except for fixed channel mode, HT mode is also fixed. Join or merge ONLY if
> > channel AND HT mode matches.
>
> makes sense.
>
> > +++ b/net/mac80211/ibss.c
> > @@ -35,6 +35,76 @@
> >
> > #define IEEE80211_IBSS_MAX_STA_ENTRIES 128
> >
> > +static bool ieee80211_can_use_ext_chan(struct ieee80211_sub_if_data *sdata,
> > + struct ieee80211_channel *channel,
> > + enum nl80211_channel_type channel_type)
> > +{
> > + /* check if we are legally allowed to use HT extension channel */
> > + if ((channel_type == NL80211_CHAN_HT40PLUS) ||
> > + (channel_type == NL80211_CHAN_HT40MINUS)) {
> > + int sec_freq = channel->center_freq +
> > + (channel_type == NL80211_CHAN_HT40PLUS ? 20 : -20);
> > + struct ieee80211_channel *sec_chan =
> > + ieee80211_get_channel(sdata->wdev.wiphy, sec_freq);
> > + if (!sec_chan || sec_chan->flags & (IEEE80211_CHAN_DISABLED |
> > + IEEE80211_CHAN_PASSIVE_SCAN |
> > + IEEE80211_CHAN_NO_IBSS |
> > + IEEE80211_CHAN_RADAR)) {
> > + return false;
> > + }
> > + }
> > + return true;
> > +}
>
> Isn't that already a function in cfg80211 that you could rename &
> export?
Hmm... Didn't want to do any changes to the module exports, but I'll use
it.
>
> > +static void ieee80211_update_ht_elems(struct ieee80211_sub_if_data *sdata,
> > + struct ieee80211_mgmt *mgmt,
> > + struct ieee80211_ht_info *ht_info)
> > +{
> > + struct ieee80211_local *local = sdata->local;
> > + struct ieee80211_supported_band *sband =
> > + local->hw.wiphy->bands[local->oper_channel->band];
> > + enum nl80211_channel_type channel_type =
> > + ieee80211_ht_info_to_channel_type(ht_info);
> > +
> > + if (!ieee80211_can_use_ext_chan(sdata, local->oper_channel, channel_type))
> > + channel_type = NL80211_CHAN_HT20;
> > +
> > + if (channel_type != local->_oper_channel_type) {
> > + struct sk_buff *skb = rcu_dereference_protected(
> > + sdata->u.ibss.presp,
> > + lockdep_is_held(&ifibss->mtx));
> > + struct sk_buff *nskb;
> > + u8 *ht_ie;
> > +
> > + /* update HT IE. If not yet existing, create one */
> > + nskb = skb_copy(skb, GFP_ATOMIC);
> > + ht_ie = (u8 *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
> > + (const u8 *)(nskb->data + 24 +
> > + sizeof(mgmt->u.beacon)),
> > + nskb->len - 24 -
> > + sizeof(mgmt->u.beacon));
> > + if (!ht_ie)
> > + ht_ie = skb_put(nskb, 4 +
> > + sizeof(struct ieee80211_ht_cap) +
> > + sizeof(struct ieee80211_ht_info));
> > +
> > + ht_ie = ieee80211_ie_build_ht_cap(ht_ie, sband,
> > + sband->ht_cap.cap);
> > + ht_ie = ieee80211_ie_build_ht_info(ht_ie, &sband->ht_cap,
> > + local->oper_channel, channel_type);
> > + rcu_assign_pointer(sdata->u.ibss.presp, nskb);
> > + kfree_skb(skb);
>
> RCU race here.
>
> > @@ -104,8 +175,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
> >
> > sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
> >
> > + /* entering a legacy IBSS. Use given HT configuration. */
> > + if (channel_type == NL80211_CHAN_NO_HT)
> > + channel_type = ifibss->channel_type;
> > local->oper_channel = chan;
> > - WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
> > +
> > + /* if phy is on a different extension channel, setting ht40 will fail */
> > + if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
> > + channel_type = NL80211_CHAN_HT20;
> > + WARN_ON(!ieee80211_set_channel_type(local, sdata,
> > + channel_type));
> > + }
>
> indentation
>
> > @@ -404,7 +525,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
> > ieee80211_sta_join_ibss(sdata, bss);
> > supp_rates = ieee80211_sta_get_rates(local, elems, band);
> > ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
> > - supp_rates, GFP_KERNEL);
> > + supp_rates, elems->ht_cap_elem, GFP_KERNEL);
>
> indentation
>
> johannes
>
On Mon, Aug 08, 2011 at 02:02:58PM +0200, Alexander Simon wrote:
> Check if the requested HT mode can be used.
> Extend cfg80211 IBSS struct for HT mode.
>
> Signed-off-by: Alexander Simon <[email protected]>
> ---
>
> As there were no replies for my RFC post, i am resending this as PATCH.
This series got a lot of comments. Are you planning to post a
revised series?
John
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.
This means setting channel into HT mode and adding HT IEs in beacons.
Check if regdom allows this first.
If joining another HT network, use its HT mode.
If joining a non-HT network, use HT mode given by iw.
Join as legacy if neither iw nor remote beacon set an HT mode.
The same applies when merging with an older BSSID
(as it is basically leaving the current and joining the older BSSID).
If we receive a beacon from another station within our BSSID with a different HT
mode than us, we switch to its HT mode. If there are several stations with
different HT modes, it may take some time until all will have the same mode.
There is no way we can distinguish the "older" station.
Except for fixed channel mode, HT mode is also fixed. Join or merge ONLY if
channel AND HT mode matches.
And finally, allow frame aggregation in IBSS mode.
Signed-off-by: Alexander Simon <[email protected]>
---
net/mac80211/agg-rx.c | 2 +
net/mac80211/agg-tx.c | 5 +-
net/mac80211/ht.c | 2 +
net/mac80211/ibss.c | 166 +++++++++++++++++++++++++++++++++++++++----
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/rx.c | 6 +-
6 files changed, 164 insertions(+), 19 deletions(-)
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index fd1aaf2..057c2a1 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -184,6 +184,8 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+ else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index c8be8ef..247fb82 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -83,6 +83,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+ else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
@@ -380,7 +382,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
*/
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
- sdata->vif.type != NL80211_IFTYPE_AP)
+ sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
return -EINVAL;
if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) {
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 7cfc286..211ebaf 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -203,6 +203,8 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+ else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 56c24ca..9f88183 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -35,6 +35,76 @@
#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
+static bool ieee80211_can_use_ext_chan(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *channel,
+ enum nl80211_channel_type channel_type)
+{
+ /* check if we are legally allowed to use HT extension channel */
+ if ((channel_type == NL80211_CHAN_HT40PLUS) ||
+ (channel_type == NL80211_CHAN_HT40MINUS)) {
+ int sec_freq = channel->center_freq +
+ (channel_type == NL80211_CHAN_HT40PLUS ? 20 : -20);
+ struct ieee80211_channel *sec_chan =
+ ieee80211_get_channel(sdata->wdev.wiphy, sec_freq);
+ if (!sec_chan || sec_chan->flags & (IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_PASSIVE_SCAN |
+ IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_RADAR)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void ieee80211_update_ht_elems(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ struct ieee80211_ht_info *ht_info)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband =
+ local->hw.wiphy->bands[local->oper_channel->band];
+ enum nl80211_channel_type channel_type =
+ ieee80211_ht_info_to_channel_type(ht_info);
+
+ if (!ieee80211_can_use_ext_chan(sdata, local->oper_channel, channel_type))
+ channel_type = NL80211_CHAN_HT20;
+
+ if (channel_type != local->_oper_channel_type) {
+ struct sk_buff *skb = rcu_dereference_protected(
+ sdata->u.ibss.presp,
+ lockdep_is_held(&ifibss->mtx));
+ struct sk_buff *nskb;
+ u8 *ht_ie;
+
+ /* update HT IE. If not yet existing, create one */
+ nskb = skb_copy(skb, GFP_ATOMIC);
+ ht_ie = (u8 *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
+ (const u8 *)(nskb->data + 24 +
+ sizeof(mgmt->u.beacon)),
+ nskb->len - 24 -
+ sizeof(mgmt->u.beacon));
+ if (!ht_ie)
+ ht_ie = skb_put(nskb, 4 +
+ sizeof(struct ieee80211_ht_cap) +
+ sizeof(struct ieee80211_ht_info));
+
+ ht_ie = ieee80211_ie_build_ht_cap(ht_ie, sband,
+ sband->ht_cap.cap);
+ ht_ie = ieee80211_ie_build_ht_info(ht_ie, &sband->ht_cap,
+ local->oper_channel, channel_type);
+ rcu_assign_pointer(sdata->u.ibss.presp, nskb);
+ kfree_skb(skb);
+
+ if(!ieee80211_set_channel_type(local, sdata, channel_type)) {
+ channel_type = NL80211_CHAN_HT20;
+ WARN_ON(!ieee80211_set_channel_type(local, sdata,
+ channel_type));
+ }
+
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ }
+
+}
static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
@@ -64,6 +134,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const int beacon_int,
struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
const u32 basic_rates,
const u16 capability, u64 tsf)
{
@@ -104,8 +175,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
+ /* entering a legacy IBSS. Use given HT configuration. */
+ if (channel_type == NL80211_CHAN_NO_HT)
+ channel_type = ifibss->channel_type;
local->oper_channel = chan;
- WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
+
+ /* if phy is on a different extension channel, setting ht40 will fail */
+ if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
+ channel_type = NL80211_CHAN_HT20;
+ WARN_ON(!ieee80211_set_channel_type(local, sdata,
+ channel_type));
+ }
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
sband = local->hw.wiphy->bands[chan->band];
@@ -171,6 +251,18 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
memcpy(skb_put(skb, ifibss->ie_len),
ifibss->ie, ifibss->ie_len);
+ /* add HT capability and information IEs */
+ if (channel_type != NL80211_CHAN_NO_HT && sband->ht_cap.ht_supported) {
+ pos = skb_put(skb, 4 +
+ sizeof(struct ieee80211_ht_cap) +
+ sizeof(struct ieee80211_ht_info));
+ pos = ieee80211_ie_build_ht_cap(pos, sband, sband->ht_cap.cap);
+ pos = ieee80211_ie_build_ht_info(pos,
+ &sband->ht_cap,
+ chan,
+ channel_type);
+ }
+
if (local->hw.queues >= 4) {
pos = skb_put(skb, 9);
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
@@ -219,6 +311,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
u32 basic_rates;
int i, j;
u16 beacon_int = cbss->beacon_interval;
+ const u8 *ht_info_ie;
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
lockdep_assert_held(&sdata->u.ibss.mtx);
@@ -242,9 +336,23 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
}
}
+ ht_info_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_INFORMATION);
+ if (ht_info_ie)
+ channel_type = ieee80211_ht_info_to_channel_type(
+ (struct ieee80211_ht_info *) (ht_info_ie + 2));
+
+ if (!ieee80211_can_use_ext_chan(sdata, cbss->channel, channel_type)) {
+ channel_type = NL80211_CHAN_HT20;
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: IBSS not allowed on secondary channel\n",
+ sdata->name);
+#endif
+ }
+
__ieee80211_sta_join_ibss(sdata, cbss->bssid,
beacon_int,
cbss->channel,
+ channel_type,
basic_rates,
cbss->capability,
cbss->tsf);
@@ -310,11 +418,24 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
} else
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
mgmt->sa, supp_rates,
- GFP_ATOMIC);
+ elems->ht_cap_elem, GFP_ATOMIC);
}
- if (sta && elems->wmm_info)
- set_sta_flags(sta, WLAN_STA_WME);
+ if (sta) {
+ if (elems->wmm_info)
+ set_sta_flags(sta, WLAN_STA_WME);
+
+ /* remote station uses ht */
+ if (elems->ht_info_elem) {
+ ieee80211_update_ht_elems(sdata, mgmt,
+ elems->ht_info_elem);
+ ieee80211_ht_cap_ie_to_sta_ht_cap(
+ local->hw.wiphy->bands[
+ local->oper_channel->band],
+ elems->ht_cap_elem,
+ &sta->sta.ht_cap);
+ }
+ }
rcu_read_unlock();
}
@@ -404,7 +525,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_join_ibss(sdata, bss);
supp_rates = ieee80211_sta_get_rates(local, elems, band);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
- supp_rates, GFP_KERNEL);
+ supp_rates, elems->ht_cap_elem, GFP_KERNEL);
}
put_bss:
@@ -417,7 +538,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* must be callable in atomic context.
*/
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- u8 *bssid,u8 *addr, u32 supp_rates,
+ u8 *bssid, u8 *addr, u32 supp_rates,
+ struct ieee80211_ht_cap *ht_cap,
gfp_t gfp)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
@@ -458,6 +580,11 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
sta->sta.supp_rates[band] = supp_rates |
ieee80211_mandatory_rates(local, band);
+ /* fill in ht rates */
+ if (ht_cap)
+ ieee80211_ht_cap_ie_to_sta_ht_cap(local->hw.wiphy->bands[band],
+ ht_cap, &sta->sta.ht_cap);
+
rate_control_rate_init(sta);
/* If it fails, maybe we raced another insertion? */
@@ -556,8 +683,8 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
sdata->drop_unencrypted = 0;
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
- ifibss->channel, ifibss->basic_rates,
- capability, 0);
+ ifibss->channel, ifibss->channel_type,
+ ifibss->basic_rates, capability, 0);
}
/*
@@ -594,10 +721,10 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
chan = ifibss->channel;
if (!is_zero_ether_addr(ifibss->bssid))
bssid = ifibss->bssid;
- cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid,
+ cbss = cfg80211_get_bss_ht(local->hw.wiphy, chan, bssid,
ifibss->ssid, ifibss->ssid_len,
WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY,
- capability);
+ capability, ifibss->channel_type);
if (cbss) {
struct ieee80211_bss *bss;
@@ -896,10 +1023,15 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb;
skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
- 36 /* bitrates */ +
- 34 /* SSID */ +
- 3 /* DS params */ +
- 4 /* IBSS params */ +
+ sizeof(struct ieee80211_hdr_3addr) +
+ 12 /* struct ieee80211_mgmt.u.beacon */ +
+ 2 + IEEE80211_MAX_SSID_LEN /* max SSID */ +
+ 2 + 8 /* max Supported Rates */ +
+ 3 /* max DS params */ +
+ 4 /* IBSS params */ +
+ 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
+ 2 + sizeof(struct ieee80211_ht_cap) +
+ 2 + sizeof(struct ieee80211_ht_info) +
params->ie_len);
if (!skb)
return -ENOMEM;
@@ -920,13 +1052,15 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
sdata->u.ibss.channel = params->channel;
+ sdata->u.ibss.channel_type = params->channel_type;
sdata->u.ibss.fixed_channel = params->channel_fixed;
/* fix ourselves to that channel now already */
if (params->channel_fixed) {
sdata->local->oper_channel = params->channel;
- WARN_ON(!ieee80211_set_channel_type(sdata->local, sdata,
- NL80211_CHAN_NO_HT));
+ if(!ieee80211_set_channel_type(sdata->local, sdata,
+ params->channel_type))
+ return -EINVAL;
}
if (params->ie) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index bfb820c..1384928 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -464,6 +464,7 @@ struct ieee80211_if_ibss {
u8 ssid_len, ie_len;
u8 *ie;
struct ieee80211_channel *channel;
+ enum nl80211_channel_type channel_type;
unsigned long ibss_join_req;
/* probe response/beacon for IBSS */
@@ -1147,6 +1148,7 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
u8 *bssid, u8 *addr, u32 supp_rates,
+ struct ieee80211_ht_cap *ht_cap,
gfp_t gfp);
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
struct cfg80211_ibss_params *params);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index fe2c2a7..fc31191 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2147,7 +2147,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
*/
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
- sdata->vif.type != NL80211_IFTYPE_AP)
+ sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
break;
/* verify action_code is present */
@@ -2664,7 +2665,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
else
rate_idx = status->rate_idx;
rx->sta = ieee80211_ibss_add_sta(sdata, bssid,
- hdr->addr2, BIT(rate_idx), GFP_ATOMIC);
+ hdr->addr2, BIT(rate_idx), NULL,
+ GFP_ATOMIC);
}
break;
case NL80211_IFTYPE_MESH_POINT:
--
1.7.3.4
On Mon, 2011-08-08 at 14:04 +0200, Alexander Simon wrote:
> +struct cfg80211_bss *cfg80211_get_bss_ht(struct wiphy *wiphy,
> + struct ieee80211_channel *channel,
> + const u8 *bssid,
> + const u8 *ssid, size_t ssid_len,
> + u16 capa_mask, u16 capa_val,
> + enum nl80211_channel_type channel_type);
indentation? Also, you really need kernel-doc for this.
> +++ b/net/wireless/chan.c
> @@ -75,6 +75,7 @@ bool can_beacon_sec_chan(struct wiphy *wiphy,
>
> return true;
> }
> +EXPORT_SYMBOL(can_beacon_sec_chan);
You've got to be kidding, if you export it you really need to give it a
decent name. Besides, you don't have to export it in this patch.
> int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
> struct wireless_dev *wdev, int freq,
> diff --git a/net/wireless/scan.c b/net/wireless/scan.c
> index 2936cb8..ff5b736 100644
> --- a/net/wireless/scan.c
> +++ b/net/wireless/scan.c
> @@ -364,6 +364,19 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy
> *wiphy,
> const u8 *ssid, size_t ssid_len,
> u16 capa_mask, u16 capa_val)
> {
> + /* call HT version with no HT requirements */
> + return cfg80211_get_bss_ht(wiphy, channel, bssid, ssid, ssid_len,
> + capa_mask, capa_val, NL80211_CHAN_NO_HT);
> +}
> +EXPORT_SYMBOL(cfg80211_get_bss);
I don't like the semantics of this. Why is NO_HT indicating "don't care
about HT"? How then do you really find a NO_HT BSS?
Also, this raises a more fundamental question -- didn't you earlier say
you wanted to be able to connect with any IBSS, even non-HT ones, but
now it must match?
johannes