diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/ieee80211_i.h | 3 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 71 | ||||
-rw-r--r-- | net/mac80211/trace.h | 8 | ||||
-rw-r--r-- | net/mac80211/util.c | 8 |
4 files changed, 70 insertions, 20 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 10c3180b165e..8f240c0ec304 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 bc6f87edc624..bd581a80e4b7 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 8286dcef228b..c215fafd7a2f 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 e4a6d559372d..155056c90edf 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; |