|
|
@@ -114,6 +114,9 @@ enum rx_mgmt_action {
|
|
|
|
|
|
/* caller must call cfg80211_send_assoc_timeout() */
|
|
|
RX_MGMT_CFG80211_ASSOC_TIMEOUT,
|
|
|
+
|
|
|
+ /* used when a processed beacon causes a deauth */
|
|
|
+ RX_MGMT_CFG80211_TX_DEAUTH,
|
|
|
};
|
|
|
|
|
|
/* utils */
|
|
|
@@ -174,79 +177,331 @@ static int ecw2cw(int ecw)
|
|
|
return (1 << ecw) - 1;
|
|
|
}
|
|
|
|
|
|
-static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
|
|
|
- struct ieee80211_ht_operation *ht_oper,
|
|
|
- const u8 *bssid, bool reconfig)
|
|
|
+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;
|
|
|
+ }
|
|
|
+
|
|
|
+ WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static u32
|
|
|
+ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct ieee80211_supported_band *sband,
|
|
|
+ struct ieee80211_channel *channel,
|
|
|
+ const struct ieee80211_ht_operation *ht_oper,
|
|
|
+ const struct ieee80211_vht_operation *vht_oper,
|
|
|
+ struct cfg80211_chan_def *chandef, bool verbose)
|
|
|
+{
|
|
|
+ struct cfg80211_chan_def vht_chandef;
|
|
|
+ u32 ht_cfreq, ret;
|
|
|
+
|
|
|
+ chandef->chan = channel;
|
|
|
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
|
|
+ chandef->center_freq1 = channel->center_freq;
|
|
|
+ chandef->center_freq2 = 0;
|
|
|
+
|
|
|
+ if (!ht_oper || !sband->ht_cap.ht_supported) {
|
|
|
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ chandef->width = NL80211_CHAN_WIDTH_20;
|
|
|
+
|
|
|
+ ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
|
|
|
+ channel->band);
|
|
|
+ /* check that channel matches the right operating channel */
|
|
|
+ if (channel->center_freq != ht_cfreq) {
|
|
|
+ /*
|
|
|
+ * It's possible that some APs are confused here;
|
|
|
+ * Netgear WNDR3700 sometimes reports 4 higher than
|
|
|
+ * the actual channel in association responses, but
|
|
|
+ * since we look at probe response/beacon data here
|
|
|
+ * it should be OK.
|
|
|
+ */
|
|
|
+ if (verbose)
|
|
|
+ sdata_info(sdata,
|
|
|
+ "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
|
|
|
+ channel->center_freq, ht_cfreq,
|
|
|
+ ht_oper->primary_chan, channel->band);
|
|
|
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* check 40 MHz support, if we have it */
|
|
|
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
|
|
|
+ switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
|
|
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
|
|
+ chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
+ chandef->center_freq1 += 10;
|
|
|
+ break;
|
|
|
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
|
|
+ chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
+ chandef->center_freq1 -= 10;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /* 40 MHz (and 80 MHz) must be supported for VHT */
|
|
|
+ ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!vht_oper || !sband->vht_cap.vht_supported) {
|
|
|
+ ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ vht_chandef.chan = channel;
|
|
|
+ vht_chandef.center_freq1 =
|
|
|
+ ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
|
|
|
+ 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;
|
|
|
+ break;
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
|
|
+ vht_chandef.width = NL80211_CHAN_WIDTH_80;
|
|
|
+ break;
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
|
|
+ vht_chandef.width = NL80211_CHAN_WIDTH_160;
|
|
|
+ break;
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
|
|
+ vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (verbose)
|
|
|
+ sdata_info(sdata,
|
|
|
+ "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
|
|
|
+ vht_oper->chan_width);
|
|
|
+ ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!cfg80211_chandef_valid(&vht_chandef)) {
|
|
|
+ if (verbose)
|
|
|
+ sdata_info(sdata,
|
|
|
+ "AP VHT information is invalid, disable VHT\n");
|
|
|
+ ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
|
|
|
+ ret = 0;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
|
|
|
+ if (verbose)
|
|
|
+ sdata_info(sdata,
|
|
|
+ "AP VHT information doesn't match HT, disable VHT\n");
|
|
|
+ ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ *chandef = vht_chandef;
|
|
|
+
|
|
|
+ ret = 0;
|
|
|
+
|
|
|
+out:
|
|
|
+ /* don't print the message below for VHT mismatch if VHT is disabled */
|
|
|
+ if (ret & IEEE80211_STA_DISABLE_VHT)
|
|
|
+ vht_chandef = *chandef;
|
|
|
+
|
|
|
+ while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
|
|
+ IEEE80211_CHAN_DISABLED)) {
|
|
|
+ if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
|
|
|
+ ret = IEEE80211_STA_DISABLE_HT |
|
|
|
+ IEEE80211_STA_DISABLE_VHT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret |= chandef_downgrade(chandef);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (chandef->width != vht_chandef.width && verbose)
|
|
|
+ sdata_info(sdata,
|
|
|
+ "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n");
|
|
|
+
|
|
|
+ WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct sta_info *sta,
|
|
|
+ const struct ieee80211_ht_operation *ht_oper,
|
|
|
+ const struct ieee80211_vht_operation *vht_oper,
|
|
|
+ const u8 *bssid, u32 *changed)
|
|
|
{
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
- struct ieee80211_chanctx_conf *chanctx_conf;
|
|
|
struct ieee80211_channel *chan;
|
|
|
- struct sta_info *sta;
|
|
|
- u32 changed = 0;
|
|
|
+ struct cfg80211_chan_def chandef;
|
|
|
u16 ht_opmode;
|
|
|
- bool disable_40 = false;
|
|
|
+ u32 flags;
|
|
|
+ enum ieee80211_sta_rx_bandwidth new_sta_bw;
|
|
|
+ int ret;
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
- if (WARN_ON(!chanctx_conf)) {
|
|
|
- rcu_read_unlock();
|
|
|
+ /* if HT was/is disabled, don't track any bandwidth changes */
|
|
|
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || !ht_oper)
|
|
|
return 0;
|
|
|
- }
|
|
|
- chan = chanctx_conf->def.chan;
|
|
|
- rcu_read_unlock();
|
|
|
+
|
|
|
+ /* don't check VHT if we associated as non-VHT station */
|
|
|
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
|
|
|
+ vht_oper = NULL;
|
|
|
+
|
|
|
+ if (WARN_ON_ONCE(!sta))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ chan = sdata->vif.bss_conf.chandef.chan;
|
|
|
sband = local->hw.wiphy->bands[chan->band];
|
|
|
|
|
|
- switch (sdata->vif.bss_conf.chandef.width) {
|
|
|
+ /* calculate new channel (type) based on HT/VHT operation IEs */
|
|
|
+ flags = ieee80211_determine_chantype(sdata, sband, chan, ht_oper,
|
|
|
+ vht_oper, &chandef, false);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Downgrade the new channel if we associated with restricted
|
|
|
+ * capabilities. For example, if we associated as a 20 MHz STA
|
|
|
+ * to a 40 MHz AP (due to regulatory, capabilities or config
|
|
|
+ * reasons) then switching to a 40 MHz channel now won't do us
|
|
|
+ * any good -- we couldn't use it with the AP.
|
|
|
+ */
|
|
|
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
|
|
+ chandef.width == NL80211_CHAN_WIDTH_80P80)
|
|
|
+ flags |= chandef_downgrade(&chandef);
|
|
|
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
|
|
|
+ chandef.width == NL80211_CHAN_WIDTH_160)
|
|
|
+ flags |= chandef_downgrade(&chandef);
|
|
|
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
|
|
|
+ chandef.width > NL80211_CHAN_WIDTH_20)
|
|
|
+ flags |= chandef_downgrade(&chandef);
|
|
|
+
|
|
|
+ if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ sdata_info(sdata,
|
|
|
+ "AP %pM changed bandwidth, new config is %d MHz, width %d (%d/%d MHz)\n",
|
|
|
+ ifmgd->bssid, chandef.chan->center_freq, chandef.width,
|
|
|
+ chandef.center_freq1, chandef.center_freq2);
|
|
|
+
|
|
|
+ if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
|
|
|
+ IEEE80211_STA_DISABLE_VHT |
|
|
|
+ IEEE80211_STA_DISABLE_40MHZ |
|
|
|
+ IEEE80211_STA_DISABLE_80P80MHZ |
|
|
|
+ IEEE80211_STA_DISABLE_160MHZ)) ||
|
|
|
+ !cfg80211_chandef_valid(&chandef)) {
|
|
|
+ sdata_info(sdata,
|
|
|
+ "AP %pM changed bandwidth in a way we can't support - disconnect\n",
|
|
|
+ ifmgd->bssid);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (chandef.width) {
|
|
|
+ case NL80211_CHAN_WIDTH_20_NOHT:
|
|
|
+ case NL80211_CHAN_WIDTH_20:
|
|
|
+ new_sta_bw = IEEE80211_STA_RX_BW_20;
|
|
|
+ break;
|
|
|
case NL80211_CHAN_WIDTH_40:
|
|
|
- if (sdata->vif.bss_conf.chandef.chan->center_freq >
|
|
|
- sdata->vif.bss_conf.chandef.center_freq1 &&
|
|
|
- chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
|
|
|
- disable_40 = true;
|
|
|
- if (sdata->vif.bss_conf.chandef.chan->center_freq <
|
|
|
- sdata->vif.bss_conf.chandef.center_freq1 &&
|
|
|
- chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
|
|
|
- disable_40 = true;
|
|
|
+ new_sta_bw = IEEE80211_STA_RX_BW_40;
|
|
|
break;
|
|
|
- default:
|
|
|
+ case NL80211_CHAN_WIDTH_80:
|
|
|
+ new_sta_bw = IEEE80211_STA_RX_BW_80;
|
|
|
break;
|
|
|
+ case NL80211_CHAN_WIDTH_80P80:
|
|
|
+ case NL80211_CHAN_WIDTH_160:
|
|
|
+ new_sta_bw = IEEE80211_STA_RX_BW_160;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- /* This can change during the lifetime of the BSS */
|
|
|
- if (!(ht_oper->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
|
|
|
- disable_40 = true;
|
|
|
-
|
|
|
- mutex_lock(&local->sta_mtx);
|
|
|
- sta = sta_info_get(sdata, bssid);
|
|
|
-
|
|
|
- WARN_ON_ONCE(!sta);
|
|
|
+ if (new_sta_bw > sta->cur_max_bandwidth)
|
|
|
+ new_sta_bw = sta->cur_max_bandwidth;
|
|
|
|
|
|
- if (sta && !sta->supports_40mhz)
|
|
|
- disable_40 = true;
|
|
|
-
|
|
|
- if (sta && (!reconfig ||
|
|
|
- (disable_40 != !(sta->sta.ht_cap.cap &
|
|
|
- IEEE80211_HT_CAP_SUP_WIDTH_20_40)))) {
|
|
|
+ if (new_sta_bw < sta->sta.bandwidth) {
|
|
|
+ sta->sta.bandwidth = new_sta_bw;
|
|
|
+ rate_control_rate_update(local, sband, sta,
|
|
|
+ IEEE80211_RC_BW_CHANGED);
|
|
|
+ }
|
|
|
|
|
|
- if (disable_40)
|
|
|
- sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
- else
|
|
|
- sta->sta.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
+ ret = ieee80211_vif_change_bandwidth(sdata, &chandef, changed);
|
|
|
+ if (ret) {
|
|
|
+ sdata_info(sdata,
|
|
|
+ "AP %pM changed bandwidth to incompatible one - disconnect\n",
|
|
|
+ ifmgd->bssid);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
+ if (new_sta_bw > sta->sta.bandwidth) {
|
|
|
+ sta->sta.bandwidth = new_sta_bw;
|
|
|
rate_control_rate_update(local, sband, sta,
|
|
|
IEEE80211_RC_BW_CHANGED);
|
|
|
}
|
|
|
- mutex_unlock(&local->sta_mtx);
|
|
|
|
|
|
ht_opmode = le16_to_cpu(ht_oper->operation_mode);
|
|
|
|
|
|
/* if bss configuration changed store the new one */
|
|
|
- if (!reconfig || (sdata->vif.bss_conf.ht_operation_mode != ht_opmode)) {
|
|
|
- changed |= BSS_CHANGED_HT;
|
|
|
+ if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
|
|
|
+ *changed |= BSS_CHANGED_HT;
|
|
|
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
|
|
|
}
|
|
|
|
|
|
- return changed;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/* frame sending functions */
|
|
|
@@ -685,7 +940,8 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
|
|
if (powersave)
|
|
|
nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
|
|
|
|
|
- IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
|
|
|
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
|
|
|
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
|
|
IEEE80211_STA_CONNECTION_POLL))
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
|
|
|
@@ -789,10 +1045,10 @@ static void ieee80211_chswitch_timer(unsigned long data)
|
|
|
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
|
|
|
}
|
|
|
|
|
|
-void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|
|
- struct ieee80211_channel_sw_ie *sw_elem,
|
|
|
- struct ieee80211_bss *bss,
|
|
|
- u64 timestamp)
|
|
|
+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)
|
|
|
{
|
|
|
struct cfg80211_bss *cbss =
|
|
|
container_of((void *)bss, struct cfg80211_bss, priv);
|
|
|
@@ -951,39 +1207,6 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
|
|
|
-{
|
|
|
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
- struct ieee80211_local *local = sdata->local;
|
|
|
- struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
-
|
|
|
- WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION ||
|
|
|
- !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) ||
|
|
|
- (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS));
|
|
|
-
|
|
|
- local->disable_dynamic_ps = false;
|
|
|
- conf->dynamic_ps_timeout = local->dynamic_ps_user_timeout;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ieee80211_enable_dyn_ps);
|
|
|
-
|
|
|
-void ieee80211_disable_dyn_ps(struct ieee80211_vif *vif)
|
|
|
-{
|
|
|
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
- struct ieee80211_local *local = sdata->local;
|
|
|
- struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
-
|
|
|
- WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION ||
|
|
|
- !(local->hw.flags & IEEE80211_HW_SUPPORTS_PS) ||
|
|
|
- (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS));
|
|
|
-
|
|
|
- local->disable_dynamic_ps = true;
|
|
|
- conf->dynamic_ps_timeout = 0;
|
|
|
- del_timer_sync(&local->dynamic_ps_timer);
|
|
|
- ieee80211_queue_work(&local->hw,
|
|
|
- &local->dynamic_ps_enable_work);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(ieee80211_disable_dyn_ps);
|
|
|
-
|
|
|
/* powersave */
|
|
|
static void ieee80211_enable_ps(struct ieee80211_local *local,
|
|
|
struct ieee80211_sub_if_data *sdata)
|
|
|
@@ -1086,7 +1309,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
|
|
|
}
|
|
|
|
|
|
if (count == 1 && ieee80211_powersave_allowed(found)) {
|
|
|
- struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
s32 beaconint_us;
|
|
|
|
|
|
if (latency < 0)
|
|
|
@@ -1110,10 +1332,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
|
|
|
else
|
|
|
timeout = 100;
|
|
|
}
|
|
|
- local->dynamic_ps_user_timeout = timeout;
|
|
|
- if (!local->disable_dynamic_ps)
|
|
|
- conf->dynamic_ps_timeout =
|
|
|
- local->dynamic_ps_user_timeout;
|
|
|
+ local->hw.conf.dynamic_ps_timeout = timeout;
|
|
|
|
|
|
if (beaconint_us > latency) {
|
|
|
local->ps_sdata = NULL;
|
|
|
@@ -1183,8 +1402,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS)
|
|
|
return;
|
|
|
|
|
|
- if (!local->disable_dynamic_ps &&
|
|
|
- local->hw.conf.dynamic_ps_timeout > 0) {
|
|
|
+ if (local->hw.conf.dynamic_ps_timeout > 0) {
|
|
|
/* don't enter PS if TX frames are pending */
|
|
|
if (drv_tx_frames_pending(local)) {
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
@@ -1249,16 +1467,30 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
|
|
|
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
|
|
|
}
|
|
|
|
|
|
+void ieee80211_dfs_cac_timer_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct delayed_work *delayed_work =
|
|
|
+ container_of(work, struct delayed_work, work);
|
|
|
+ struct ieee80211_sub_if_data *sdata =
|
|
|
+ container_of(delayed_work, struct ieee80211_sub_if_data,
|
|
|
+ dfs_cac_timer_work);
|
|
|
+
|
|
|
+ ieee80211_vif_release_channel(sdata);
|
|
|
+
|
|
|
+ cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
|
|
|
+}
|
|
|
+
|
|
|
/* MLME */
|
|
|
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
|
|
struct ieee80211_sub_if_data *sdata,
|
|
|
- u8 *wmm_param, size_t wmm_param_len)
|
|
|
+ const u8 *wmm_param, size_t wmm_param_len)
|
|
|
{
|
|
|
struct ieee80211_tx_queue_params params;
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
size_t left;
|
|
|
int count;
|
|
|
- u8 *pos, uapsd_queues = 0;
|
|
|
+ const u8 *pos;
|
|
|
+ u8 uapsd_queues = 0;
|
|
|
|
|
|
if (!local->ops->conf_tx)
|
|
|
return false;
|
|
|
@@ -1661,17 +1893,18 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
|
|
if (!ieee80211_is_data(hdr->frame_control))
|
|
|
return;
|
|
|
|
|
|
- if (ack)
|
|
|
- ieee80211_sta_reset_conn_monitor(sdata);
|
|
|
-
|
|
|
if (ieee80211_is_nullfunc(hdr->frame_control) &&
|
|
|
sdata->u.mgd.probe_send_count > 0) {
|
|
|
if (ack)
|
|
|
- sdata->u.mgd.probe_send_count = 0;
|
|
|
+ ieee80211_sta_reset_conn_monitor(sdata);
|
|
|
else
|
|
|
sdata->u.mgd.nullfunc_failed = true;
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ if (ack)
|
|
|
+ ieee80211_sta_reset_conn_monitor(sdata);
|
|
|
}
|
|
|
|
|
|
static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|
|
@@ -1746,7 +1979,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
if (beacon)
|
|
|
mlme_dbg_ratelimited(sdata,
|
|
|
- "detected beacon loss from AP - sending probe request\n");
|
|
|
+ "detected beacon loss from AP - probing\n");
|
|
|
|
|
|
ieee80211_cqm_rssi_notify(&sdata->vif,
|
|
|
NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL);
|
|
|
@@ -1830,7 +2063,6 @@ EXPORT_SYMBOL(ieee80211_ap_probereq_get);
|
|
|
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|
|
{
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
- struct ieee80211_local *local = sdata->local;
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
|
|
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
@@ -1843,6 +2075,8 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
|
|
true, frame_buf);
|
|
|
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
|
|
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
|
|
+ IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
|
/*
|
|
|
@@ -1850,10 +2084,6 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
|
|
* but that's not a problem.
|
|
|
*/
|
|
|
cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
|
|
|
-
|
|
|
- mutex_lock(&local->mtx);
|
|
|
- ieee80211_recalc_idle(local);
|
|
|
- mutex_unlock(&local->mtx);
|
|
|
}
|
|
|
|
|
|
static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
|
|
|
@@ -1887,8 +2117,6 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
|
|
|
container_of(work, struct ieee80211_sub_if_data,
|
|
|
u.mgd.csa_connection_drop_work);
|
|
|
|
|
|
- ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
|
|
- IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
__ieee80211_disconnect(sdata);
|
|
|
}
|
|
|
|
|
|
@@ -1934,7 +2162,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
|
|
|
ieee80211_vif_release_channel(sdata);
|
|
|
}
|
|
|
|
|
|
- cfg80211_put_bss(auth_data->bss);
|
|
|
+ cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss);
|
|
|
kfree(auth_data);
|
|
|
sdata->u.mgd.auth_data = NULL;
|
|
|
}
|
|
|
@@ -2028,6 +2256,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
sdata_info(sdata, "authenticated\n");
|
|
|
ifmgd->auth_data->done = true;
|
|
|
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
|
|
|
+ ifmgd->auth_data->timeout_started = true;
|
|
|
run_again(ifmgd, ifmgd->auth_data->timeout);
|
|
|
|
|
|
if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
|
|
|
@@ -2086,10 +2315,6 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
|
|
|
|
|
|
- mutex_lock(&sdata->local->mtx);
|
|
|
- ieee80211_recalc_idle(sdata->local);
|
|
|
- mutex_unlock(&sdata->local->mtx);
|
|
|
-
|
|
|
return RX_MGMT_CFG80211_DEAUTH;
|
|
|
}
|
|
|
|
|
|
@@ -2117,10 +2342,6 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
|
|
|
|
|
|
- mutex_lock(&sdata->local->mtx);
|
|
|
- ieee80211_recalc_idle(sdata->local);
|
|
|
- mutex_unlock(&sdata->local->mtx);
|
|
|
-
|
|
|
return RX_MGMT_CFG80211_DISASSOC;
|
|
|
}
|
|
|
|
|
|
@@ -2230,6 +2451,24 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
ifmgd->aid = aid;
|
|
|
|
|
|
+ /*
|
|
|
+ * We previously checked these in the beacon/probe response, so
|
|
|
+ * they should be present here. This is just a safety net.
|
|
|
+ */
|
|
|
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
|
|
|
+ (!elems.wmm_param || !elems.ht_cap_elem || !elems.ht_operation)) {
|
|
|
+ sdata_info(sdata,
|
|
|
+ "HT AP is missing WMM params or HT capability/operation in AssocResp\n");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
|
|
|
+ (!elems.vht_cap_elem || !elems.vht_operation)) {
|
|
|
+ sdata_info(sdata,
|
|
|
+ "VHT AP is missing VHT capability/operation in AssocResp\n");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
mutex_lock(&sdata->local->sta_mtx);
|
|
|
/*
|
|
|
* station info was already allocated and inserted before
|
|
|
@@ -2243,17 +2482,36 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
|
|
|
|
|
|
+ /* Set up internal HT/VHT capabilities */
|
|
|
if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
|
|
|
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
|
|
|
- elems.ht_cap_elem, &sta->sta.ht_cap);
|
|
|
+ elems.ht_cap_elem, sta);
|
|
|
+
|
|
|
+ if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
|
|
+ ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
|
|
|
+ elems.vht_cap_elem, sta);
|
|
|
|
|
|
- sta->supports_40mhz =
|
|
|
- sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
+ /*
|
|
|
+ * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
|
|
|
+ * in their association response, so ignore that data for our own
|
|
|
+ * configuration. If it changed since the last beacon, we'll get the
|
|
|
+ * next beacon and update then.
|
|
|
+ */
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If an operating mode notification IE is present, override the
|
|
|
+ * NSS calculation (that would be done in rate_control_rate_init())
|
|
|
+ * and use the # of streams from that element.
|
|
|
+ */
|
|
|
+ if (elems.opmode_notif &&
|
|
|
+ !(*elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) {
|
|
|
+ u8 nss;
|
|
|
|
|
|
- if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
|
|
- ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
|
|
|
- elems.vht_cap_elem,
|
|
|
- &sta->sta.vht_cap);
|
|
|
+ nss = *elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
|
|
|
+ nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
|
|
|
+ nss += 1;
|
|
|
+ sta->sta.rx_nss = nss;
|
|
|
+ }
|
|
|
|
|
|
rate_control_rate_init(sta);
|
|
|
|
|
|
@@ -2263,9 +2521,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
if (elems.wmm_param)
|
|
|
set_sta_flag(sta, WLAN_STA_WME);
|
|
|
|
|
|
- err = sta_info_move_state(sta, IEEE80211_STA_AUTH);
|
|
|
- if (!err)
|
|
|
- err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
|
|
+ err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
|
|
if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
|
|
|
err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
|
|
|
if (err) {
|
|
|
@@ -2294,11 +2550,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
ieee80211_set_wmm_default(sdata, false);
|
|
|
changed |= BSS_CHANGED_QOS;
|
|
|
|
|
|
- if (elems.ht_operation && elems.wmm_param &&
|
|
|
- !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
|
|
|
- changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
|
|
|
- cbss->bssid, false);
|
|
|
-
|
|
|
/* set AID and assoc capability,
|
|
|
* ieee80211_set_associated() will tell the driver */
|
|
|
bss_conf->aid = aid;
|
|
|
@@ -2372,6 +2623,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
|
"%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
|
|
|
mgmt->sa, tu, ms);
|
|
|
assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
|
|
|
+ assoc_data->timeout_started = true;
|
|
|
if (ms > IEEE80211_ASSOC_TIMEOUT)
|
|
|
run_again(ifmgd, assoc_data->timeout);
|
|
|
return RX_MGMT_NONE;
|
|
|
@@ -2387,7 +2639,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
|
if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
|
|
|
/* oops -- internal error -- send timeout for now */
|
|
|
ieee80211_destroy_assoc_data(sdata, false);
|
|
|
- cfg80211_put_bss(*bss);
|
|
|
+ cfg80211_put_bss(sdata->local->hw.wiphy, *bss);
|
|
|
return RX_MGMT_CFG80211_ASSOC_TIMEOUT;
|
|
|
}
|
|
|
sdata_info(sdata, "associated\n");
|
|
|
@@ -2423,7 +2675,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
|
|
need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
|
|
|
|
|
|
if (elems->tim && !elems->parse_error) {
|
|
|
- struct ieee80211_tim_ie *tim_ie = elems->tim;
|
|
|
+ const struct ieee80211_tim_ie *tim_ie = elems->tim;
|
|
|
sdata->u.mgd.dtim_period = tim_ie->dtim_period;
|
|
|
}
|
|
|
}
|
|
|
@@ -2495,6 +2747,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
|
|
|
sdata_info(sdata, "direct probe responded\n");
|
|
|
ifmgd->auth_data->tries = 0;
|
|
|
ifmgd->auth_data->timeout = jiffies;
|
|
|
+ ifmgd->auth_data->timeout_started = true;
|
|
|
run_again(ifmgd, ifmgd->auth_data->timeout);
|
|
|
}
|
|
|
}
|
|
|
@@ -2520,10 +2773,10 @@ static const u64 care_about_ies =
|
|
|
(1ULL << WLAN_EID_HT_CAPABILITY) |
|
|
|
(1ULL << WLAN_EID_HT_OPERATION);
|
|
|
|
|
|
-static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
- struct ieee80211_mgmt *mgmt,
|
|
|
- size_t len,
|
|
|
- struct ieee80211_rx_status *rx_status)
|
|
|
+static enum rx_mgmt_action
|
|
|
+ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct ieee80211_mgmt *mgmt, size_t len,
|
|
|
+ u8 *deauth_buf, struct ieee80211_rx_status *rx_status)
|
|
|
{
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
|
|
@@ -2532,6 +2785,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
|
struct ieee80211_channel *chan;
|
|
|
+ struct sta_info *sta;
|
|
|
u32 changed = 0;
|
|
|
bool erp_valid;
|
|
|
u8 erp_value = 0;
|
|
|
@@ -2543,18 +2797,18 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
/* Process beacon from the current BSS */
|
|
|
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
|
|
|
if (baselen > len)
|
|
|
- return;
|
|
|
+ return RX_MGMT_NONE;
|
|
|
|
|
|
rcu_read_lock();
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
if (!chanctx_conf) {
|
|
|
rcu_read_unlock();
|
|
|
- return;
|
|
|
+ return RX_MGMT_NONE;
|
|
|
}
|
|
|
|
|
|
if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
|
|
|
rcu_read_unlock();
|
|
|
- return;
|
|
|
+ return RX_MGMT_NONE;
|
|
|
}
|
|
|
chan = chanctx_conf->def.chan;
|
|
|
rcu_read_unlock();
|
|
|
@@ -2567,15 +2821,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
|
|
ifmgd->assoc_data->have_beacon = true;
|
|
|
ifmgd->assoc_data->need_beacon = false;
|
|
|
+ if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
|
|
|
+ sdata->vif.bss_conf.sync_tsf =
|
|
|
+ le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
|
+ sdata->vif.bss_conf.sync_device_ts =
|
|
|
+ rx_status->device_timestamp;
|
|
|
+ if (elems.tim)
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count =
|
|
|
+ elems.tim->dtim_count;
|
|
|
+ else
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
+ }
|
|
|
/* continue assoc process */
|
|
|
ifmgd->assoc_data->timeout = jiffies;
|
|
|
+ ifmgd->assoc_data->timeout_started = true;
|
|
|
run_again(ifmgd, ifmgd->assoc_data->timeout);
|
|
|
- return;
|
|
|
+ return RX_MGMT_NONE;
|
|
|
}
|
|
|
|
|
|
if (!ifmgd->associated ||
|
|
|
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
|
|
|
- return;
|
|
|
+ return RX_MGMT_NONE;
|
|
|
bssid = ifmgd->associated->bssid;
|
|
|
|
|
|
/* Track average RSSI from the Beacon frames of the current AP */
|
|
|
@@ -2641,7 +2907,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
|
|
|
mlme_dbg_ratelimited(sdata,
|
|
|
- "cancelling probereq poll due to a received beacon\n");
|
|
|
+ "cancelling AP probe due to a received beacon\n");
|
|
|
mutex_lock(&local->mtx);
|
|
|
ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
|
|
|
ieee80211_run_deferred_scan(local);
|
|
|
@@ -2713,7 +2979,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
}
|
|
|
|
|
|
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
|
|
|
- return;
|
|
|
+ return RX_MGMT_NONE;
|
|
|
ifmgd->beacon_crc = ncrc;
|
|
|
ifmgd->beacon_crc_valid = true;
|
|
|
|
|
|
@@ -2725,7 +2991,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
/*
|
|
|
* If we haven't had a beacon before, tell the driver about the
|
|
|
- * DTIM period now.
|
|
|
+ * DTIM period (and beacon timing if desired) now.
|
|
|
*/
|
|
|
if (!bss_conf->dtim_period) {
|
|
|
/* a few bogus AP send dtim_period = 0 or no TIM IE */
|
|
|
@@ -2733,6 +2999,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
|
|
|
else
|
|
|
bss_conf->dtim_period = 1;
|
|
|
+
|
|
|
+ if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
|
|
|
+ sdata->vif.bss_conf.sync_tsf =
|
|
|
+ le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
|
+ sdata->vif.bss_conf.sync_device_ts =
|
|
|
+ rx_status->device_timestamp;
|
|
|
+ if (elems.tim)
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count =
|
|
|
+ elems.tim->dtim_count;
|
|
|
+ else
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
+ }
|
|
|
+
|
|
|
changed |= BSS_CHANGED_DTIM_PERIOD;
|
|
|
}
|
|
|
|
|
|
@@ -2746,11 +3025,22 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
le16_to_cpu(mgmt->u.beacon.capab_info),
|
|
|
erp_valid, erp_value);
|
|
|
|
|
|
+ mutex_lock(&local->sta_mtx);
|
|
|
+ sta = sta_info_get(sdata, bssid);
|
|
|
+
|
|
|
+ if (ieee80211_config_bw(sdata, sta, elems.ht_operation,
|
|
|
+ elems.vht_operation, bssid, &changed)) {
|
|
|
+ mutex_unlock(&local->sta_mtx);
|
|
|
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
+ WLAN_REASON_DEAUTH_LEAVING,
|
|
|
+ true, deauth_buf);
|
|
|
+ return RX_MGMT_CFG80211_TX_DEAUTH;
|
|
|
+ }
|
|
|
|
|
|
- if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
|
|
|
- !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
|
|
|
- changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
|
|
|
- bssid, true);
|
|
|
+ if (sta && elems.opmode_notif)
|
|
|
+ ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif,
|
|
|
+ rx_status->band, true);
|
|
|
+ mutex_unlock(&local->sta_mtx);
|
|
|
|
|
|
if (elems.country_elem && elems.pwr_constr_elem &&
|
|
|
mgmt->u.probe_resp.capab_info &
|
|
|
@@ -2761,6 +3051,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
elems.pwr_constr_elem);
|
|
|
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
|
+
|
|
|
+ return RX_MGMT_NONE;
|
|
|
}
|
|
|
|
|
|
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
@@ -2771,6 +3063,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
struct ieee80211_mgmt *mgmt;
|
|
|
struct cfg80211_bss *bss = NULL;
|
|
|
enum rx_mgmt_action rma = RX_MGMT_NONE;
|
|
|
+ u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
|
|
u16 fc;
|
|
|
|
|
|
rx_status = (struct ieee80211_rx_status *) skb->cb;
|
|
|
@@ -2781,7 +3074,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
switch (fc & IEEE80211_FCTL_STYPE) {
|
|
|
case IEEE80211_STYPE_BEACON:
|
|
|
- ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
|
|
|
+ rma = ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
|
|
|
+ deauth_buf, rx_status);
|
|
|
break;
|
|
|
case IEEE80211_STYPE_PROBE_RESP:
|
|
|
ieee80211_rx_mgmt_probe_resp(sdata, skb);
|
|
|
@@ -2830,6 +3124,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
case RX_MGMT_CFG80211_ASSOC_TIMEOUT:
|
|
|
cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
|
|
|
break;
|
|
|
+ case RX_MGMT_CFG80211_TX_DEAUTH:
|
|
|
+ cfg80211_send_deauth(sdata->dev, deauth_buf,
|
|
|
+ sizeof(deauth_buf));
|
|
|
+ break;
|
|
|
default:
|
|
|
WARN(1, "unexpected: %d", rma);
|
|
|
}
|
|
|
@@ -2853,7 +3151,6 @@ static void ieee80211_sta_timer(unsigned long data)
|
|
|
static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
|
|
|
u8 *bssid, u8 reason, bool tx)
|
|
|
{
|
|
|
- struct ieee80211_local *local = sdata->local;
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
|
|
|
|
|
@@ -2867,10 +3164,6 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
|
|
|
*/
|
|
|
cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
|
|
|
|
|
|
- mutex_lock(&local->mtx);
|
|
|
- ieee80211_recalc_idle(local);
|
|
|
- mutex_unlock(&local->mtx);
|
|
|
-
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
}
|
|
|
|
|
|
@@ -2953,7 +3246,10 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
|
|
|
|
|
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
|
|
|
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
|
|
|
+ ifmgd->auth_data->timeout_started = true;
|
|
|
run_again(ifmgd, auth_data->timeout);
|
|
|
+ } else {
|
|
|
+ auth_data->timeout_started = false;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
@@ -2987,7 +3283,10 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
|
|
|
|
|
|
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
|
|
|
assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
|
|
|
+ assoc_data->timeout_started = true;
|
|
|
run_again(&sdata->u.mgd, assoc_data->timeout);
|
|
|
+ } else {
|
|
|
+ assoc_data->timeout_started = false;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
@@ -3026,6 +3325,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|
|
} else {
|
|
|
ifmgd->auth_data->timeout = jiffies - 1;
|
|
|
}
|
|
|
+ ifmgd->auth_data->timeout_started = true;
|
|
|
} else if (ifmgd->assoc_data &&
|
|
|
(ieee80211_is_assoc_req(fc) ||
|
|
|
ieee80211_is_reassoc_req(fc))) {
|
|
|
@@ -3036,10 +3336,11 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|
|
} else {
|
|
|
ifmgd->assoc_data->timeout = jiffies - 1;
|
|
|
}
|
|
|
+ ifmgd->assoc_data->timeout_started = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (ifmgd->auth_data &&
|
|
|
+ if (ifmgd->auth_data && ifmgd->auth_data->timeout_started &&
|
|
|
time_after(jiffies, ifmgd->auth_data->timeout)) {
|
|
|
if (ifmgd->auth_data->done) {
|
|
|
/*
|
|
|
@@ -3058,10 +3359,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|
|
cfg80211_send_auth_timeout(sdata->dev, bssid);
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
}
|
|
|
- } else if (ifmgd->auth_data)
|
|
|
+ } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
|
|
|
run_again(ifmgd, ifmgd->auth_data->timeout);
|
|
|
|
|
|
- if (ifmgd->assoc_data &&
|
|
|
+ if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
|
|
|
time_after(jiffies, ifmgd->assoc_data->timeout)) {
|
|
|
if ((ifmgd->assoc_data->need_beacon &&
|
|
|
!ifmgd->assoc_data->have_beacon) ||
|
|
|
@@ -3076,7 +3377,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|
|
cfg80211_send_assoc_timeout(sdata->dev, bssid);
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
}
|
|
|
- } else if (ifmgd->assoc_data)
|
|
|
+ } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
|
|
|
run_again(ifmgd, ifmgd->assoc_data->timeout);
|
|
|
|
|
|
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
|
|
@@ -3141,10 +3442,6 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
-
|
|
|
- mutex_lock(&local->mtx);
|
|
|
- ieee80211_recalc_idle(local);
|
|
|
- mutex_unlock(&local->mtx);
|
|
|
}
|
|
|
|
|
|
static void ieee80211_sta_bcn_mon_timer(unsigned long data)
|
|
|
@@ -3324,201 +3621,6 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-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;
|
|
|
- }
|
|
|
-
|
|
|
- WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
|
|
-
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
-static u32
|
|
|
-ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
|
|
- struct ieee80211_supported_band *sband,
|
|
|
- struct ieee80211_channel *channel,
|
|
|
- const struct ieee80211_ht_operation *ht_oper,
|
|
|
- const struct ieee80211_vht_operation *vht_oper,
|
|
|
- struct cfg80211_chan_def *chandef)
|
|
|
-{
|
|
|
- struct cfg80211_chan_def vht_chandef;
|
|
|
- u32 ht_cfreq, ret;
|
|
|
-
|
|
|
- chandef->chan = channel;
|
|
|
- chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
|
|
- chandef->center_freq1 = channel->center_freq;
|
|
|
- chandef->center_freq2 = 0;
|
|
|
-
|
|
|
- if (!ht_oper || !sband->ht_cap.ht_supported) {
|
|
|
- ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- chandef->width = NL80211_CHAN_WIDTH_20;
|
|
|
-
|
|
|
- ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
|
|
|
- channel->band);
|
|
|
- /* check that channel matches the right operating channel */
|
|
|
- if (channel->center_freq != ht_cfreq) {
|
|
|
- /*
|
|
|
- * It's possible that some APs are confused here;
|
|
|
- * Netgear WNDR3700 sometimes reports 4 higher than
|
|
|
- * the actual channel in association responses, but
|
|
|
- * since we look at probe response/beacon data here
|
|
|
- * it should be OK.
|
|
|
- */
|
|
|
- sdata_info(sdata,
|
|
|
- "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
|
|
|
- channel->center_freq, ht_cfreq,
|
|
|
- ht_oper->primary_chan, channel->band);
|
|
|
- ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- /* check 40 MHz support, if we have it */
|
|
|
- if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
|
|
|
- switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
|
|
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
|
|
- chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
- chandef->center_freq1 += 10;
|
|
|
- break;
|
|
|
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
|
|
- chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
- chandef->center_freq1 -= 10;
|
|
|
- break;
|
|
|
- }
|
|
|
- } else {
|
|
|
- /* 40 MHz (and 80 MHz) must be supported for VHT */
|
|
|
- ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- if (!vht_oper || !sband->vht_cap.vht_supported) {
|
|
|
- ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- vht_chandef.chan = channel;
|
|
|
- vht_chandef.center_freq1 =
|
|
|
- ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
|
|
|
- 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;
|
|
|
- break;
|
|
|
- case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
|
|
- vht_chandef.width = NL80211_CHAN_WIDTH_80;
|
|
|
- break;
|
|
|
- case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
|
|
- vht_chandef.width = NL80211_CHAN_WIDTH_160;
|
|
|
- break;
|
|
|
- case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
|
|
- vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
|
|
- break;
|
|
|
- default:
|
|
|
- sdata_info(sdata,
|
|
|
- "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
|
|
|
- vht_oper->chan_width);
|
|
|
- ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- if (!cfg80211_chandef_valid(&vht_chandef)) {
|
|
|
- sdata_info(sdata,
|
|
|
- "AP VHT information is invalid, disable VHT\n");
|
|
|
- ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
|
|
|
- ret = 0;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
|
|
|
- sdata_info(sdata,
|
|
|
- "AP VHT information doesn't match HT, disable VHT\n");
|
|
|
- ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- *chandef = vht_chandef;
|
|
|
-
|
|
|
- ret = 0;
|
|
|
-
|
|
|
- while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
|
|
- IEEE80211_CHAN_DISABLED)) {
|
|
|
- if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
|
|
|
- ret = IEEE80211_STA_DISABLE_HT |
|
|
|
- IEEE80211_STA_DISABLE_VHT;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- ret = chandef_downgrade(chandef);
|
|
|
- }
|
|
|
-
|
|
|
- if (chandef->width != vht_chandef.width)
|
|
|
- sdata_info(sdata,
|
|
|
- "local regulatory prevented using AP HT/VHT configuration, downgraded\n");
|
|
|
-
|
|
|
-out:
|
|
|
- WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
|
|
|
- return ret;
|
|
|
-}
|
|
|
-
|
|
|
static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
|
|
|
struct cfg80211_bss *cbss)
|
|
|
{
|
|
|
@@ -3584,16 +3686,22 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
|
|
|
sband->ht_cap.ht_supported) {
|
|
|
- const u8 *ht_oper_ie;
|
|
|
+ const u8 *ht_oper_ie, *ht_cap;
|
|
|
|
|
|
ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION);
|
|
|
if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
|
|
|
ht_oper = (void *)(ht_oper_ie + 2);
|
|
|
+
|
|
|
+ ht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
|
|
|
+ if (!ht_cap || ht_cap[1] < sizeof(struct ieee80211_ht_cap)) {
|
|
|
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
|
|
+ ht_oper = NULL;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
|
|
|
sband->vht_cap.vht_supported) {
|
|
|
- const u8 *vht_oper_ie;
|
|
|
+ const u8 *vht_oper_ie, *vht_cap;
|
|
|
|
|
|
vht_oper_ie = ieee80211_bss_get_ie(cbss,
|
|
|
WLAN_EID_VHT_OPERATION);
|
|
|
@@ -3603,15 +3711,21 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
|
|
vht_oper = NULL;
|
|
|
sdata_info(sdata,
|
|
|
"AP advertised VHT without HT, disabling both\n");
|
|
|
- sdata->flags |= IEEE80211_STA_DISABLE_HT;
|
|
|
- sdata->flags |= IEEE80211_STA_DISABLE_VHT;
|
|
|
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
|
|
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
|
|
+ }
|
|
|
+
|
|
|
+ vht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
|
|
|
+ if (!vht_cap || vht_cap[1] < sizeof(struct ieee80211_vht_cap)) {
|
|
|
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
|
|
+ vht_oper = NULL;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
|
|
|
cbss->channel,
|
|
|
ht_oper, vht_oper,
|
|
|
- &chandef);
|
|
|
+ &chandef, true);
|
|
|
|
|
|
sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
|
|
|
local->rx_chains);
|
|
|
@@ -3628,8 +3742,11 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
|
|
*/
|
|
|
ret = ieee80211_vif_use_channel(sdata, &chandef,
|
|
|
IEEE80211_CHANCTX_SHARED);
|
|
|
- while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
|
|
|
+ while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
|
|
|
ifmgd->flags |= chandef_downgrade(&chandef);
|
|
|
+ ret = ieee80211_vif_use_channel(sdata, &chandef,
|
|
|
+ IEEE80211_CHANCTX_SHARED);
|
|
|
+ }
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
@@ -3658,15 +3775,12 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
- mutex_lock(&local->mtx);
|
|
|
- ieee80211_recalc_idle(sdata->local);
|
|
|
- mutex_unlock(&local->mtx);
|
|
|
-
|
|
|
if (new_sta) {
|
|
|
u32 rates = 0, basic_rates = 0;
|
|
|
bool have_higher_than_11mbit;
|
|
|
int min_rate = INT_MAX, min_rate_index = -1;
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
+ const struct cfg80211_bss_ies *ies;
|
|
|
|
|
|
sband = local->hw.wiphy->bands[cbss->channel->band];
|
|
|
|
|
|
@@ -3710,8 +3824,34 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
/* set timing information */
|
|
|
sdata->vif.bss_conf.beacon_int = cbss->beacon_interval;
|
|
|
- sdata->vif.bss_conf.sync_tsf = cbss->tsf;
|
|
|
- sdata->vif.bss_conf.sync_device_ts = bss->device_ts;
|
|
|
+ rcu_read_lock();
|
|
|
+ ies = rcu_dereference(cbss->beacon_ies);
|
|
|
+ if (ies) {
|
|
|
+ const u8 *tim_ie;
|
|
|
+
|
|
|
+ sdata->vif.bss_conf.sync_tsf = ies->tsf;
|
|
|
+ sdata->vif.bss_conf.sync_device_ts =
|
|
|
+ bss->device_ts_beacon;
|
|
|
+ tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
|
|
|
+ ies->data, ies->len);
|
|
|
+ if (tim_ie && tim_ie[1] >= 2)
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count = tim_ie[2];
|
|
|
+ else
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
+ } else if (!(local->hw.flags &
|
|
|
+ IEEE80211_HW_TIMING_BEACON_ONLY)) {
|
|
|
+ ies = rcu_dereference(cbss->proberesp_ies);
|
|
|
+ /* must be non-NULL since beacon IEs were NULL */
|
|
|
+ sdata->vif.bss_conf.sync_tsf = ies->tsf;
|
|
|
+ sdata->vif.bss_conf.sync_device_ts =
|
|
|
+ bss->device_ts_presp;
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
+ } else {
|
|
|
+ sdata->vif.bss_conf.sync_tsf = 0;
|
|
|
+ sdata->vif.bss_conf.sync_device_ts = 0;
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
/* tell driver about BSSID, basic rates and timing */
|
|
|
ieee80211_bss_info_change_notify(sdata,
|
|
|
@@ -3831,7 +3971,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
}
|
|
|
|
|
|
/* hold our own reference */
|
|
|
- cfg80211_ref_bss(auth_data->bss);
|
|
|
+ cfg80211_ref_bss(local->hw.wiphy, auth_data->bss);
|
|
|
err = 0;
|
|
|
goto out_unlock;
|
|
|
|
|
|
@@ -4032,20 +4172,33 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
sdata_info(sdata, "waiting for beacon from %pM\n",
|
|
|
ifmgd->bssid);
|
|
|
assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
|
|
|
+ assoc_data->timeout_started = true;
|
|
|
assoc_data->need_beacon = true;
|
|
|
} else if (beacon_ies) {
|
|
|
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
|
|
|
beacon_ies->data,
|
|
|
beacon_ies->len);
|
|
|
+ u8 dtim_count = 0;
|
|
|
+
|
|
|
if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) {
|
|
|
const struct ieee80211_tim_ie *tim;
|
|
|
tim = (void *)(tim_ie + 2);
|
|
|
ifmgd->dtim_period = tim->dtim_period;
|
|
|
+ dtim_count = tim->dtim_count;
|
|
|
}
|
|
|
assoc_data->have_beacon = true;
|
|
|
assoc_data->timeout = jiffies;
|
|
|
+ assoc_data->timeout_started = true;
|
|
|
+
|
|
|
+ if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) {
|
|
|
+ sdata->vif.bss_conf.sync_tsf = beacon_ies->tsf;
|
|
|
+ sdata->vif.bss_conf.sync_device_ts =
|
|
|
+ bss->device_ts_beacon;
|
|
|
+ sdata->vif.bss_conf.sync_dtim_count = dtim_count;
|
|
|
+ }
|
|
|
} else {
|
|
|
assoc_data->timeout = jiffies;
|
|
|
+ assoc_data->timeout_started = true;
|
|
|
}
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
@@ -4115,10 +4268,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
|
out:
|
|
|
- mutex_lock(&sdata->local->mtx);
|
|
|
- ieee80211_recalc_idle(sdata->local);
|
|
|
- mutex_unlock(&sdata->local->mtx);
|
|
|
-
|
|
|
if (sent_frame)
|
|
|
__cfg80211_send_deauth(sdata->dev, frame_buf,
|
|
|
IEEE80211_DEAUTH_FRAME_LEN);
|
|
|
@@ -4159,10 +4308,6 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
|
__cfg80211_send_disassoc(sdata->dev, frame_buf,
|
|
|
IEEE80211_DEAUTH_FRAME_LEN);
|
|
|
|
|
|
- mutex_lock(&sdata->local->mtx);
|
|
|
- ieee80211_recalc_idle(sdata->local);
|
|
|
- mutex_unlock(&sdata->local->mtx);
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|