summaryrefslogtreecommitdiff
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c656
1 files changed, 510 insertions, 146 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 93af0f1c9d99..26053bf2faa8 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -157,14 +157,18 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct cfg80211_chan_def vht_chandef;
+ struct ieee80211_sta_ht_cap sta_ht_cap;
u32 ht_cfreq, ret;
+ memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+ ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
chandef->chan = channel;
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
chandef->center_freq1 = channel->center_freq;
chandef->center_freq2 = 0;
- if (!ht_cap || !ht_oper || !sband->ht_cap.ht_supported) {
+ if (!ht_cap || !ht_oper || !sta_ht_cap.ht_supported) {
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
goto out;
}
@@ -174,6 +178,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
if (!(ht_cap->cap_info &
cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) {
ret = IEEE80211_STA_DISABLE_40MHZ;
+ vht_chandef = *chandef;
goto out;
}
@@ -197,7 +202,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
}
/* check 40 MHz support, if we have it */
- if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ if (sta_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;
@@ -552,13 +557,17 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
cap = vht_cap.cap;
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
- cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
- cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
+ bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+ cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
}
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
- cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
}
/*
@@ -775,11 +784,30 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
WLAN_EID_QOS_CAPA,
WLAN_EID_RRM_ENABLED_CAPABILITIES,
WLAN_EID_MOBILITY_DOMAIN,
+ WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */
+ WLAN_EID_RIC_DATA, /* reassoc only */
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
};
- noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
- before_ht, ARRAY_SIZE(before_ht),
- offset);
+ static const u8 after_ric[] = {
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+ WLAN_EID_HT_CAPABILITY,
+ WLAN_EID_BSS_COEX_2040,
+ WLAN_EID_EXT_CAPABILITY,
+ WLAN_EID_QOS_TRAFFIC_CAPA,
+ WLAN_EID_TIM_BCAST_REQ,
+ WLAN_EID_INTERWORKING,
+ /* 60GHz doesn't happen right now */
+ WLAN_EID_VHT_CAPABILITY,
+ WLAN_EID_OPMODE_NOTIF,
+ };
+
+ noffset = ieee80211_ie_split_ric(assoc_data->ie,
+ assoc_data->ie_len,
+ before_ht,
+ ARRAY_SIZE(before_ht),
+ after_ric,
+ ARRAY_SIZE(after_ric),
+ offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, assoc_data->ie + offset, noffset - offset);
offset = noffset;
@@ -813,6 +841,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
WLAN_EID_TIM_BCAST_REQ,
WLAN_EID_INTERWORKING,
};
+
+ /* RIC already taken above, so no need to handle here anymore */
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
before_vht, ARRAY_SIZE(before_vht),
offset);
@@ -1001,14 +1031,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
/* XXX: shouldn't really modify cfg80211-owned data! */
ifmgd->associated->channel = sdata->csa_chandef.chan;
- sdata->vif.csa_active = false;
-
- /* XXX: wait for a beacon first? */
- if (sdata->csa_block_tx) {
- ieee80211_wake_vif_queues(local, sdata,
- IEEE80211_QUEUE_STOP_REASON_CSA);
- sdata->csa_block_tx = false;
- }
+ ifmgd->csa_waiting_bcn = true;
ieee80211_sta_reset_beacon_monitor(sdata);
ieee80211_sta_reset_conn_monitor(sdata);
@@ -1019,6 +1042,37 @@ out:
sdata_unlock(sdata);
}
+static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ int ret;
+
+ sdata_assert_lock(sdata);
+
+ WARN_ON(!sdata->vif.csa_active);
+
+ if (sdata->csa_block_tx) {
+ ieee80211_wake_vif_queues(local, sdata,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+ sdata->csa_block_tx = false;
+ }
+
+ sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
+
+ ret = drv_post_channel_switch(sdata);
+ if (ret) {
+ sdata_info(sdata,
+ "driver post channel switch failed, disconnecting\n");
+ ieee80211_queue_work(&local->hw,
+ &ifmgd->csa_connection_drop_work);
+ return;
+ }
+
+ cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
+}
+
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@@ -1046,7 +1100,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
static void
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- u64 timestamp, struct ieee802_11_elems *elems,
+ u64 timestamp, u32 device_timestamp,
+ struct ieee802_11_elems *elems,
bool beacon)
{
struct ieee80211_local *local = sdata->local;
@@ -1056,6 +1111,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx *chanctx;
enum ieee80211_band current_band;
struct ieee80211_csa_ie csa_ie;
+ struct ieee80211_channel_switch ch_switch;
int res;
sdata_assert_lock(sdata);
@@ -1094,6 +1150,17 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return;
}
+ if (cfg80211_chandef_identical(&csa_ie.chandef,
+ &sdata->vif.bss_conf.chandef)) {
+ if (ifmgd->csa_ignored_same_chan)
+ return;
+ sdata_info(sdata,
+ "AP %pM tries to chanswitch to same channel, ignore\n",
+ ifmgd->associated->bssid);
+ ifmgd->csa_ignored_same_chan = true;
+ return;
+ }
+
mutex_lock(&local->mtx);
mutex_lock(&local->chanctx_mtx);
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
@@ -1101,30 +1168,28 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
if (!conf) {
sdata_info(sdata,
"no channel context assigned to vif?, disconnecting\n");
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
+ goto drop_connection;
}
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
- if (local->use_chanctx) {
- u32 num_chanctx = 0;
- list_for_each_entry(chanctx, &local->chanctx_list, list)
- num_chanctx++;
+ if (local->use_chanctx &&
+ !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
+ sdata_info(sdata,
+ "driver doesn't support chan-switch with channel contexts\n");
+ goto drop_connection;
+ }
- if (num_chanctx > 1 ||
- !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
- sdata_info(sdata,
- "not handling chan-switch with channel contexts\n");
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
- }
+ ch_switch.timestamp = timestamp;
+ ch_switch.device_timestamp = device_timestamp;
+ ch_switch.block_tx = csa_ie.mode;
+ ch_switch.chandef = csa_ie.chandef;
+ ch_switch.count = csa_ie.count;
+
+ if (drv_pre_channel_switch(sdata, &ch_switch)) {
+ sdata_info(sdata,
+ "preparing for channel switch failed, disconnecting\n");
+ goto drop_connection;
}
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@@ -1133,33 +1198,26 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
sdata_info(sdata,
"failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
res);
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
+ goto drop_connection;
}
mutex_unlock(&local->chanctx_mtx);
sdata->vif.csa_active = true;
sdata->csa_chandef = csa_ie.chandef;
sdata->csa_block_tx = csa_ie.mode;
+ ifmgd->csa_ignored_same_chan = false;
if (sdata->csa_block_tx)
ieee80211_stop_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
mutex_unlock(&local->mtx);
+ cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
+ csa_ie.count);
+
if (local->ops->channel_switch) {
/* use driver's channel switch callback */
- struct ieee80211_channel_switch ch_switch = {
- .timestamp = timestamp,
- .block_tx = csa_ie.mode,
- .chandef = csa_ie.chandef,
- .count = csa_ie.count,
- };
-
- drv_channel_switch(local, &ch_switch);
+ drv_channel_switch(local, sdata, &ch_switch);
return;
}
@@ -1170,6 +1228,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
mod_timer(&ifmgd->chswitch_timer,
TU_TO_EXP_TIME((csa_ie.count - 1) *
cbss->beacon_interval));
+ return;
+ drop_connection:
+ ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
+ mutex_unlock(&local->chanctx_mtx);
+ mutex_unlock(&local->mtx);
}
static bool
@@ -1226,8 +1289,11 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
country_ie_len -= 3;
}
- if (have_chan_pwr)
+ if (have_chan_pwr && pwr_constr_elem)
*pwr_reduction = *pwr_constr_elem;
+ else
+ *pwr_reduction = 0;
+
return have_chan_pwr;
}
@@ -1256,10 +1322,11 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
int chan_pwr = 0, pwr_reduction_80211h = 0;
int pwr_level_cisco, pwr_level_80211h;
int new_ap_level;
+ __le16 capab = mgmt->u.probe_resp.capab_info;
- if (country_ie && pwr_constr_ie &&
- mgmt->u.probe_resp.capab_info &
- cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) {
+ if (country_ie &&
+ (capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) ||
+ capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) {
has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
sdata, channel, country_ie, country_ie_len,
pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
@@ -1281,15 +1348,15 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
*/
if (has_80211h_pwr &&
(!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
- sdata_info(sdata,
- "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
- pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
- sdata->u.mgd.bssid);
+ sdata_dbg(sdata,
+ "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
+ pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
+ sdata->u.mgd.bssid);
new_ap_level = pwr_level_80211h;
} else { /* has_cisco_pwr is always true here. */
- sdata_info(sdata,
- "Limiting TX power to %d dBm as advertised by %pM\n",
- pwr_level_cisco, sdata->u.mgd.bssid);
+ sdata_dbg(sdata,
+ "Limiting TX power to %d dBm as advertised by %pM\n",
+ pwr_level_cisco, sdata->u.mgd.bssid);
new_ap_level = pwr_level_cisco;
}
@@ -1538,7 +1605,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
} else {
ieee80211_send_nullfunc(local, sdata, 1);
/* Flush to get the tx status of nullfunc frame */
- ieee80211_flush_queues(local, sdata);
+ ieee80211_flush_queues(local, sdata, false);
}
}
@@ -1555,9 +1622,6 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
{
struct ieee80211_local *local = (void *) data;
- if (local->quiescing || local->suspended)
- return;
-
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
}
@@ -1580,6 +1644,95 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
mutex_unlock(&sdata->local->mtx);
}
+static bool
+__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ bool ret = false;
+ int ac;
+
+ if (local->hw.queues < IEEE80211_NUM_ACS)
+ return false;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+ int non_acm_ac;
+ unsigned long now = jiffies;
+
+ if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
+ tx_tspec->admitted_time &&
+ time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->consumed_tx_time = 0;
+ tx_tspec->time_slice_start = now;
+
+ if (tx_tspec->downgraded)
+ tx_tspec->action =
+ TX_TSPEC_ACTION_STOP_DOWNGRADE;
+ }
+
+ switch (tx_tspec->action) {
+ case TX_TSPEC_ACTION_STOP_DOWNGRADE:
+ /* take the original parameters */
+ if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
+ sdata_err(sdata,
+ "failed to set TX queue parameters for queue %d\n",
+ ac);
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ tx_tspec->downgraded = false;
+ ret = true;
+ break;
+ case TX_TSPEC_ACTION_DOWNGRADE:
+ if (time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ ret = true;
+ break;
+ }
+ /* downgrade next lower non-ACM AC */
+ for (non_acm_ac = ac + 1;
+ non_acm_ac < IEEE80211_NUM_ACS;
+ non_acm_ac++)
+ if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
+ break;
+ /* The loop will result in using BK even if it requires
+ * admission control, such configuration makes no sense
+ * and we have to transmit somehow - the AC selection
+ * does the same thing.
+ */
+ if (drv_conf_tx(local, sdata, ac,
+ &sdata->tx_conf[non_acm_ac]))
+ sdata_err(sdata,
+ "failed to set TX queue parameters for queue %d\n",
+ ac);
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ ret = true;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk,
+ tx_tspec->time_slice_start + HZ - now + 1);
+ break;
+ case TX_TSPEC_ACTION_NONE:
+ /* nothing now */
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
+{
+ if (__ieee80211_sta_handle_tspec_ac_params(sdata))
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
+}
+
+static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.tx_tspec_wk.work);
+ ieee80211_sta_handle_tspec_ac_params(sdata);
+}
+
/* MLME */
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -1664,12 +1817,14 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
params.uapsd = uapsd;
mlme_dbg(sdata,
- "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
+ "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
queue, aci, acm,
params.aifs, params.cw_min, params.cw_max,
- params.txop, params.uapsd);
+ params.txop, params.uapsd,
+ ifmgd->tx_tspec[queue].downgraded);
sdata->tx_conf[queue] = params;
- if (drv_conf_tx(local, sdata, queue, &params))
+ if (!ifmgd->tx_tspec[queue].downgraded &&
+ drv_conf_tx(local, sdata, queue, &params))
sdata_err(sdata,
"failed to set TX queue parameters for queue %d\n",
queue);
@@ -1854,21 +2009,29 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
/* disable per-vif ps */
ieee80211_recalc_ps_vif(sdata);
- /* flush out any pending frame (e.g. DELBA) before deauth/disassoc */
+ /* make sure ongoing transmission finishes */
+ synchronize_net();
+
+ /*
+ * drop any frame before deauth/disassoc, this can be data or
+ * management frame. Since we are disconnecting, we should not
+ * insist sending these frames which can take time and delay
+ * the disconnection and possible the roaming.
+ */
if (tx)
- ieee80211_flush_queues(local, sdata);
+ ieee80211_flush_queues(local, sdata, true);
/* deauthenticate/disassociate now */
if (tx || frame_buf)
ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype,
reason, tx, frame_buf);
- /* flush out frame */
+ /* flush out frame - make sure the deauth was actually sent */
if (tx)
- ieee80211_flush_queues(local, sdata);
+ ieee80211_flush_queues(local, sdata, false);
/* clear bssid only after building the needed mgmt frames */
- memset(ifmgd->bssid, 0, ETH_ALEN);
+ eth_zero_addr(ifmgd->bssid);
/* remove AP and TDLS peers */
sta_info_flush(sdata);
@@ -1924,6 +2087,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_vif_release_channel(sdata);
sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
+ ifmgd->csa_ignored_same_chan = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -1931,6 +2096,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
}
mutex_unlock(&local->mtx);
+ /* existing TX TSPEC sessions no longer exist */
+ memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
+ cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
+
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
}
@@ -1983,9 +2152,46 @@ out:
mutex_unlock(&local->mtx);
}
+static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr,
+ u16 tx_time)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ int ac = ieee80211_ac_from_tid(tid);
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+ unsigned long now = jiffies;
+
+ if (likely(!tx_tspec->admitted_time))
+ return;
+
+ if (time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->consumed_tx_time = 0;
+ tx_tspec->time_slice_start = now;
+
+ if (tx_tspec->downgraded) {
+ tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
+ }
+ }
+
+ if (tx_tspec->downgraded)
+ return;
+
+ tx_tspec->consumed_tx_time += tx_time;
+
+ if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
+ tx_tspec->downgraded = true;
+ tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
+ }
+}
+
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_hdr *hdr, bool ack)
+ struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
{
+ ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
+
if (!ieee80211_is_data(hdr->frame_control))
return;
@@ -2040,7 +2246,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
else
ssid_len = ssid[1];
- ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
+ ieee80211_send_probe_req(sdata, sdata->vif.addr, dst,
+ ssid + 2, ssid_len, NULL,
0, (u32) -1, true, 0,
ifmgd->associated->channel, false);
rcu_read_unlock();
@@ -2048,8 +2255,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
run_again(sdata, ifmgd->probe_timeout);
- if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
- ieee80211_flush_queues(sdata->local, sdata);
}
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@ -2078,9 +2283,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
"detected beacon loss from AP (missed %d beacons) - probing\n",
beacon_loss_count);
- ieee80211_cqm_rssi_notify(&sdata->vif,
- NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
- GFP_KERNEL);
+ ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL);
}
/*
@@ -2145,7 +2348,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
else
ssid_len = ssid[1];
- skb = ieee80211_build_probe_req(sdata, cbss->bssid,
+ skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid,
(u32) -1, cbss->channel,
ssid + 2, ssid_len,
NULL, 0, true);
@@ -2155,6 +2358,24 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_ap_probereq_get);
+static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata,
+ const u8 *buf, size_t len, bool tx,
+ u16 reason)
+{
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT,
+ .u.mlme.reason = reason,
+ };
+
+ if (tx)
+ cfg80211_tx_mlme_mgmt(sdata->dev, buf, len);
+ else
+ cfg80211_rx_mlme_mgmt(sdata->dev, buf, len);
+
+ drv_event_callback(sdata->local, sdata, &event);
+}
+
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@@ -2172,6 +2393,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
true, frame_buf);
mutex_lock(&local->mtx);
sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -2179,8 +2401,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
}
mutex_unlock(&local->mtx);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+
sdata_unlock(sdata);
}
@@ -2251,9 +2474,15 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
sdata_assert_lock(sdata);
if (!assoc) {
+ /*
+ * we are not authenticated yet, the only timer that could be
+ * running is the timeout for the authentication response which
+ * which is not relevant anymore.
+ */
+ del_timer_sync(&sdata->u.mgd.timer);
sta_info_destroy_addr(sdata, auth_data->bss->bssid);
- memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
+ eth_zero_addr(sdata->u.mgd.bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
sdata->u.mgd.flags = 0;
mutex_lock(&sdata->local->mtx);
@@ -2298,6 +2527,10 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
u8 bssid[ETH_ALEN];
u16 auth_alg, auth_transaction, status_code;
struct sta_info *sta;
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = AUTH_EVENT,
+ };
sdata_assert_lock(sdata);
@@ -2330,6 +2563,9 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
mgmt->sa, status_code);
ieee80211_destroy_auth_data(sdata, false);
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+ event.u.mlme.status = MLME_DENIED;
+ event.u.mlme.reason = status_code;
+ drv_event_callback(sdata->local, sdata, &event);
return;
}
@@ -2352,6 +2588,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
return;
}
+ event.u.mlme.status = MLME_SUCCESS;
+ drv_event_callback(sdata->local, sdata, &event);
sdata_info(sdata, "authenticated\n");
ifmgd->auth_data->done = true;
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
@@ -2470,7 +2708,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
- cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+ ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
}
@@ -2496,7 +2734,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
- cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
+ ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
}
static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@ -2558,9 +2796,15 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
sdata_assert_lock(sdata);
if (!assoc) {
+ /*
+ * we are not associated yet, the only timer that could be
+ * running is the timeout for the association response which
+ * which is not relevant anymore.
+ */
+ del_timer_sync(&sdata->u.mgd.timer);
sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
- memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
+ eth_zero_addr(sdata->u.mgd.bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
sdata->u.mgd.flags = 0;
mutex_lock(&sdata->local->mtx);
@@ -2618,6 +2862,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
}
ifmgd->aid = aid;
+ ifmgd->tdls_chan_switch_prohibited =
+ elems.ext_capab && elems.ext_capab_len >= 5 &&
+ (elems.ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
/*
* Some APs are erroneously not including some information in their
@@ -2749,10 +2996,14 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
rate_control_rate_init(sta);
- if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
+ if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
set_sta_flag(sta, WLAN_STA_MFP);
+ sta->sta.mfp = true;
+ } else {
+ sta->sta.mfp = false;
+ }
- sta->sta.wme = elems.wmm_param;
+ sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
@@ -2822,6 +3073,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
u8 *pos;
bool reassoc;
struct cfg80211_bss *bss;
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = ASSOC_EVENT,
+ };
sdata_assert_lock(sdata);
@@ -2873,6 +3128,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
sdata_info(sdata, "%pM denied association (code=%d)\n",
mgmt->sa, status_code);
ieee80211_destroy_assoc_data(sdata, false);
+ event.u.mlme.status = MLME_DENIED;
+ event.u.mlme.reason = status_code;
+ drv_event_callback(sdata->local, sdata, &event);
} else {
if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */
@@ -2880,6 +3138,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
cfg80211_assoc_timeout(sdata->dev, bss);
return;
}
+ event.u.mlme.status = MLME_SUCCESS;
+ drv_event_callback(sdata->local, sdata, &event);
sdata_info(sdata, "associated\n");
/*
@@ -2984,7 +3244,8 @@ static const u64 care_about_ies =
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
(1ULL << WLAN_EID_HT_CAPABILITY) |
- (1ULL << WLAN_EID_HT_OPERATION);
+ (1ULL << WLAN_EID_HT_OPERATION) |
+ (1ULL << WLAN_EID_EXT_CHANSWITCH_ANN);
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len,
@@ -3081,6 +3342,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
int sig = ifmgd->ave_beacon_signal;
int last_sig = ifmgd->last_ave_beacon_signal;
+ struct ieee80211_event event = {
+ .type = RSSI_EVENT,
+ };
/*
* if signal crosses either of the boundaries, invoke callback
@@ -3089,12 +3353,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (sig > ifmgd->rssi_max_thold &&
(last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
ifmgd->last_ave_beacon_signal = sig;
- drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH);
+ event.u.rssi.data = RSSI_EVENT_HIGH;
+ drv_event_callback(local, sdata, &event);
} else if (sig < ifmgd->rssi_min_thold &&
(last_sig >= ifmgd->rssi_max_thold ||
last_sig == 0)) {
ifmgd->last_ave_beacon_signal = sig;
- drv_rssi_callback(local, sdata, RSSI_EVENT_LOW);
+ event.u.rssi.data = RSSI_EVENT_LOW;
+ drv_event_callback(local, sdata, &event);
}
}
@@ -3196,6 +3462,29 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
+ if (ifmgd->csa_waiting_bcn)
+ ieee80211_chswitch_post_beacon(sdata);
+
+ /*
+ * Update beacon timing and dtim count on every beacon appearance. This
+ * will allow the driver to use the most updated values. Do it before
+ * comparing this one with last received beacon.
+ * IMPORTANT: These parameters would possibly be out of sync by the time
+ * the driver will use them. The synchronized view is currently
+ * guaranteed only in certain callbacks.
+ */
+ 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;
+ }
+
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
@@ -3204,6 +3493,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
+ rx_status->device_timestamp,
&elems, true);
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
@@ -3222,18 +3512,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
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_BEACON_INFO;
ifmgd->have_beacon = true;
@@ -3264,8 +3542,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DEAUTH_LEAVING,
true, deauth_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf,
- sizeof(deauth_buf));
+ ieee80211_report_disconnect(sdata, deauth_buf,
+ sizeof(deauth_buf), true,
+ WLAN_REASON_DEAUTH_LEAVING);
return;
}
@@ -3335,8 +3614,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
break;
ieee80211_sta_process_chanswitch(sdata,
- rx_status->mactime,
- &elems, false);
+ rx_status->mactime,
+ rx_status->device_timestamp,
+ &elems, false);
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
ies_len = skb->len -
offsetof(struct ieee80211_mgmt,
@@ -3357,8 +3637,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
&mgmt->u.action.u.ext_chan_switch.data;
ieee80211_sta_process_chanswitch(sdata,
- rx_status->mactime,
- &elems, false);
+ rx_status->mactime,
+ rx_status->device_timestamp,
+ &elems, false);
}
break;
}
@@ -3381,8 +3662,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
tx, frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+ reason);
}
static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@@ -3456,7 +3737,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
* Direct probe is sent to broadcast address as some APs
* will not answer to direct packet in unassociated state.
*/
- ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
+ ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+ ssidie + 2, ssidie[1],
NULL, 0, (u32) -1, true, 0,
auth_data->bss->channel, false);
rcu_read_unlock();
@@ -3575,12 +3857,18 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
ieee80211_destroy_auth_data(sdata, false);
} else if (ieee80211_probe_auth(sdata)) {
u8 bssid[ETH_ALEN];
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = AUTH_EVENT,
+ .u.mlme.status = MLME_TIMEOUT,
+ };
memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
ieee80211_destroy_auth_data(sdata, false);
cfg80211_auth_timeout(sdata->dev, bssid);
+ drv_event_callback(sdata->local, sdata, &event);
}
} else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
run_again(sdata, ifmgd->auth_data->timeout);
@@ -3590,9 +3878,15 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
ieee80211_do_assoc(sdata)) {
struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
+ struct ieee80211_event event = {
+ .type = MLME_EVENT,
+ .u.mlme.data = ASSOC_EVENT,
+ .u.mlme.status = MLME_TIMEOUT,
+ };
ieee80211_destroy_assoc_data(sdata, false);
cfg80211_assoc_timeout(sdata->dev, bss);
+ drv_event_callback(sdata->local, sdata, &event);
}
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
run_again(sdata, ifmgd->assoc_data->timeout);
@@ -3664,12 +3958,9 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_local *local = sdata->local;
-
- if (local->quiescing)
- return;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- if (sdata->vif.csa_active)
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
sdata->u.mgd.connection_loss = false;
@@ -3684,10 +3975,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- if (local->quiescing)
- return;
-
- if (sdata->vif.csa_active)
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
@@ -3749,6 +4037,34 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
IEEE80211_DEAUTH_FRAME_LEN);
}
+ /* This is a bit of a hack - we should find a better and more generic
+ * solution to this. Normally when suspending, cfg80211 will in fact
+ * deauthenticate. However, it doesn't (and cannot) stop an ongoing
+ * auth (not so important) or assoc (this is the problem) process.
+ *
+ * As a consequence, it can happen that we are in the process of both
+ * associating and suspending, and receive an association response
+ * after cfg80211 has checked if it needs to disconnect, but before
+ * we actually set the flag to drop incoming frames. This will then
+ * cause the workqueue flush to process the association response in
+ * the suspend, resulting in a successful association just before it
+ * tries to remove the interface from the driver, which now though
+ * has a channel context assigned ... this results in issues.
+ *
+ * To work around this (for now) simply deauth here again if we're
+ * now connected.
+ */
+ if (ifmgd->associated && !sdata->local->wowlan) {
+ u8 bssid[ETH_ALEN];
+ struct cfg80211_deauth_request req = {
+ .reason_code = WLAN_REASON_DEAUTH_LEAVING,
+ .bssid = bssid,
+ };
+
+ memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
+ ieee80211_mgd_deauth(sdata, &req);
+ }
+
sdata_unlock(sdata);
}
@@ -3799,6 +4115,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
(unsigned long) sdata);
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
(unsigned long) sdata);
+ INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
+ ieee80211_sta_handle_tspec_ac_params_wk);
ifmgd->flags = 0;
ifmgd->powersave = sdata->wdev.ps;
@@ -3810,6 +4128,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
else
ifmgd->req_smps = IEEE80211_SMPS_OFF;
+
+ /* Setup TDLS data */
+ spin_lock_init(&ifmgd->teardown_lock);
+ ifmgd->teardown_skb = NULL;
+ ifmgd->orig_teardown_skb = NULL;
}
/* scan finished notification */
@@ -3990,9 +4313,13 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss *bss = (void *)cbss->priv;
struct sta_info *new_sta = NULL;
- bool have_sta = false;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_sta_ht_cap sta_ht_cap;
+ bool have_sta = false, is_override = false;
int err;
+ sband = local->hw.wiphy->bands[cbss->channel->band];
+
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
return -EINVAL;
@@ -4007,25 +4334,32 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
if (!new_sta)
return -ENOMEM;
}
+
+ memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
+ ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
+
+ is_override = (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) !=
+ (sband->ht_cap.cap &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+
+ if (new_sta || is_override) {
+ err = ieee80211_prep_channel(sdata, cbss);
+ if (err) {
+ if (new_sta)
+ sta_info_free(local, new_sta);
+ return -EINVAL;
+ }
+ }
+
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_chanctx_conf *chanctx_conf;
- struct ieee80211_supported_band *sband;
const struct cfg80211_bss_ies *ies;
- int shift;
+ int shift = ieee80211_vif_get_shift(&sdata->vif);
u32 rate_flags;
- sband = local->hw.wiphy->bands[cbss->channel->band];
-
- err = ieee80211_prep_channel(sdata, cbss);
- if (err) {
- sta_info_free(local, new_sta);
- return -EINVAL;
- }
- shift = ieee80211_vif_get_shift(&sdata->vif);
-
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
@@ -4119,6 +4453,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
} else
WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid));
+ /* Cancel scan to ensure that nothing interferes with connection */
+ if (local->scanning)
+ ieee80211_scan_cancel(local);
+
return 0;
}
@@ -4207,8 +4545,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
WLAN_REASON_UNSPECIFIED,
false, frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- sizeof(frame_buf));
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ WLAN_REASON_UNSPECIFIED);
}
sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@@ -4228,7 +4567,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
return 0;
err_clear:
- memset(ifmgd->bssid, 0, ETH_ALEN);
+ eth_zero_addr(ifmgd->bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
ifmgd->auth_data = NULL;
err_free:
@@ -4308,8 +4647,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
WLAN_REASON_UNSPECIFIED,
false, frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- sizeof(frame_buf));
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ WLAN_REASON_UNSPECIFIED);
}
if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@@ -4461,8 +4801,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
rcu_read_unlock();
+ if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) &&
+ (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK),
+ "U-APSD not supported with HW_PS_NULLFUNC_STACK\n"))
+ sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+
if (bss->wmm_used && bss->uapsd_supported &&
- (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
+ (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) {
assoc_data->uapsd = true;
ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
} else {
@@ -4566,7 +4911,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
return 0;
err_clear:
- memset(ifmgd->bssid, 0, ETH_ALEN);
+ eth_zero_addr(ifmgd->bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
ifmgd->assoc_data = NULL;
err_free:
@@ -4594,8 +4939,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
req->reason_code, tx,
frame_buf);
ieee80211_destroy_auth_data(sdata, false);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ req->reason_code);
return 0;
}
@@ -4609,8 +4955,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
req->reason_code, tx, frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf,
+ sizeof(frame_buf), true,
+ req->reason_code);
return 0;
}
@@ -4642,8 +4989,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
req->reason_code, !req->local_state_change,
frame_buf);
- cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
+ req->reason_code);
return 0;
}
@@ -4672,6 +5019,13 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
}
if (ifmgd->auth_data)
ieee80211_destroy_auth_data(sdata, false);
+ spin_lock_bh(&ifmgd->teardown_lock);
+ if (ifmgd->teardown_skb) {
+ kfree_skb(ifmgd->teardown_skb);
+ ifmgd->teardown_skb = NULL;
+ ifmgd->orig_teardown_skb = NULL;
+ }
+ spin_unlock_bh(&ifmgd->teardown_lock);
del_timer_sync(&ifmgd->timer);
sdata_unlock(sdata);
}
@@ -4687,3 +5041,13 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
}
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
+
+void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ trace_api_cqm_beacon_loss_notify(sdata->local, sdata);
+
+ cfg80211_cqm_beacon_loss_notify(sdata->dev, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify);