Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-next-2.6
[safe/jmp/linux-2.6] / net / mac80211 / mlme.c
index 32d6e66..bfc4a50 100644 (file)
 #include "rate.h"
 #include "led.h"
 
-#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
-#define IEEE80211_AUTH_MAX_TRIES 3
-#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
-#define IEEE80211_ASSOC_MAX_TRIES 3
 #define IEEE80211_MAX_PROBE_TRIES 5
 
 /*
@@ -249,30 +245,15 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
 void ieee80211_send_pspoll(struct ieee80211_local *local,
                           struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_pspoll *pspoll;
        struct sk_buff *skb;
-       u16 fc;
 
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*pspoll));
-       if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for "
-                      "pspoll frame\n", sdata->name);
+       skb = ieee80211_pspoll_get(&local->hw, &sdata->vif);
+       if (!skb)
                return;
-       }
-       skb_reserve(skb, local->hw.extra_tx_headroom);
-
-       pspoll = (struct ieee80211_pspoll *) skb_put(skb, sizeof(*pspoll));
-       memset(pspoll, 0, sizeof(*pspoll));
-       fc = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL | IEEE80211_FCTL_PM;
-       pspoll->frame_control = cpu_to_le16(fc);
-       pspoll->aid = cpu_to_le16(ifmgd->aid);
 
-       /* aid in PS-Poll has its two MSBs each set to 1 */
-       pspoll->aid |= cpu_to_le16(1 << 15 | 1 << 14);
-
-       memcpy(pspoll->bssid, ifmgd->bssid, ETH_ALEN);
-       memcpy(pspoll->ta, sdata->vif.addr, ETH_ALEN);
+       pspoll = (struct ieee80211_pspoll *) skb->data;
+       pspoll->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
        ieee80211_tx_skb(sdata, skb);
@@ -283,30 +264,47 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
                             int powersave)
 {
        struct sk_buff *skb;
+       struct ieee80211_hdr_3addr *nullfunc;
+
+       skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif);
+       if (!skb)
+               return;
+
+       nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
+       if (powersave)
+               nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       ieee80211_tx_skb(sdata, skb);
+}
+
+static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+                                         struct ieee80211_sub_if_data *sdata)
+{
+       struct sk_buff *skb;
        struct ieee80211_hdr *nullfunc;
        __le16 fc;
 
        if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
                return;
 
-       skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30);
        if (!skb) {
-               printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
-                      "frame\n", sdata->name);
+               printk(KERN_DEBUG "%s: failed to allocate buffer for 4addr "
+                      "nullfunc frame\n", sdata->name);
                return;
        }
        skb_reserve(skb, local->hw.extra_tx_headroom);
 
-       nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
-       memset(nullfunc, 0, 24);
+       nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30);
+       memset(nullfunc, 0, 30);
        fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
-                        IEEE80211_FCTL_TODS);
-       if (powersave)
-               fc |= cpu_to_le16(IEEE80211_FCTL_PM);
+                        IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
        nullfunc->frame_control = fc;
        memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
        memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
        memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
+       memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN);
 
        IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
        ieee80211_tx_skb(sdata, skb);
@@ -436,8 +434,11 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
        } else {
                if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
                        ieee80211_send_nullfunc(local, sdata, 1);
-               conf->flags |= IEEE80211_CONF_PS;
-               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+
+               if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+                       conf->flags |= IEEE80211_CONF_PS;
+                       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+               }
        }
 }
 
