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.c104
1 files changed, 84 insertions, 20 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index d6470c7fd6ce..cd37a4e3c0d7 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -160,7 +160,8 @@ static int ecw2cw(int ecw)
*/
static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
struct ieee80211_ht_info *hti,
- const u8 *bssid, u16 ap_ht_cap_flags)
+ const u8 *bssid, u16 ap_ht_cap_flags,
+ bool beacon_htcap_ie)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
@@ -232,6 +233,21 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type));
}
+ if (beacon_htcap_ie && (prev_chantype != channel_type)) {
+ /*
+ * Whenever the AP announces the HT mode change that can be
+ * 40MHz intolerant or etc., it would be safer to stop tx
+ * queues before doing hw config to avoid buffer overflow.
+ */
+ ieee80211_stop_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
+
+ /* flush out all packets */
+ synchronize_net();
+
+ drv_flush(local, false);
+ }
+
/* channel_type change automatically detected */
ieee80211_hw_config(local, 0);
@@ -243,6 +259,10 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
IEEE80211_RC_HT_CHANGED,
channel_type);
rcu_read_unlock();
+
+ if (beacon_htcap_ie)
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE);
}
ht_opmode = le16_to_cpu(hti->operation_mode);
@@ -271,11 +291,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for "
- "deauth/disassoc frame\n", sdata->name);
+ if (!skb)
return;
- }
+
skb_reserve(skb, local->hw.extra_tx_headroom);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
@@ -354,11 +372,9 @@ static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
return;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30);
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for 4addr "
- "nullfunc frame\n", sdata->name);
+ if (!skb)
return;
- }
+
skb_reserve(skb, local->hw.extra_tx_headroom);
nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30);
@@ -394,6 +410,9 @@ static void ieee80211_chswitch_work(struct work_struct *work)
/* call "hw_config" only if doing sw channel switch */
ieee80211_hw_config(sdata->local,
IEEE80211_CONF_CHANGE_CHANNEL);
+ } else {
+ /* update the device channel directly */
+ sdata->local->hw.conf.channel = sdata->local->oper_channel;
}
/* XXX: shouldn't really modify cfg80211-owned data! */
@@ -917,8 +936,8 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
params.aifs, params.cw_min, params.cw_max,
params.txop, params.uapsd);
#endif
- local->tx_conf[queue] = params;
- if (drv_conf_tx(local, queue, &params))
+ sdata->tx_conf[queue] = params;
+ if (drv_conf_tx(local, sdata, queue, &params))
wiphy_debug(local->hw.wiphy,
"failed to set TX queue parameters for queue %d\n",
queue);
@@ -1220,7 +1239,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
} else {
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0,
- (u32) -1, true);
+ (u32) -1, true, false);
}
ifmgd->probe_send_count++;
@@ -1482,10 +1501,14 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
ifmgd->aid = aid;
- sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
- if (!sta) {
- printk(KERN_DEBUG "%s: failed to alloc STA entry for"
- " the AP\n", sdata->name);
+ mutex_lock(&sdata->local->sta_mtx);
+ /*
+ * station info was already allocated and inserted before
+ * the association and should be available to us
+ */
+ sta = sta_info_get_rx(sdata, cbss->bssid);
+ if (WARN_ON(!sta)) {
+ mutex_unlock(&sdata->local->sta_mtx);
return false;
}
@@ -1556,7 +1579,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
if (elems.wmm_param)
set_sta_flags(sta, WLAN_STA_WME);
- err = sta_info_insert(sta);
+ /* sta_info_reinsert will also unlock the mutex lock */
+ err = sta_info_reinsert(sta);
sta = NULL;
if (err) {
printk(KERN_DEBUG "%s: failed to insert STA entry for"
@@ -1584,7 +1608,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
(sdata->local->hw.queues >= 4) &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- cbss->bssid, ap_ht_cap_flags);
+ cbss->bssid, ap_ht_cap_flags,
+ false);
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
@@ -1918,7 +1943,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- bssid, ap_ht_cap_flags);
+ bssid, ap_ht_cap_flags, true);
}
/* Note: country IE parsing is done for us by cfg80211 */
@@ -2429,6 +2454,29 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
return 0;
}
+/* create and insert a dummy station entry */
+static int ieee80211_pre_assoc(struct ieee80211_sub_if_data *sdata,
+ u8 *bssid) {
+ struct sta_info *sta;
+ int err;
+
+ sta = sta_info_alloc(sdata, bssid, GFP_KERNEL);
+ if (!sta)
+ return -ENOMEM;
+
+ sta->dummy = true;
+
+ err = sta_info_insert(sta);
+ sta = NULL;
+ if (err) {
+ printk(KERN_DEBUG "%s: failed to insert Dummy STA entry for"
+ " the AP (error %d)\n", sdata->name, err);
+ return err;
+ }
+
+ return 0;
+}
+
static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
struct sk_buff *skb)
{
@@ -2436,9 +2484,11 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
struct ieee80211_mgmt *mgmt;
struct ieee80211_rx_status *rx_status;
struct ieee802_11_elems elems;
+ struct cfg80211_bss *cbss = wk->assoc.bss;
u16 status;
if (!skb) {
+ sta_info_destroy_addr(wk->sdata, cbss->bssid);
cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
goto destroy;
}
@@ -2468,12 +2518,16 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
mutex_unlock(&wk->sdata->u.mgd.mtx);
/* oops -- internal error -- send timeout for now */
+ sta_info_destroy_addr(wk->sdata, cbss->bssid);
cfg80211_send_assoc_timeout(wk->sdata->dev,
wk->filter_ta);
return WORK_DONE_DESTROY;
}
mutex_unlock(&wk->sdata->u.mgd.mtx);
+ } else {
+ /* assoc failed - destroy the dummy station entry */
+ sta_info_destroy_addr(wk->sdata, cbss->bssid);
}
cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
@@ -2492,7 +2546,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss *bss = (void *)req->bss->priv;
struct ieee80211_work *wk;
const u8 *ssid;
- int i;
+ int i, err;
mutex_lock(&ifmgd->mtx);
if (ifmgd->associated) {
@@ -2517,6 +2571,16 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (!wk)
return -ENOMEM;
+ /*
+ * create a dummy station info entry in order
+ * to start accepting incoming EAPOL packets from the station
+ */
+ err = ieee80211_pre_assoc(sdata, req->bss->bssid);
+ if (err) {
+ kfree(wk);
+ return err;
+ }
+
ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;