forked from Minki/linux
mac80211: split off channel switch parsing function
The channel switch parsing function can be re-used for the IBSS code, put the common part into an extra function. Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de> Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de> [also move/rename chandef_downgrade] Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
774f073461
commit
e6b7cde4d3
@ -1480,6 +1480,29 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
|
|||||||
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_mgmt *mgmt,
|
struct ieee80211_mgmt *mgmt,
|
||||||
size_t len);
|
size_t len);
|
||||||
|
/**
|
||||||
|
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
|
||||||
|
* @sdata: the sdata of the interface which has received the frame
|
||||||
|
* @elems: parsed 802.11 elements received with the frame
|
||||||
|
* @beacon: indicates if the frame was a beacon or probe response
|
||||||
|
* @current_band: indicates the current band
|
||||||
|
* @sta_flags: contains information about own capabilities and restrictions
|
||||||
|
* to decide which channel switch announcements can be accepted. Only the
|
||||||
|
* following subset of &enum ieee80211_sta_flags are evaluated:
|
||||||
|
* %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
|
||||||
|
* %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
|
||||||
|
* %IEEE80211_STA_DISABLE_160MHZ.
|
||||||
|
* @count: to be filled with the counter until the switch (on success only)
|
||||||
|
* @bssid: the currently connected bssid (for reporting)
|
||||||
|
* @mode: to be filled with CSA mode (on success only)
|
||||||
|
* @new_chandef: to be filled with destination chandef (on success only)
|
||||||
|
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
|
||||||
|
*/
|
||||||
|
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee802_11_elems *elems, bool beacon,
|
||||||
|
enum ieee80211_band current_band,
|
||||||
|
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
|
||||||
|
struct cfg80211_chan_def *new_chandef);
|
||||||
|
|
||||||
/* Suspend/resume and hw reconfiguration */
|
/* Suspend/resume and hw reconfiguration */
|
||||||
int ieee80211_reconfig(struct ieee80211_local *local);
|
int ieee80211_reconfig(struct ieee80211_local *local);
|
||||||
@ -1653,6 +1676,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
|
|||||||
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
|
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
|
||||||
const struct ieee80211_ht_operation *ht_oper,
|
const struct ieee80211_ht_operation *ht_oper,
|
||||||
struct cfg80211_chan_def *chandef);
|
struct cfg80211_chan_def *chandef);
|
||||||
|
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
|
||||||
|
|
||||||
int __must_check
|
int __must_check
|
||||||
ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
|
ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
|
||||||
|
@ -145,66 +145,6 @@ static int ecw2cw(int ecw)
|
|||||||
return (1 << ecw) - 1;
|
return (1 << ecw) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 chandef_downgrade(struct cfg80211_chan_def *c)
|
|
||||||
{
|
|
||||||
u32 ret;
|
|
||||||
int tmp;
|
|
||||||
|
|
||||||
switch (c->width) {
|
|
||||||
case NL80211_CHAN_WIDTH_20:
|
|
||||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
|
||||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
||||||
break;
|
|
||||||
case NL80211_CHAN_WIDTH_40:
|
|
||||||
c->width = NL80211_CHAN_WIDTH_20;
|
|
||||||
c->center_freq1 = c->chan->center_freq;
|
|
||||||
ret = IEEE80211_STA_DISABLE_40MHZ |
|
|
||||||
IEEE80211_STA_DISABLE_VHT;
|
|
||||||
break;
|
|
||||||
case NL80211_CHAN_WIDTH_80:
|
|
||||||
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
|
||||||
/* n_P40 */
|
|
||||||
tmp /= 2;
|
|
||||||
/* freq_P40 */
|
|
||||||
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
|
|
||||||
c->width = NL80211_CHAN_WIDTH_40;
|
|
||||||
ret = IEEE80211_STA_DISABLE_VHT;
|
|
||||||
break;
|
|
||||||
case NL80211_CHAN_WIDTH_80P80:
|
|
||||||
c->center_freq2 = 0;
|
|
||||||
c->width = NL80211_CHAN_WIDTH_80;
|
|
||||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
|
||||||
IEEE80211_STA_DISABLE_160MHZ;
|
|
||||||
break;
|
|
||||||
case NL80211_CHAN_WIDTH_160:
|
|
||||||
/* n_P20 */
|
|
||||||
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
|
||||||
/* n_P80 */
|
|
||||||
tmp /= 4;
|
|
||||||
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
|
|
||||||
c->width = NL80211_CHAN_WIDTH_80;
|
|
||||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
|
||||||
IEEE80211_STA_DISABLE_160MHZ;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
|
||||||
WARN_ON_ONCE(1);
|
|
||||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
|
||||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
||||||
break;
|
|
||||||
case NL80211_CHAN_WIDTH_5:
|
|
||||||
case NL80211_CHAN_WIDTH_10:
|
|
||||||
WARN_ON_ONCE(1);
|
|
||||||
/* keep c->width */
|
|
||||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32
|
static u32
|
||||||
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_supported_band *sband,
|
struct ieee80211_supported_band *sband,
|
||||||
@ -352,7 +292,7 @@ out:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret |= chandef_downgrade(chandef);
|
ret |= ieee80211_chandef_downgrade(chandef);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chandef->width != vht_chandef.width && !tracking)
|
if (chandef->width != vht_chandef.width && !tracking)
|
||||||
@ -406,13 +346,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
|
|||||||
*/
|
*/
|
||||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||||
chandef.width == NL80211_CHAN_WIDTH_80P80)
|
chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||||
flags |= chandef_downgrade(&chandef);
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||||
chandef.width == NL80211_CHAN_WIDTH_160)
|
chandef.width == NL80211_CHAN_WIDTH_160)
|
||||||
flags |= chandef_downgrade(&chandef);
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
|
||||||
chandef.width > NL80211_CHAN_WIDTH_20)
|
chandef.width > NL80211_CHAN_WIDTH_20)
|
||||||
flags |= chandef_downgrade(&chandef);
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||||
|
|
||||||
if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
|
if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
|
||||||
return 0;
|
return 0;
|
||||||
@ -999,20 +939,12 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||||
struct cfg80211_bss *cbss = ifmgd->associated;
|
struct cfg80211_bss *cbss = ifmgd->associated;
|
||||||
struct ieee80211_bss *bss;
|
|
||||||
struct ieee80211_chanctx *chanctx;
|
struct ieee80211_chanctx *chanctx;
|
||||||
enum ieee80211_band new_band;
|
enum ieee80211_band current_band;
|
||||||
int new_freq;
|
|
||||||
u8 new_chan_no;
|
|
||||||
u8 count;
|
u8 count;
|
||||||
u8 mode;
|
u8 mode;
|
||||||
struct ieee80211_channel *new_chan;
|
|
||||||
struct cfg80211_chan_def new_chandef = {};
|
struct cfg80211_chan_def new_chandef = {};
|
||||||
struct cfg80211_chan_def new_vht_chandef = {};
|
int res;
|
||||||
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
|
|
||||||
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
|
|
||||||
const struct ieee80211_ht_operation *ht_oper;
|
|
||||||
int secondary_channel_offset = -1;
|
|
||||||
|
|
||||||
sdata_assert_lock(sdata);
|
sdata_assert_lock(sdata);
|
||||||
|
|
||||||
@ -1026,162 +958,23 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||||||
if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
|
if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sec_chan_offs = elems->sec_chan_offs;
|
current_band = cbss->channel->band;
|
||||||
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
|
res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
|
||||||
ht_oper = elems->ht_operation;
|
ifmgd->flags,
|
||||||
|
ifmgd->associated->bssid, &count,
|
||||||
if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
|
&mode, &new_chandef);
|
||||||
IEEE80211_STA_DISABLE_40MHZ)) {
|
if (res < 0)
|
||||||
sec_chan_offs = NULL;
|
|
||||||
wide_bw_chansw_ie = NULL;
|
|
||||||
/* only used for bandwidth here */
|
|
||||||
ht_oper = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
|
|
||||||
wide_bw_chansw_ie = NULL;
|
|
||||||
|
|
||||||
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(new_chan_no, new_band);
|
|
||||||
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);
|
|
||||||
ieee80211_queue_work(&local->hw,
|
ieee80211_queue_work(&local->hw,
|
||||||
&ifmgd->csa_connection_drop_work);
|
&ifmgd->csa_connection_drop_work);
|
||||||
|
if (res)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (!beacon && sec_chan_offs) {
|
|
||||||
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
|
|
||||||
} else if (beacon && ht_oper) {
|
|
||||||
secondary_channel_offset =
|
|
||||||
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
|
|
||||||
} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
|
|
||||||
/*
|
|
||||||
* If it's not a beacon, HT is enabled and the IE not present,
|
|
||||||
* it's 20 MHz, 802.11-2012 8.5.2.6:
|
|
||||||
* This element [the Secondary Channel Offset Element] is
|
|
||||||
* present when switching to a 40 MHz channel. It may be
|
|
||||||
* present when switching to a 20 MHz channel (in which
|
|
||||||
* case the secondary channel offset is set to SCN).
|
|
||||||
*/
|
|
||||||
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_chan,
|
|
||||||
NL80211_CHAN_HT20);
|
|
||||||
break;
|
|
||||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
|
||||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
|
||||||
NL80211_CHAN_HT40PLUS);
|
|
||||||
break;
|
|
||||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
|
||||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
|
||||||
NL80211_CHAN_HT40MINUS);
|
|
||||||
break;
|
|
||||||
case -1:
|
|
||||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
|
||||||
NL80211_CHAN_NO_HT);
|
|
||||||
/* keep width for 5/10 MHz channels */
|
|
||||||
switch (sdata->vif.bss_conf.chandef.width) {
|
|
||||||
case NL80211_CHAN_WIDTH_5:
|
|
||||||
case NL80211_CHAN_WIDTH_10:
|
|
||||||
new_chandef.width = sdata->vif.bss_conf.chandef.width;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
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,
|
if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
|
||||||
IEEE80211_CHAN_DISABLED)) {
|
IEEE80211_CHAN_DISABLED)) {
|
||||||
sdata_info(sdata,
|
sdata_info(sdata,
|
||||||
"AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
"AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||||
ifmgd->associated->bssid, new_freq,
|
ifmgd->associated->bssid,
|
||||||
|
new_chandef.chan->center_freq,
|
||||||
new_chandef.width, new_chandef.center_freq1,
|
new_chandef.width, new_chandef.center_freq1,
|
||||||
new_chandef.center_freq2);
|
new_chandef.center_freq2);
|
||||||
ieee80211_queue_work(&local->hw,
|
ieee80211_queue_work(&local->hw,
|
||||||
@ -3856,7 +3649,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
|
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
|
||||||
ifmgd->flags |= chandef_downgrade(&chandef);
|
ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
|
||||||
ret = ieee80211_vif_use_channel(sdata, &chandef,
|
ret = ieee80211_vif_use_channel(sdata, &chandef,
|
||||||
IEEE80211_CHANCTX_SHARED);
|
IEEE80211_CHANCTX_SHARED);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,168 @@
|
|||||||
#include "sta_info.h"
|
#include "sta_info.h"
|
||||||
#include "wme.h"
|
#include "wme.h"
|
||||||
|
|
||||||
|
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee802_11_elems *elems, bool beacon,
|
||||||
|
enum ieee80211_band current_band,
|
||||||
|
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
|
||||||
|
struct cfg80211_chan_def *new_chandef)
|
||||||
|
{
|
||||||
|
enum ieee80211_band new_band;
|
||||||
|
int new_freq;
|
||||||
|
u8 new_chan_no;
|
||||||
|
struct ieee80211_channel *new_chan;
|
||||||
|
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;
|
||||||
|
const struct ieee80211_ht_operation *ht_oper;
|
||||||
|
int secondary_channel_offset = -1;
|
||||||
|
|
||||||
|
sec_chan_offs = elems->sec_chan_offs;
|
||||||
|
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
|
||||||
|
ht_oper = elems->ht_operation;
|
||||||
|
|
||||||
|
if (sta_flags & (IEEE80211_STA_DISABLE_HT |
|
||||||
|
IEEE80211_STA_DISABLE_40MHZ)) {
|
||||||
|
sec_chan_offs = NULL;
|
||||||
|
wide_bw_chansw_ie = NULL;
|
||||||
|
/* only used for bandwidth here */
|
||||||
|
ht_oper = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sta_flags & IEEE80211_STA_DISABLE_VHT)
|
||||||
|
wide_bw_chansw_ie = NULL;
|
||||||
|
|
||||||
|
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);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
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 = current_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 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
|
||||||
|
new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
|
||||||
|
if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
|
||||||
|
sdata_info(sdata,
|
||||||
|
"BSS %pM switches to unsupported channel (%d MHz), disconnecting\n",
|
||||||
|
bssid, new_freq);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!beacon && sec_chan_offs) {
|
||||||
|
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
|
||||||
|
} else if (beacon && ht_oper) {
|
||||||
|
secondary_channel_offset =
|
||||||
|
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
|
||||||
|
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
|
||||||
|
/* If it's not a beacon, HT is enabled and the IE not present,
|
||||||
|
* it's 20 MHz, 802.11-2012 8.5.2.6:
|
||||||
|
* This element [the Secondary Channel Offset Element] is
|
||||||
|
* present when switching to a 40 MHz channel. It may be
|
||||||
|
* present when switching to a 20 MHz channel (in which
|
||||||
|
* case the secondary channel offset is set to SCN).
|
||||||
|
*/
|
||||||
|
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_chan,
|
||||||
|
NL80211_CHAN_HT20);
|
||||||
|
break;
|
||||||
|
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||||
|
cfg80211_chandef_create(new_chandef, new_chan,
|
||||||
|
NL80211_CHAN_HT40PLUS);
|
||||||
|
break;
|
||||||
|
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||||
|
cfg80211_chandef_create(new_chandef, new_chan,
|
||||||
|
NL80211_CHAN_HT40MINUS);
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
cfg80211_chandef_create(new_chandef, new_chan,
|
||||||
|
NL80211_CHAN_NO_HT);
|
||||||
|
/* keep width for 5/10 MHz channels */
|
||||||
|
switch (sdata->vif.bss_conf.chandef.width) {
|
||||||
|
case NL80211_CHAN_WIDTH_5:
|
||||||
|
case NL80211_CHAN_WIDTH_10:
|
||||||
|
new_chandef->width = sdata->vif.bss_conf.chandef.width;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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 (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||||
|
new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||||
|
ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||||
|
if (sta_flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||||
|
new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
|
||||||
|
ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||||
|
if (sta_flags & IEEE80211_STA_DISABLE_40MHZ &&
|
||||||
|
new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
|
||||||
|
ieee80211_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,
|
||||||
|
"BSS %pM: CSA has inconsistent channel data, disconnecting\n",
|
||||||
|
bssid);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
*new_chandef = new_vht_chandef;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
|
static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_msrment_ie *request_ie,
|
struct ieee80211_msrment_ie *request_ie,
|
||||||
const u8 *da, const u8 *bssid,
|
const u8 *da, const u8 *bssid,
|
||||||
|
@ -2292,3 +2292,63 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw)
|
|||||||
ieee80211_queue_work(hw, &local->radar_detected_work);
|
ieee80211_queue_work(hw, &local->radar_detected_work);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_radar_detected);
|
EXPORT_SYMBOL(ieee80211_radar_detected);
|
||||||
|
|
||||||
|
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
|
||||||
|
{
|
||||||
|
u32 ret;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
switch (c->width) {
|
||||||
|
case NL80211_CHAN_WIDTH_20:
|
||||||
|
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||||
|
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||||
|
break;
|
||||||
|
case NL80211_CHAN_WIDTH_40:
|
||||||
|
c->width = NL80211_CHAN_WIDTH_20;
|
||||||
|
c->center_freq1 = c->chan->center_freq;
|
||||||
|
ret = IEEE80211_STA_DISABLE_40MHZ |
|
||||||
|
IEEE80211_STA_DISABLE_VHT;
|
||||||
|
break;
|
||||||
|
case NL80211_CHAN_WIDTH_80:
|
||||||
|
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
||||||
|
/* n_P40 */
|
||||||
|
tmp /= 2;
|
||||||
|
/* freq_P40 */
|
||||||
|
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
|
||||||
|
c->width = NL80211_CHAN_WIDTH_40;
|
||||||
|
ret = IEEE80211_STA_DISABLE_VHT;
|
||||||
|
break;
|
||||||
|
case NL80211_CHAN_WIDTH_80P80:
|
||||||
|
c->center_freq2 = 0;
|
||||||
|
c->width = NL80211_CHAN_WIDTH_80;
|
||||||
|
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||||
|
IEEE80211_STA_DISABLE_160MHZ;
|
||||||
|
break;
|
||||||
|
case NL80211_CHAN_WIDTH_160:
|
||||||
|
/* n_P20 */
|
||||||
|
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
||||||
|
/* n_P80 */
|
||||||
|
tmp /= 4;
|
||||||
|
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
|
||||||
|
c->width = NL80211_CHAN_WIDTH_80;
|
||||||
|
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||||
|
IEEE80211_STA_DISABLE_160MHZ;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||||
|
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||||
|
break;
|
||||||
|
case NL80211_CHAN_WIDTH_5:
|
||||||
|
case NL80211_CHAN_WIDTH_10:
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
/* keep c->width */
|
||||||
|
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user