@@ -482,6 +483,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
 
        if (count == 1 && found->u.mgd.powersave &&
            found->u.mgd.associated &&
+           found->u.mgd.associated->beacon_ies &&
            !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
                                    IEEE80211_STA_CONNECTION_POLL))) {
                s32 beaconint_us;
@@ -495,14 +497,22 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                if (beaconint_us > latency) {
                        local->ps_sdata = NULL;
                } else {
-                       u8 dtimper = found->vif.bss_conf.dtim_period;
+                       struct ieee80211_bss *bss;
                        int maxslp = 1;
+                       u8 dtimper;
 
-                       if (dtimper > 1)
+                       bss = (void *)found->u.mgd.associated->priv;
+                       dtimper = bss->dtim_period;
+
+                       /* If the TIM IE is invalid, pretend the value is 1 */
+                       if (!dtimper)
+                               dtimper = 1;
+                       else if (dtimper > 1)
                                maxslp = min_t(int, dtimper,
                                                    latency / beaconint_us);
 
                        local->hw.conf.max_sleep_period = maxslp;
+                       local->hw.conf.ps_dtim_period = dtimper;
                        local->ps_sdata = found;
                }
        } else {
@@ -534,6 +544,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                container_of(work, struct ieee80211_local,
                             dynamic_ps_enable_work);
        struct ieee80211_sub_if_data *sdata = local->ps_sdata;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        /* can only happen when PS was just disabled anyway */
        if (!sdata)
@@ -542,11 +553,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
        if (local->hw.conf.flags & IEEE80211_CONF_PS)
                return;
 
-       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+       if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
+           (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
                ieee80211_send_nullfunc(local, sdata, 1);
 
-       local->hw.conf.flags |= IEEE80211_CONF_PS;
-       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ||
+           (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
+               ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
+               local->hw.conf.flags |= IEEE80211_CONF_PS;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       }
 }
 
 void ieee80211_dynamic_ps_timer(unsigned long data)
@@ -567,7 +583,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
        struct ieee80211_tx_queue_params params;
        size_t left;
        int count;
-       u8 *pos;
+       u8 *pos, uapsd_queues = 0;
 
        if (local->hw.queues < 4)
                return;
@@ -577,6 +593,10 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
 
        if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
                return;
+
+       if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
+               uapsd_queues = local->uapsd_queues;
+
        count = wmm_param[6] & 0x0f;
        if (count == ifmgd->wmm_last_param_set)
                return;
@@ -591,6 +611,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
        for (; left >= 4; left -= 4, pos += 4) {
                int aci = (pos[0] >> 5) & 0x03;
                int acm = (pos[0] >> 4) & 0x01;
+               bool uapsd = false;
                int queue;
 
                switch (aci) {
@@ -598,22 +619,30 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                        queue = 3;
                        if (acm)
                                local->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
+                       if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
+                               uapsd = true;
                        break;
                case 2: /* AC_VI */
                        queue = 1;
                        if (acm)
                                local->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
+                       if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
+                               uapsd = true;
                        break;
                case 3: /* AC_VO */
                        queue = 0;
                        if (acm)
                                local->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
+                       if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+                               uapsd = true;
                        break;
                case 0: /* AC_BE */
                default:
                        queue = 2;
                        if (acm)
                                local->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
+                       if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+                               uapsd = true;
                        break;
                }
 
@@ -621,11 +650,14 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
                params.cw_min = ecw2cw(pos[1] & 0x0f);
                params.txop = get_unaligned_le16(pos + 2);
+               params.uapsd = uapsd;
+
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d "
-                      "cWmin=%d cWmax=%d txop=%d\n",
+                      "cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
                       wiphy_name(local->hw.wiphy), queue, aci, acm,
-                      params.aifs, params.cw_min, params.cw_max, params.txop);
+                      params.aifs, params.cw_min, params.cw_max, params.txop,
+                      params.uapsd);
 #endif
                if (drv_conf_tx(local, queue, &params) && local->ops->conf_tx)
                        printk(KERN_DEBUG "%s: failed to set TX queue "
@@ -652,6 +684,8 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
        }
 
        use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
+       if (sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ)
+               use_short_slot = true;
 
        if (use_protection != bss_conf->use_cts_prot) {
                bss_conf->use_cts_prot = use_protection;
@@ -682,7 +716,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        /* set timing information */
        sdata->vif.bss_conf.beacon_int = cbss->beacon_interval;
        sdata->vif.bss_conf.timestamp = cbss->tsf;
-       sdata->vif.bss_conf.dtim_period = bss->dtim_period;
 
        bss_info_changed |= BSS_CHANGED_BEACON_INT;
        bss_info_changed |= ieee80211_handle_bss_capability(sdata,
@@ -723,7 +756,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        ieee80211_recalc_smps(local, sdata);
        mutex_unlock(&local->iflist_mtx);
 
-       netif_start_queue(sdata->dev);
+       netif_tx_start_all_queues(sdata->dev);
        netif_carrier_on(sdata->dev);
 }
 
@@ -759,13 +792,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
         * time -- we don't want the scan code to enable queues.
         */
 
-       netif_stop_queue(sdata->dev);
+       netif_tx_stop_all_queues(sdata->dev);
        netif_carrier_off(sdata->dev);
 
        rcu_read_lock();
        sta = sta_info_get(sdata, bssid);
-       if (sta)
+       if (sta) {
+               set_sta_flags(sta, WLAN_STA_DISASSOC);
                ieee80211_sta_tear_down_BA_sessions(sta);
+       }
        rcu_read_unlock();
 
        changed |= ieee80211_reset_erp_info(sdata);
@@ -798,19 +833,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
        changed |= BSS_CHANGED_BSSID;
        ieee80211_bss_info_change_notify(sdata, changed);
 
-       rcu_read_lock();
-
-       sta = sta_info_get(sdata, bssid);
-       if (!sta) {
-               rcu_read_unlock();
-               return;
-       }
-
-       sta_info_unlink(&sta);
-
-       rcu_read_unlock();
-
-       sta_info_destroy(sta);
+       sta_info_destroy_addr(sdata, bssid);
 }
 
 void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1096,7 +1119,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
        if (err) {
                printk(KERN_DEBUG "%s: failed to insert STA entry for"
                       " the AP (error %d)\n", sdata->name, err);
-               return RX_MGMT_CFG80211_ASSOC_ERROR;
+               return false;
        }
 
        if (elems.wmm_param)
