From 9d6d6f4924133567a108a862d9cf949cd03f71cb Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 8 Apr 2013 11:06:12 -0700 Subject: mac80211: unset FC retry bit in mesh fwding path Otherwise forwarded frames would keep the retry bit set from the previous link transmission. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5b4492af4e8..5168f89c754 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2085,6 +2085,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) } fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data; + fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY); info = IEEE80211_SKB_CB(fwd_skb); memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; -- cgit v1.2.3-18-g5258 From 3088f7d2db42925808c4b43a6258647ee4d1dd5f Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 8 Apr 2013 11:06:16 -0700 Subject: mac80211: stringify another plink state The patch "mac80211: stringify mesh peering events" missed an opportunity to print the peering state as a string. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 937e06fe8f2..cdd41835334 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -544,8 +544,8 @@ static void mesh_plink_timer(unsigned long data) return; } mpl_dbg(sta->sdata, - "Mesh plink timer for %pM fired on state %d\n", - sta->sta.addr, sta->plink_state); + "Mesh plink timer for %pM fired on state %s\n", + sta->sta.addr, mplstates[sta->plink_state]); reason = 0; llid = sta->llid; plid = sta->plid; -- cgit v1.2.3-18-g5258 From e47468518b9dc42db459e7240909946316d9c6c9 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 8 Apr 2013 22:43:16 +0200 Subject: mac80211: fix recalc_radar hwconf sync problem local->hw.conf maybe not be synced when recalcing whether radar is enabled, sometimes leaving radar enabled even if it's not neccesary anymore. Fix this by: * setting radar_enabled when creating the chanctx * turning radar_enabled off before destroying the last channel context Reported-by: Zefir Kurtisi Signed-off-by: Simon Wunderlich Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 8024874ba95..166165efd8e 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -57,6 +57,22 @@ ieee80211_find_chanctx(struct ieee80211_local *local, return NULL; } +static bool ieee80211_is_radar_required(struct ieee80211_local *local) +{ + struct ieee80211_sub_if_data *sdata; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (sdata->radar_required) { + rcu_read_unlock(); + return true; + } + } + rcu_read_unlock(); + + return false; +} + static struct ieee80211_chanctx * ieee80211_new_chanctx(struct ieee80211_local *local, const struct cfg80211_chan_def *chandef, @@ -75,6 +91,9 @@ ieee80211_new_chanctx(struct ieee80211_local *local, ctx->conf.rx_chains_static = 1; ctx->conf.rx_chains_dynamic = 1; ctx->mode = mode; + ctx->conf.radar_enabled = ieee80211_is_radar_required(local); + if (!local->use_chanctx) + local->hw.conf.radar_enabled = ctx->conf.radar_enabled; if (!local->use_chanctx) { local->_oper_chandef = *chandef; @@ -99,6 +118,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local, static void ieee80211_free_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *ctx) { + bool check_single_channel = false; lockdep_assert_held(&local->chanctx_mtx); WARN_ON_ONCE(ctx->refcount != 0); @@ -108,6 +128,14 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local, chandef->width = NL80211_CHAN_WIDTH_20_NOHT; chandef->center_freq1 = chandef->chan->center_freq; chandef->center_freq2 = 0; + + /* NOTE: Disabling radar is only valid here for + * single channel context. To be sure, check it ... + */ + if (local->hw.conf.radar_enabled) + check_single_channel = true; + local->hw.conf.radar_enabled = false; + ieee80211_hw_config(local, 0); } else { drv_remove_chanctx(local, ctx); @@ -116,6 +144,9 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local, list_del_rcu(&ctx->list); kfree_rcu(ctx, rcu_head); + /* throw a warning if this wasn't the only channel context. */ + WARN_ON(check_single_channel && !list_empty(&local->chanctx_list)); + mutex_lock(&local->mtx); ieee80211_recalc_idle(local); mutex_unlock(&local->mtx); @@ -227,19 +258,11 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, struct ieee80211_chanctx *chanctx) { - struct ieee80211_sub_if_data *sdata; - bool radar_enabled = false; + bool radar_enabled; lockdep_assert_held(&local->chanctx_mtx); - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (sdata->radar_required) { - radar_enabled = true; - break; - } - } - rcu_read_unlock(); + radar_enabled = ieee80211_is_radar_required(local); if (radar_enabled == chanctx->conf.radar_enabled) return; -- cgit v1.2.3-18-g5258 From 0eabccd940f0f25da0ae29e555fddb8fb84d8e27 Mon Sep 17 00:00:00 2001 From: Marek Puzyniak Date: Wed, 10 Apr 2013 13:47:45 +0200 Subject: mac80211: clear SSID when stopping AP When AP interface is stopped ssid_len in the BSS configuration isn't cleared which can confuse drivers when switching modes. Set the length to zero when stopping the AP interface. Signed-off-by: Marek Puzyniak Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index edca2a288ab..fdd95bd751a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1052,6 +1052,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) ieee80211_free_keys(sdata); sdata->vif.bss_conf.enable_beacon = false; + sdata->vif.bss_conf.ssid_len = 0; clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); -- cgit v1.2.3-18-g5258 From a21a4d3e8ad4d513f5a6eefc91510febb205f601 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 7 Apr 2013 19:32:47 +0200 Subject: mac80211: always advertise STBC/MCSes even if no AP support Advertise STBC capabilities and MCS rates even if the AP doesn't support them. This has always been the right thing to do, but used to be problematic with some APs. Now WFA testing requires this so re-enable it, problematic APs would then presumably not pass the test and be fixed. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 32 -------------------------------- 1 file changed, 32 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 79647eaecc8..cf40fac0aae 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -604,7 +604,6 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, u8 *pos; u32 cap; struct ieee80211_sta_vht_cap vht_cap; - int i; BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); @@ -632,37 +631,6 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; - if (!(ap_vht_cap->vht_cap_info & - cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC))) - cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 | - IEEE80211_VHT_CAP_RXSTBC_3 | - IEEE80211_VHT_CAP_RXSTBC_4); - - for (i = 0; i < 8; i++) { - int shift = i * 2; - u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift; - u16 ap_mcs, our_mcs; - - ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) & - mask) >> shift; - our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) & - mask) >> shift; - - if (our_mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) - continue; - - switch (ap_mcs) { - default: - if (our_mcs <= ap_mcs) - break; - /* fall through */ - case IEEE80211_VHT_MCS_NOT_SUPPORTED: - vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask); - vht_cap.vht_mcs.rx_mcs_map |= - cpu_to_le16(ap_mcs << shift); - } - } - /* reserve and fill IE */ pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); -- cgit v1.2.3-18-g5258 From 0ca54f6c5fd4ce58aa044d1fc7f00d7f6cf2801c Mon Sep 17 00:00:00 2001 From: Marek Puzyniak Date: Wed, 10 Apr 2013 13:19:13 +0200 Subject: mac80211: provide SSID in IBSS mode Some drivers need SSID in AP and IBSS mode. AP SSID is provided through BSS_CHANGED_SSID notification. There was no easy way to do the same for IBSS. In IBSS mode SSID is known but was not stored in BSS configuration. Extend the AP-mode functionality to also work in IBSS mode. Signed-off-by: Marek Puzyniak Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 2a0b2186d98..b7bf6d76f1d 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -209,6 +209,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.beacon_int = beacon_int; sdata->vif.bss_conf.basic_rates = basic_rates; + sdata->vif.bss_conf.ssid_len = ifibss->ssid_len; + memcpy(sdata->vif.bss_conf.ssid, ifibss->ssid, ifibss->ssid_len); bss_change = BSS_CHANGED_BEACON_INT; bss_change |= ieee80211_reset_erp_info(sdata); bss_change |= BSS_CHANGED_BSSID; @@ -217,6 +219,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, bss_change |= BSS_CHANGED_BASIC_RATES; bss_change |= BSS_CHANGED_HT; bss_change |= BSS_CHANGED_IBSS; + bss_change |= BSS_CHANGED_SSID; /* * In 5 GHz/802.11a, we can always use short slot time. @@ -1159,6 +1162,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.ibss_joined = false; sdata->vif.bss_conf.ibss_creator = false; sdata->vif.bss_conf.enable_beacon = false; + sdata->vif.bss_conf.ssid_len = 0; clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_IBSS); -- cgit v1.2.3-18-g5258 From 2419ea14bb0dfabe740f1e005c0782db9bc56441 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 10 Apr 2013 15:41:40 -0700 Subject: mac80211: fix ieee80211_queue_stopped() Johannes Berg notes mac80211 drivers which use ieee80211_queue_stopped() really only want to know if they previously requested a queue stop. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 447e6651e7f..1d6217ac3ba 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -485,7 +485,8 @@ int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) return true; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - ret = !!local->queue_stop_reasons[queue]; + ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER, + &local->queue_stop_reasons[queue]); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); return ret; } -- cgit v1.2.3-18-g5258 From 5253ffb8c9e1f2bf25c2e85dc0be8f74f55cf1ce Mon Sep 17 00:00:00 2001 From: Karl Beldan Date: Fri, 5 Apr 2013 12:06:24 +0200 Subject: mac80211: always pick a basic rate to tx RTS/CTS for pre-HT rates When the 1st rate control entry is a pre-HT rate we want to set rts_cts_rate_idx "as the fastest basic rate that is not faster than the data rate"(code comments). But in case some bss allowed rate indexes are lower than the lowest bss basic rate, if the rate control selects a rate among the formers for its 1st rate control entry, rts_cts_rate_idx remains 0 and is not a basic rate index. This commit sets rts_cts_rate_idx to the lowest bss basic rate index in this situation. Note that the code assumes that lowest indexes == lowest bitrates. Signed-off-by: Karl Beldan Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index aad0bf5d881..c93483fd477 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -712,19 +712,22 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) } /* - * set up the RTS/CTS rate as the fastest basic rate - * that is not faster than the data rate + * Set up the RTS/CTS rate as the fastest basic rate + * that is not faster than the data rate unless there + * is no basic rate slower than the data rate, in which + * case we pick the slowest basic rate * * XXX: Should this check all retry rates? */ if (!(info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) { - s8 baserate = 0; + u32 basic_rates = tx->sdata->vif.bss_conf.basic_rates; + s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0; rate = &sband->bitrates[info->control.rates[0].idx]; for (i = 0; i < sband->n_bitrates; i++) { /* must be a basic rate */ - if (!(tx->sdata->vif.bss_conf.basic_rates & BIT(i))) + if (!(basic_rates & BIT(i))) continue; /* must not be faster than the data rate */ if (sband->bitrates[i].bitrate > rate->bitrate) -- cgit v1.2.3-18-g5258 From 990de49f74e772b6db5208457b7aa712a5f4db86 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 16 Apr 2013 14:32:26 +0200 Subject: wireless: regulatory: fix channel disabling race condition When a full scan 2.4 and 5 GHz scan is scheduled, but then the 2.4 GHz part of the scan disables a 5.2 GHz channel due to, e.g. receiving country or frequency information, that 5.2 GHz channel might already be in the list of channels to scan next. Then, when the driver checks if it should do a passive scan, that will return false and attempt an active scan. This is not only wrong but can also lead to the iwlwifi device firmware crashing since it checks regulatory as well. Fix this by not setting the channel flags to just disabled but rather OR'ing in the disabled flag. That way, even if the race happens, the channel will be scanned passively which is still (mostly) correct. Cc: stable@vger.kernel.org Signed-off-by: Johannes Berg --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e6df52dc8c6..cc35fbaa457 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -855,7 +855,7 @@ static void handle_channel(struct wiphy *wiphy, return; REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); - chan->flags = IEEE80211_CHAN_DISABLED; + chan->flags |= IEEE80211_CHAN_DISABLED; return; } -- cgit v1.2.3-18-g5258 From 6553bf04ff6686db658e09626edad003809f6baf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Mar 2013 10:26:17 +0100 Subject: mac80211: use second center_freq segment only in 80+80 The field is otherwise reserved, so we shouldn't read and reject it, though any sane system will probably have to set it to 0 anyway. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index cf40fac0aae..2a2c4535449 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -303,12 +303,6 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, channel->band); vht_chandef.center_freq2 = 0; - if (vht_oper->center_freq_seg2_idx) - vht_chandef.center_freq2 = - ieee80211_channel_to_frequency( - vht_oper->center_freq_seg2_idx, - channel->band); - switch (vht_oper->chan_width) { case IEEE80211_VHT_CHANWIDTH_USE_HT: vht_chandef.width = chandef->width; @@ -321,6 +315,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, break; case IEEE80211_VHT_CHANWIDTH_80P80MHZ: vht_chandef.width = NL80211_CHAN_WIDTH_80P80; + vht_chandef.center_freq2 = + ieee80211_channel_to_frequency( + vht_oper->center_freq_seg2_idx, + channel->band); break; default: if (verbose) -- cgit v1.2.3-18-g5258 From 37799e52a29af2268d1fbe18908a0d6b9f68af88 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Mar 2013 14:02:26 +0100 Subject: mac80211: unify CSA action frame/beacon processing CSA action frame content should be processed as variable IEs rather than fixed to make it extensible. Unify the code and process them just like CSA in beacons to make it easier to extend for HT/VHT. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 4 --- net/mac80211/mlme.c | 71 ++++++++++++++++++++++++++++------------------ net/mac80211/rx.c | 4 --- 3 files changed, 43 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8d5dcbf17bb..373460f9c06 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1252,10 +1252,6 @@ void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata); int ieee80211_max_network_latency(struct notifier_block *nb, unsigned long data, void *dummy); int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata); -void -ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, - const struct ieee80211_channel_sw_ie *sw_elem, - struct ieee80211_bss *bss, u64 timestamp); void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2a2c4535449..ade3cd6c337 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1020,33 +1020,37 @@ static void ieee80211_chswitch_timer(unsigned long data) ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work); } -void +static void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, - const struct ieee80211_channel_sw_ie *sw_elem, - struct ieee80211_bss *bss, u64 timestamp) + u64 timestamp, struct ieee802_11_elems *elems) { - struct cfg80211_bss *cbss = - container_of((void *)bss, struct cfg80211_bss, priv); - struct ieee80211_channel *new_ch; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num, - cbss->channel->band); + struct cfg80211_bss *cbss = ifmgd->associated; + struct ieee80211_bss *bss; + struct ieee80211_channel *new_ch; + int new_freq; struct ieee80211_chanctx *chanctx; ASSERT_MGD_MTX(ifmgd); - if (!ifmgd->associated) + if (!cbss) return; if (sdata->local->scanning) return; - /* Disregard subsequent beacons if we are already running a timer - processing a CSA */ - + /* disregard subsequent announcements if we are already processing */ if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) return; + if (!elems->ch_switch_ie) + return; + + bss = (void *)cbss->priv; + + new_freq = ieee80211_channel_to_frequency( + elems->ch_switch_ie->new_ch_num, + cbss->channel->band); new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { sdata_info(sdata, @@ -1086,7 +1090,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sdata->local->csa_channel = new_ch; - if (sw_elem->mode) + if (elems->ch_switch_ie->mode) ieee80211_stop_queues_by_reason(&sdata->local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -1095,9 +1099,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, /* use driver's channel switch callback */ struct ieee80211_channel_switch ch_switch = { .timestamp = timestamp, - .block_tx = sw_elem->mode, + .block_tx = elems->ch_switch_ie->mode, .channel = new_ch, - .count = sw_elem->count, + .count = elems->ch_switch_ie->count, }; drv_channel_switch(sdata->local, &ch_switch); @@ -1105,11 +1109,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, } /* channel switch handled in software */ - if (sw_elem->count <= 1) + if (elems->ch_switch_ie->count <= 1) ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); else mod_timer(&ifmgd->chswitch_timer, - TU_TO_EXP_TIME(sw_elem->count * + TU_TO_EXP_TIME(elems->ch_switch_ie->count * cbss->beacon_interval)); } @@ -2655,7 +2659,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (bss) ieee80211_rx_bss_put(local, bss); - if (!sdata->u.mgd.associated) + if (!sdata->u.mgd.associated || + !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) return; if (need_ps) { @@ -2664,10 +2669,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, mutex_unlock(&local->iflist_mtx); } - if (elems->ch_switch_ie && - memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0) - ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie, - bss, rx_status->mactime); + ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); } @@ -3061,14 +3063,27 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss); break; case IEEE80211_STYPE_ACTION: - switch (mgmt->u.action.category) { - case WLAN_CATEGORY_SPECTRUM_MGMT: + if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) { + struct ieee802_11_elems elems; + int ies_len = skb->len - + offsetof(struct ieee80211_mgmt, + u.action.u.chan_switch.variable); + + if (ies_len < 0) + break; + + ieee802_11_parse_elems( + mgmt->u.action.u.chan_switch.variable, + ies_len, &elems); + + if (elems.parse_error) + break; + ieee80211_sta_process_chanswitch(sdata, - &mgmt->u.action.u.chan_switch.sw_elem, - (void *)ifmgd->associated->priv, - rx_status->mactime); - break; + rx_status->mactime, + &elems); } + break; } mutex_unlock(&ifmgd->mtx); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5168f89c754..e9825f15c14 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2507,10 +2507,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ieee80211_process_measurement_req(sdata, mgmt, len); goto handled; case WLAN_ACTION_SPCT_CHL_SWITCH: - if (len < (IEEE80211_MIN_ACTION_SIZE + - sizeof(mgmt->u.action.u.chan_switch))) - break; - if (sdata->vif.type != NL80211_IFTYPE_STATION) break; -- cgit v1.2.3-18-g5258 From 1ce3e82b0eb472161313183be0033e46d5c4bbaf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Aug 2012 17:00:55 +0200 Subject: cfg80211: add ieee80211_operating_class_to_band This function converts a (global only!) operating class to an internal band identifier. This will be needed for extended channel switch support. Signed-off-by: Johannes Berg --- net/wireless/util.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'net') diff --git a/net/wireless/util.c b/net/wireless/util.c index 37a56ee1e1e..3d8a1334f4a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1155,6 +1155,26 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, } EXPORT_SYMBOL(cfg80211_get_p2p_attr); +bool ieee80211_operating_class_to_band(u8 operating_class, + enum ieee80211_band *band) +{ + switch (operating_class) { + case 112: + case 115 ... 127: + *band = IEEE80211_BAND_5GHZ; + return true; + case 81: + case 82: + case 83: + case 84: + *band = IEEE80211_BAND_2GHZ; + return true; + } + + return false; +} +EXPORT_SYMBOL(ieee80211_operating_class_to_band); + int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int) { -- cgit v1.2.3-18-g5258 From b4f286a1c0ad0b84c2d502b354d4d98d5a86c64b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Mar 2013 14:13:58 +0100 Subject: mac80211: support extended channel switch Support extended channel switch when the operating class is one of the global operating classes as defined in Annex E of 802.11-2012. If it isn't, disconnect from the AP instead. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 77 ++++++++++++++++++++++++++++++---------------- net/mac80211/util.c | 7 +++++ 3 files changed, 59 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 373460f9c06..10c3180b165 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1178,6 +1178,7 @@ struct ieee802_11_elems { const u8 *perr; const struct ieee80211_rann_ie *rann; const struct ieee80211_channel_sw_ie *ch_switch_ie; + const struct ieee80211_ext_chansw_ie *ext_chansw_ie; const u8 *country_elem; const u8 *pwr_constr_elem; const struct ieee80211_timeout_interval_ie *timeout_int; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ade3cd6c337..bc6f87edc62 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1024,56 +1024,79 @@ static void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, u64 timestamp, struct ieee802_11_elems *elems) { + struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct cfg80211_bss *cbss = ifmgd->associated; struct ieee80211_bss *bss; struct ieee80211_channel *new_ch; - int new_freq; struct ieee80211_chanctx *chanctx; + enum ieee80211_band new_band; + int new_freq; + u8 new_chan_no; + u8 count; + u8 mode; ASSERT_MGD_MTX(ifmgd); if (!cbss) return; - if (sdata->local->scanning) + if (local->scanning) return; /* disregard subsequent announcements if we are already processing */ if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) return; - if (!elems->ch_switch_ie) + if (elems->ext_chansw_ie) { + if (!ieee80211_operating_class_to_band( + elems->ext_chansw_ie->new_operating_class, + &new_band)) { + sdata_info(sdata, + "cannot understand ECSA IE operating class %d, disconnecting\n", + elems->ext_chansw_ie->new_operating_class); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + } + new_chan_no = elems->ext_chansw_ie->new_ch_num; + count = elems->ext_chansw_ie->count; + mode = elems->ext_chansw_ie->mode; + } else if (elems->ch_switch_ie) { + new_band = cbss->channel->band; + new_chan_no = elems->ch_switch_ie->new_ch_num; + count = elems->ch_switch_ie->count; + mode = elems->ch_switch_ie->mode; + } else { + /* nothing here we understand */ return; + } bss = (void *)cbss->priv; - new_freq = ieee80211_channel_to_frequency( - elems->ch_switch_ie->new_ch_num, - cbss->channel->band); - new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); + new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); + new_ch = ieee80211_get_channel(local->hw.wiphy, new_freq); if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { sdata_info(sdata, "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", ifmgd->associated->bssid, new_freq); - ieee80211_queue_work(&sdata->local->hw, + ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); return; } ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; - if (sdata->local->use_chanctx) { + if (local->use_chanctx) { sdata_info(sdata, "not handling channel switch with channel contexts\n"); - ieee80211_queue_work(&sdata->local->hw, + ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); return; } - mutex_lock(&sdata->local->chanctx_mtx); + mutex_lock(&local->chanctx_mtx); if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { - mutex_unlock(&sdata->local->chanctx_mtx); + mutex_unlock(&local->chanctx_mtx); return; } chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), @@ -1081,40 +1104,39 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (chanctx->refcount > 1) { sdata_info(sdata, "channel switch with multiple interfaces on the same channel, disconnecting\n"); - ieee80211_queue_work(&sdata->local->hw, + ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); - mutex_unlock(&sdata->local->chanctx_mtx); + mutex_unlock(&local->chanctx_mtx); return; } - mutex_unlock(&sdata->local->chanctx_mtx); + mutex_unlock(&local->chanctx_mtx); - sdata->local->csa_channel = new_ch; + local->csa_channel = new_ch; - if (elems->ch_switch_ie->mode) - ieee80211_stop_queues_by_reason(&sdata->local->hw, + if (mode) + ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); - if (sdata->local->ops->channel_switch) { + if (local->ops->channel_switch) { /* use driver's channel switch callback */ struct ieee80211_channel_switch ch_switch = { .timestamp = timestamp, - .block_tx = elems->ch_switch_ie->mode, + .block_tx = mode, .channel = new_ch, - .count = elems->ch_switch_ie->count, + .count = count, }; - drv_channel_switch(sdata->local, &ch_switch); + drv_channel_switch(local, &ch_switch); return; } /* channel switch handled in software */ - if (elems->ch_switch_ie->count <= 1) - ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); + if (count <= 1) + ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work); else mod_timer(&ifmgd->chswitch_timer, - TU_TO_EXP_TIME(elems->ch_switch_ie->count * - cbss->beacon_interval)); + TU_TO_EXP_TIME(count * cbss->beacon_interval)); } static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, @@ -2629,6 +2651,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel; bool need_ps = false; + lockdep_assert_held(&sdata->u.mgd.mtx); + if ((sdata->u.mgd.associated && ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) || (sdata->u.mgd.assoc_data && @@ -2670,6 +2694,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, } ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); + } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 1d6217ac3ba..e4a6d559372 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -863,6 +863,13 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, } elems->ch_switch_ie = (void *)pos; break; + case WLAN_EID_EXT_CHANSWITCH_ANN: + if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { + elem_parse_failed = true; + break; + } + elems->ext_chansw_ie = (void *)pos; + break; case WLAN_EID_COUNTRY: elems->country_elem = pos; elems->country_elem_len = elen; -- cgit v1.2.3-18-g5258 From 85220d71bf3ca1ba9129e0744247ae5f61bec559 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 25 Mar 2013 18:29:27 +0100 Subject: mac80211: support secondary channel offset in CSA Add support for the secondary channel offset IE in channel switch announcements. This is necessary for proper handling of CSA on HT access points. For this to work it is also necessary to convert everything here to use chandef structs instead of just channels. The driver updates aren't really correct though. In particular, the TI wl18xx driver update can't possibly be right since it just ignores the new channel width for lack of firmware API. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 3 +- net/mac80211/mlme.c | 71 ++++++++++++++++++++++++++++++++++++---------- net/mac80211/trace.h | 8 +++--- net/mac80211/util.c | 8 ++++++ 4 files changed, 70 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 10c3180b165..8f240c0ec30 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1019,7 +1019,7 @@ struct ieee80211_local { enum mac80211_scan_state next_scan_state; struct delayed_work scan_work; struct ieee80211_sub_if_data __rcu *scan_sdata; - struct ieee80211_channel *csa_channel; + struct cfg80211_chan_def csa_chandef; /* For backward compatibility only -- do not use */ struct cfg80211_chan_def _oper_chandef; @@ -1183,6 +1183,7 @@ struct ieee802_11_elems { const u8 *pwr_constr_elem; const struct ieee80211_timeout_interval_ie *timeout_int; const u8 *opmode_notif; + const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; /* length of them, respectively */ u8 ssid_len; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bc6f87edc62..bd581a80e4b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -289,6 +289,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, } else { /* 40 MHz (and 80 MHz) must be supported for VHT */ ret = IEEE80211_STA_DISABLE_VHT; + /* also mark 40 MHz disabled */ + ret |= IEEE80211_STA_DISABLE_40MHZ; goto out; } @@ -964,16 +966,7 @@ static void ieee80211_chswitch_work(struct work_struct *work) if (!ifmgd->associated) goto out; - /* - * FIXME: Here we are downgrading to NL80211_CHAN_WIDTH_20_NOHT - * and don't adjust our ht/vht settings - * This is wrong - we should behave according to the CSA params - */ - local->_oper_chandef.chan = local->csa_channel; - local->_oper_chandef.width = NL80211_CHAN_WIDTH_20_NOHT; - local->_oper_chandef.center_freq1 = - local->_oper_chandef.chan->center_freq; - local->_oper_chandef.center_freq2 = 0; + local->_oper_chandef = local->csa_chandef; if (!local->ops->channel_switch) { /* call "hw_config" only if doing sw channel switch */ @@ -1028,13 +1021,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct cfg80211_bss *cbss = ifmgd->associated; struct ieee80211_bss *bss; - struct ieee80211_channel *new_ch; struct ieee80211_chanctx *chanctx; enum ieee80211_band new_band; int new_freq; u8 new_chan_no; u8 count; u8 mode; + struct cfg80211_chan_def new_chandef = {}; + int secondary_channel_offset = -1; ASSERT_MGD_MTX(ifmgd); @@ -1048,6 +1042,19 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) return; + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { + /* if HT is enabled and the IE not present, it's still HT */ + secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + if (elems->sec_chan_offs) + secondary_channel_offset = + elems->sec_chan_offs->sec_chan_offs; + } + + if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && + (secondary_channel_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE || + secondary_channel_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)) + secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + if (elems->ext_chansw_ie) { if (!ieee80211_operating_class_to_band( elems->ext_chansw_ie->new_operating_class, @@ -1074,8 +1081,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, bss = (void *)cbss->priv; new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); - new_ch = ieee80211_get_channel(local->hw.wiphy, new_freq); - if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { + new_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); + if (!new_chandef.chan || + new_chandef.chan->flags & IEEE80211_CHAN_DISABLED) { sdata_info(sdata, "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", ifmgd->associated->bssid, new_freq); @@ -1084,6 +1092,39 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, return; } + switch (secondary_channel_offset) { + default: + /* secondary_channel_offset was present but is invalid */ + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + cfg80211_chandef_create(&new_chandef, new_chandef.chan, + NL80211_CHAN_HT20); + break; + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + cfg80211_chandef_create(&new_chandef, new_chandef.chan, + NL80211_CHAN_HT40PLUS); + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + cfg80211_chandef_create(&new_chandef, new_chandef.chan, + NL80211_CHAN_HT40MINUS); + break; + case -1: + cfg80211_chandef_create(&new_chandef, new_chandef.chan, + NL80211_CHAN_NO_HT); + break; + } + + if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef, + IEEE80211_CHAN_DISABLED)) { + sdata_info(sdata, + "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", + ifmgd->associated->bssid, new_freq, + new_chandef.width, new_chandef.center_freq1, + new_chandef.center_freq2); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + return; + } + ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; if (local->use_chanctx) { @@ -1111,7 +1152,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, } mutex_unlock(&local->chanctx_mtx); - local->csa_channel = new_ch; + local->csa_chandef = new_chandef; if (mode) ieee80211_stop_queues_by_reason(&local->hw, @@ -1123,7 +1164,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel_switch ch_switch = { .timestamp = timestamp, .block_tx = mode, - .channel = new_ch, + .chandef = new_chandef, .count = count, }; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 8286dcef228..c215fafd7a2 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -990,23 +990,23 @@ TRACE_EVENT(drv_channel_switch, TP_STRUCT__entry( LOCAL_ENTRY + CHANDEF_ENTRY __field(u64, timestamp) __field(bool, block_tx) - __field(u16, freq) __field(u8, count) ), TP_fast_assign( LOCAL_ASSIGN; + CHANDEF_ASSIGN(&ch_switch->chandef) __entry->timestamp = ch_switch->timestamp; __entry->block_tx = ch_switch->block_tx; - __entry->freq = ch_switch->channel->center_freq; __entry->count = ch_switch->count; ), TP_printk( - LOCAL_PR_FMT " new freq:%u count:%d", - LOCAL_PR_ARG, __entry->freq, __entry->count + LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d", + LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count ) ); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e4a6d559372..155056c90ed 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -716,6 +716,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, case WLAN_EID_COUNTRY: case WLAN_EID_PWR_CONSTRAINT: case WLAN_EID_TIMEOUT_INTERVAL: + case WLAN_EID_SECONDARY_CHANNEL_OFFSET: if (test_bit(id, seen_elems)) { elems->parse_error = true; left -= elen; @@ -870,6 +871,13 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, } elems->ext_chansw_ie = (void *)pos; break; + case WLAN_EID_SECONDARY_CHANNEL_OFFSET: + if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { + elem_parse_failed = true; + break; + } + elems->sec_chan_offs = (void *)pos; + break; case WLAN_EID_COUNTRY: elems->country_elem = pos; elems->country_elem_len = elen; -- cgit v1.2.3-18-g5258 From 1b3a2e494bc793445f576c5476e9767cf7621684 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Mar 2013 15:17:18 +0100 Subject: mac80211: handle extended channel switch announcement Handle the (public) extended channel switch announcement action frames. Parts of the data in these frames isn't really in IEs, but put it into the elems struct anyway to simplify the handling. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 31 +++++++++++++++++++++++++++---- net/mac80211/rx.c | 16 ++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bd581a80e4b..c53aedb47a6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3100,6 +3100,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, enum rx_mgmt_action rma = RX_MGMT_NONE; u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; u16 fc; + struct ieee802_11_elems elems; + int ies_len; rx_status = (struct ieee80211_rx_status *) skb->cb; mgmt = (struct ieee80211_mgmt *) skb->data; @@ -3130,10 +3132,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, break; case IEEE80211_STYPE_ACTION: if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) { - struct ieee802_11_elems elems; - int ies_len = skb->len - - offsetof(struct ieee80211_mgmt, - u.action.u.chan_switch.variable); + ies_len = skb->len - + offsetof(struct ieee80211_mgmt, + u.action.u.chan_switch.variable); if (ies_len < 0) break; @@ -3145,6 +3146,28 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, if (elems.parse_error) break; + ieee80211_sta_process_chanswitch(sdata, + rx_status->mactime, + &elems); + } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { + ies_len = skb->len - + offsetof(struct ieee80211_mgmt, + u.action.u.ext_chan_switch.variable); + + if (ies_len < 0) + break; + + ieee802_11_parse_elems( + mgmt->u.action.u.ext_chan_switch.variable, + ies_len, &elems); + + if (elems.parse_error) + break; + + /* for the handling code pretend this was also an IE */ + elems.ext_chansw_ie = + &mgmt->u.action.u.ext_chan_switch.data; + ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, &elems); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e9825f15c14..643fcf7c9dc 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2424,6 +2424,22 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) } break; + case WLAN_CATEGORY_PUBLIC: + if (len < IEEE80211_MIN_ACTION_SIZE + 1) + goto invalid; + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + if (!rx->sta) + break; + if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) + break; + if (mgmt->u.action.u.ext_chan_switch.action_code != + WLAN_PUB_ACTION_EXT_CHANSW_ANN) + break; + if (len < offsetof(struct ieee80211_mgmt, + u.action.u.ext_chan_switch.variable)) + goto invalid; + goto queue; case WLAN_CATEGORY_VHT: if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_MESH_POINT && -- cgit v1.2.3-18-g5258 From b2e506bfc4d752b68a0ccaae1e977898263eba4c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 26 Mar 2013 14:54:16 +0100 Subject: mac80211: parse VHT channel switch IEs VHT introduces multiple IEs that need to be parsed for a wide bandwidth channel switch. Two are (currently) needed in mac80211: * wide bandwidth channel switch element * channel switch wrapper element The former is contained in the latter for beacons and probe responses, but not for the spectrum management action frames so the IE parser needs a new argument to differentiate them. Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 2 +- net/mac80211/ieee80211_i.h | 7 ++++--- net/mac80211/mesh.c | 4 ++-- net/mac80211/mesh_hwmp.c | 2 +- net/mac80211/mesh_plink.c | 2 +- net/mac80211/mlme.c | 16 ++++++++-------- net/mac80211/scan.c | 2 +- net/mac80211/util.c | 36 +++++++++++++++++++++++++++++++++++- 8 files changed, 53 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b7bf6d76f1d..170f9a7fa31 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -914,7 +914,7 @@ void ieee80211_rx_mgmt_probe_beacon(struct ieee80211_sub_if_data *sdata, return; ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, - &elems); + false, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8f240c0ec30..f4a65a340a5 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1179,6 +1179,7 @@ struct ieee802_11_elems { const struct ieee80211_rann_ie *rann; const struct ieee80211_channel_sw_ie *ch_switch_ie; const struct ieee80211_ext_chansw_ie *ext_chansw_ie; + const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; const u8 *country_elem; const u8 *pwr_constr_elem; const struct ieee80211_timeout_interval_ie *timeout_int; @@ -1490,13 +1491,13 @@ static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, ieee80211_tx_skb_tid(sdata, skb, 7); } -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, +u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action, struct ieee802_11_elems *elems, u64 filter, u32 crc); -static inline void ieee802_11_parse_elems(u8 *start, size_t len, +static inline void ieee802_11_parse_elems(u8 *start, size_t len, bool action, struct ieee802_11_elems *elems) { - ieee802_11_parse_elems_crc(start, len, elems, 0, 0); + ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); } u32 ieee80211_mandatory_rates(struct ieee80211_local *local, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 0acc2874d29..4b984765d62 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -838,7 +838,7 @@ ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, if (baselen > len) return; - ieee802_11_parse_elems(pos, len - baselen, &elems); + ieee802_11_parse_elems(pos, len - baselen, false, &elems); /* 802.11-2012 10.1.4.3.2 */ if ((!ether_addr_equal(mgmt->da, sdata->vif.addr) && @@ -899,7 +899,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, return; ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, - &elems); + false, &elems); /* ignore non-mesh or secure / unsecure mismatch */ if ((!elems.mesh_id || !elems.mesh_config) || diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index c82d5e6a24c..486819cd02c 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -880,7 +880,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, baselen = (u8 *) mgmt->u.action.u.mesh_action.variable - (u8 *) mgmt; ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable, - len - baselen, &elems); + len - baselen, false, &elems); if (elems.preq) { if (elems.preq_len != 37) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index cdd41835334..09bebed9941 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -687,7 +687,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, baseaddr += 4; baselen += 4; } - ieee802_11_parse_elems(baseaddr, len - baselen, &elems); + ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); if (!elems.peering) { mpl_dbg(sdata, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c53aedb47a6..3e0421265bf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2203,7 +2203,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, u32 tx_flags = 0; pos = mgmt->u.auth.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); if (!elems.challenge) return; auth_data->expected_transaction = 4; @@ -2468,7 +2468,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } pos = mgmt->u.assoc_resp.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); if (!elems.supp_rates) { sdata_info(sdata, "no SuppRates element in AssocResp\n"); @@ -2637,7 +2637,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); pos = mgmt->u.assoc_resp.variable; - ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); + ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems); if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && elems.timeout_int && @@ -2760,7 +2760,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, return; ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, - &elems); + false, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); @@ -2843,7 +2843,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) { ieee802_11_parse_elems(mgmt->u.beacon.variable, - len - baselen, &elems); + len - baselen, false, &elems); ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ifmgd->assoc_data->have_beacon = true; @@ -2953,7 +2953,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4); ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, - len - baselen, &elems, + len - baselen, false, &elems, care_about_ies, ncrc); if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { @@ -3141,7 +3141,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems( mgmt->u.action.u.chan_switch.variable, - ies_len, &elems); + ies_len, true, &elems); if (elems.parse_error) break; @@ -3159,7 +3159,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee802_11_parse_elems( mgmt->u.action.u.ext_chan_switch.variable, - ies_len, &elems); + ies_len, true, &elems); if (elems.parse_error) break; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 33fbf104569..99b103921a4 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -181,7 +181,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) if (baselen > skb->len) return; - ieee802_11_parse_elems(elements, skb->len - baselen, &elems); + ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems); channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 155056c90ed..3f87fa468b1 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -661,7 +661,7 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_queue_delayed_work); -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, +u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, bool action, struct ieee802_11_elems *elems, u64 filter, u32 crc) { @@ -669,6 +669,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, u8 *pos = start; bool calc_crc = filter != 0; DECLARE_BITMAP(seen_elems, 256); + const u8 *ie; bitmap_zero(seen_elems, 256); memset(elems, 0, sizeof(*elems)); @@ -717,6 +718,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, case WLAN_EID_PWR_CONSTRAINT: case WLAN_EID_TIMEOUT_INTERVAL: case WLAN_EID_SECONDARY_CHANNEL_OFFSET: + case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: + /* + * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible + * that if the content gets bigger it might be needed more than once + */ if (test_bit(id, seen_elems)) { elems->parse_error = true; left -= elen; @@ -878,6 +884,34 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, } elems->sec_chan_offs = (void *)pos; break; + case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: + if (!action || + elen != sizeof(*elems->wide_bw_chansw_ie)) { + elem_parse_failed = true; + break; + } + elems->wide_bw_chansw_ie = (void *)pos; + break; + case WLAN_EID_CHANNEL_SWITCH_WRAPPER: + if (action) { + elem_parse_failed = true; + break; + } + /* + * This is a bit tricky, but as we only care about + * the wide bandwidth channel switch element, so + * just parse it out manually. + */ + ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, + pos, elen); + if (ie) { + if (ie[1] == sizeof(*elems->wide_bw_chansw_ie)) + elems->wide_bw_chansw_ie = + (void *)(ie + 2); + else + elem_parse_failed = true; + } + break; case WLAN_EID_COUNTRY: elems->country_elem = pos; elems->country_elem_len = elen; -- cgit v1.2.3-18-g5258 From cd64f2a9b4a9eb055e4adc14b559055775b1b62f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Mar 2013 10:44:18 +0100 Subject: mac80211: handle wide bandwidth channel switch Parse and react to the wide bandwidth channel switch element in beacons/action frames. Finding the element was done in a previous patch (it has different positions in beacons/action frames), now handle it. If there's something wrong with it simply disconnect. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 96 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 3e0421265bf..43023f0db68 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1027,7 +1027,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, u8 new_chan_no; u8 count; u8 mode; + struct ieee80211_channel *new_chan; struct cfg80211_chan_def new_chandef = {}; + struct cfg80211_chan_def new_vht_chandef = {}; + const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; + const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; int secondary_channel_offset = -1; ASSERT_MGD_MTX(ifmgd); @@ -1042,18 +1046,17 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) return; - if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { - /* if HT is enabled and the IE not present, it's still HT */ - secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - if (elems->sec_chan_offs) - secondary_channel_offset = - elems->sec_chan_offs->sec_chan_offs; + sec_chan_offs = elems->sec_chan_offs; + wide_bw_chansw_ie = elems->wide_bw_chansw_ie; + + if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | + IEEE80211_STA_DISABLE_40MHZ)) { + sec_chan_offs = NULL; + wide_bw_chansw_ie = NULL; } - if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && - (secondary_channel_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE || - secondary_channel_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)) - secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) + wide_bw_chansw_ie = NULL; if (elems->ext_chansw_ie) { if (!ieee80211_operating_class_to_band( @@ -1081,9 +1084,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, bss = (void *)cbss->priv; new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); - new_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); - if (!new_chandef.chan || - new_chandef.chan->flags & IEEE80211_CHAN_DISABLED) { + new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); + if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { sdata_info(sdata, "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", ifmgd->associated->bssid, new_freq); @@ -1092,27 +1094,87 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, return; } + if (sec_chan_offs) { + secondary_channel_offset = sec_chan_offs->sec_chan_offs; + } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { + /* if HT is enabled and the IE not present, it's still HT */ + secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + } + switch (secondary_channel_offset) { default: /* secondary_channel_offset was present but is invalid */ case IEEE80211_HT_PARAM_CHA_SEC_NONE: - cfg80211_chandef_create(&new_chandef, new_chandef.chan, + cfg80211_chandef_create(&new_chandef, new_chan, NL80211_CHAN_HT20); break; case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - cfg80211_chandef_create(&new_chandef, new_chandef.chan, + cfg80211_chandef_create(&new_chandef, new_chan, NL80211_CHAN_HT40PLUS); break; case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - cfg80211_chandef_create(&new_chandef, new_chandef.chan, + cfg80211_chandef_create(&new_chandef, new_chan, NL80211_CHAN_HT40MINUS); break; case -1: - cfg80211_chandef_create(&new_chandef, new_chandef.chan, + cfg80211_chandef_create(&new_chandef, new_chan, NL80211_CHAN_NO_HT); break; } + if (wide_bw_chansw_ie) { + new_vht_chandef.chan = new_chan; + new_vht_chandef.center_freq1 = + ieee80211_channel_to_frequency( + wide_bw_chansw_ie->new_center_freq_seg0, + new_band); + + switch (wide_bw_chansw_ie->new_channel_width) { + default: + /* hmmm, ignore VHT and use HT if present */ + case IEEE80211_VHT_CHANWIDTH_USE_HT: + new_vht_chandef.chan = NULL; + break; + case IEEE80211_VHT_CHANWIDTH_80MHZ: + new_vht_chandef.width = NL80211_CHAN_WIDTH_80; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + new_vht_chandef.width = NL80211_CHAN_WIDTH_160; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + /* field is otherwise reserved */ + new_vht_chandef.center_freq2 = + ieee80211_channel_to_frequency( + wide_bw_chansw_ie->new_center_freq_seg1, + new_band); + new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; + break; + } + if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && + new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) + chandef_downgrade(&new_vht_chandef); + if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && + new_vht_chandef.width == NL80211_CHAN_WIDTH_160) + chandef_downgrade(&new_vht_chandef); + if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && + new_vht_chandef.width > NL80211_CHAN_WIDTH_20) + chandef_downgrade(&new_vht_chandef); + } + + /* if VHT data is there validate & use it */ + if (new_vht_chandef.chan) { + if (!cfg80211_chandef_compatible(&new_vht_chandef, + &new_chandef)) { + sdata_info(sdata, + "AP %pM CSA has inconsistent channel data, disconnecting\n", + ifmgd->associated->bssid); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + return; + } + new_chandef = new_vht_chandef; + } + if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef, IEEE80211_CHAN_DISABLED)) { sdata_info(sdata, -- cgit v1.2.3-18-g5258 From 7a7da6ee0ea3443cd5111adffa80a3daba4bb8df Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Tue, 9 Apr 2013 17:14:09 +0300 Subject: mac80211: remove warning from ieee80211_beacon_loss Currently, mac80211 assumes that connection monitor offload for BSS station implies that the device: - sends periodic keep alive packets to associated AP - monitors missed beacons - actively probes the AP in case of missed beacons In case of poor connection conditions it expects the function ieee80211_connection_loss() to be called by driver. However, some devices implement connection monitor offload excluding active AP probing. To allow them to call ieee80211_beacon_loss() cleanly, remove the warning there and thus allow them to use mac80211 for the AP probing even if connection monitor offload is supported. Signed-off-by: Alexander Bondar Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 43023f0db68..c7860d0450d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2215,7 +2215,6 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif) trace_api_beacon_loss(sdata); - WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); sdata->u.mgd.connection_loss = false; ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } -- cgit v1.2.3-18-g5258 From fbd647b17689d584748bad62395cd1161d42d37c Mon Sep 17 00:00:00 2001 From: Karl Beldan Date: Mon, 15 Apr 2013 18:28:21 +0200 Subject: mac80211: fix rate control tx handler for VHT rates Handle VHT rates like HT ones, otherwise we easily trigger the pre-HT rates WARN_ON(rc_rate->idx >= sband->n_bitrates) which will set rc_rate->idx to -1. Signed-off-by: Karl Beldan Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c93483fd477..bb82c873f77 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -742,16 +742,18 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) } for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + struct ieee80211_tx_rate *rc_rate = &info->control.rates[i]; + /* * make sure there's no valid rate following * an invalid one, just in case drivers don't * take the API seriously to stop at -1. */ if (inval) { - info->control.rates[i].idx = -1; + rc_rate->idx = -1; continue; } - if (info->control.rates[i].idx < 0) { + if (rc_rate->idx < 0) { inval = true; continue; } @@ -760,36 +762,37 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) * For now assume MCS is already set up correctly, this * needs to be fixed. */ - if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) { - WARN_ON(info->control.rates[i].idx > 76); + if (rc_rate->flags & IEEE80211_TX_RC_MCS) { + WARN_ON(rc_rate->idx > 76); + continue; + } + + if (rc_rate->flags & IEEE80211_TX_RC_VHT_MCS) { + WARN_ON(ieee80211_rate_get_vht_mcs(rc_rate) > 9); continue; } /* set up RTS protection if desired */ if (rts) - info->control.rates[i].flags |= - IEEE80211_TX_RC_USE_RTS_CTS; + rc_rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; /* RC is busted */ - if (WARN_ON_ONCE(info->control.rates[i].idx >= - sband->n_bitrates)) { - info->control.rates[i].idx = -1; + if (WARN_ON_ONCE(rc_rate->idx >= sband->n_bitrates)) { + rc_rate->idx = -1; continue; } - rate = &sband->bitrates[info->control.rates[i].idx]; + rate = &sband->bitrates[rc_rate->idx]; /* set up short preamble */ if (short_preamble && rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) - info->control.rates[i].flags |= - IEEE80211_TX_RC_USE_SHORT_PREAMBLE; + rc_rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; /* set up G protection */ if (!rts && tx->sdata->vif.bss_conf.use_cts_prot && rate->flags & IEEE80211_RATE_ERP_G) - info->control.rates[i].flags |= - IEEE80211_TX_RC_USE_CTS_PROTECT; + rc_rate->flags |= IEEE80211_TX_RC_USE_CTS_PROTECT; } return TX_CONTINUE; -- cgit v1.2.3-18-g5258 From fffa4b1c170a3cabc58671495b0ae89ded007199 Mon Sep 17 00:00:00 2001 From: Karl Beldan Date: Mon, 15 Apr 2013 19:04:06 +0200 Subject: mac80211: adjust initial chandefs assignments in ieee80211_register_hw I noticed that monitor interfaces by default would start on 5GHz while STA/AP ones would start 2GHZ - It stems from the fact that ieee80211_register_hw unnecessarily adjusts the local->monitor_chandef for each band. This avoids this and while at it uses a si