@@ -1105,6 +1128,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
        else
                ieee80211_set_wmm_default(sdata);
 
+       local->oper_channel = wk->chan;
+
        if (elems.ht_info_elem && elems.wmm_param &&
            (sdata->local->hw.queues >= 4) &&
            !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
@@ -1118,6 +1143,13 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
        ieee80211_set_associated(sdata, cbss, changed);
 
        /*
+        * If we're using 4-addr mode, let the AP know that we're
+        * doing so, so that it can create the STA VLAN on its side
+        */
+       if (ifmgd->use_4addr)
+               ieee80211_send_4addr_nullfunc(local, sdata);
+
+       /*
         * Start timer to probe the connection to the AP now.
         * Also start the timer that will detect beacon loss.
         */
@@ -1139,6 +1171,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        int freq;
        struct ieee80211_bss *bss;
        struct ieee80211_channel *channel;
+       bool need_ps = false;
+
+       if (sdata->u.mgd.associated) {
+               bss = (void *)sdata->u.mgd.associated->priv;
+               /* not previously set so we may need to recalc */
+               need_ps = !bss->dtim_period;
+       }
 
        if (elems->ds_params && elems->ds_params_len == 1)
                freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
@@ -1158,6 +1197,12 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        if (!sdata->u.mgd.associated)
                return;
 
+       if (need_ps) {
+               mutex_lock(&local->iflist_mtx);
+               ieee80211_recalc_ps(local, -1);
+               mutex_unlock(&local->iflist_mtx);
+       }
+
        if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
            (memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid,
                                                        ETH_ALEN) == 0)) {
@@ -1449,7 +1494,9 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                        rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
                        break;
                case IEEE80211_STYPE_ACTION:
-                       /* XXX: differentiate, can only happen for CSA now! */
+                       if (mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
+                               break;
+
                        ieee80211_sta_process_chanswitch(sdata,
                                        &mgmt->u.action.u.chan_switch.sw_elem,
                                        (void *)ifmgd->associated->priv);
@@ -1772,7 +1819,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        if (!wk)
                return -ENOMEM;
 
-       memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);;
+       memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);
 
        if (req->ie && req->ie_len) {
                memcpy(wk->ie, req->ie, req->ie_len);
@@ -1792,20 +1839,15 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        wk->probe_auth.algorithm = auth_alg;
        wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
 
-       wk->type = IEEE80211_WORK_DIRECT_PROBE;
+       /* if we already have a probe, don't probe again */
+       if (req->bss->proberesp_ies)
+               wk->type = IEEE80211_WORK_AUTH;
+       else
+               wk->type = IEEE80211_WORK_DIRECT_PROBE;
        wk->chan = req->bss->channel;
        wk->sdata = sdata;
        wk->done = ieee80211_probe_auth_done;
 
-       /*
-        * XXX: if still associated need to tell AP that we're going
-        *      to sleep and then change channel etc.
-        *      For now switch channel here, later will be handled
-        *      by submitting this as an off-channel work item.
-        */
-       sdata->local->oper_channel = req->bss->channel;
-       ieee80211_hw_config(sdata->local, 0);
-
        ieee80211_add_work(wk);
        return 0;
 }
@@ -1861,6 +1903,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                return -ENOMEM;
 
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
+       ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
 
        for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
                if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
@@ -1904,6 +1947,15 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        wk->assoc.ht_information_ie =
                ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
 
+       if (bss->wmm_used && bss->uapsd_supported &&
+           (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
+               wk->assoc.uapsd_used = true;
+               ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
+       } else {
+               wk->assoc.uapsd_used = false;
+               ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
+       }
+
        ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
        memcpy(wk->assoc.ssid, ssid + 2, ssid[1]);
        wk->assoc.ssid_len = ssid[1];
@@ -1929,9 +1981,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        else
                ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
 
-       sdata->local->oper_channel = req->bss->channel;
-       ieee80211_hw_config(sdata->local, 0);
-
        ieee80211_add_work(wk);
        return 0;
 }
@@ -1958,12 +2007,18 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
                mutex_lock(&local->work_mtx);
                list_for_each_entry(wk, &local->work_list, list) {
-                       if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
+                       if (wk->sdata != sdata)
                                continue;
+
+                       if (wk->type != IEEE80211_WORK_DIRECT_PROBE &&
+                           wk->type != IEEE80211_WORK_AUTH)
+                               continue;
+
                        if (memcmp(req->bss->bssid, wk->filter_ta, ETH_ALEN))
                                continue;
-                       not_auth_yet = true;
-                       list_del(&wk->list);
+
+                       not_auth_yet = wk->type == IEEE80211_WORK_DIRECT_PROBE;
+                       list_del_rcu(&wk->list);
                        free_work(wk);
                        break;
                }