]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/mac80211/mlme.c
mac80211: clean up BA session teardown
[linux-2.6-omap-h63xx.git] / net / mac80211 / mlme.c
index ec400479c5f611b32e723087e80e4f7589a72f23..bfc47b33068740a643d7d2cbb45b1f02f13f76e5 100644 (file)
@@ -511,6 +511,39 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED);
 }
 
+void ieee80211_send_pspoll(struct ieee80211_local *local,
+                          struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+       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->dev->name);
+               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(ifsta->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, ifsta->bssid, ETH_ALEN);
+       memcpy(pspoll->ta, sdata->dev->dev_addr, ETH_ALEN);
+
+       ieee80211_tx_skb(sdata, skb, 0);
+
+       return;
+}
+
 /* MLME */
 static void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
                                         struct ieee80211_bss *bss)
@@ -611,7 +644,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
        }
 }
 
-static bool check_tim(struct ieee802_11_elems *elems, u16 aid, bool *is_mc)
+static bool ieee80211_check_tim(struct ieee802_11_elems *elems, u16 aid)
 {
        u8 mask;
        u8 index, indexn1, indexn2;
@@ -621,9 +654,6 @@ static bool check_tim(struct ieee802_11_elems *elems, u16 aid, bool *is_mc)
        index = aid / 8;
        mask  = 1 << (aid & 7);
 
-       if (tim->bitmap_ctrl & 0x01)
-               *is_mc = true;
-
        indexn1 = tim->bitmap_ctrl & 0xfe;
        indexn2 = elems->tim_len + indexn1 - 4;
 
@@ -840,6 +870,14 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
                       sdata->dev->name, ifsta->bssid);
                ifsta->state = IEEE80211_STA_MLME_DISABLED;
                ieee80211_sta_send_apinfo(sdata, ifsta);
+
+               /*
+                * Most likely AP is not in the range so remove the
+                * bss information associated to the AP
+                */
+               ieee80211_rx_bss_remove(sdata, ifsta->bssid,
+                               sdata->local->hw.conf.channel->center_freq,
+                               ifsta->ssid, ifsta->ssid_len);
                return;
        }
 
@@ -871,6 +909,9 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
                       sdata->dev->name, ifsta->bssid);
                ifsta->state = IEEE80211_STA_MLME_DISABLED;
                ieee80211_sta_send_apinfo(sdata, ifsta);
+               ieee80211_rx_bss_remove(sdata, ifsta->bssid,
+                               sdata->local->hw.conf.channel->center_freq,
+                               ifsta->ssid, ifsta->ssid_len);
                return;
        }
 
@@ -913,7 +954,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        netif_tx_stop_all_queues(sdata->dev);
        netif_carrier_off(sdata->dev);
 
-       ieee80211_sta_tear_down_BA_sessions(sdata, sta->sta.addr);
+       ieee80211_sta_tear_down_BA_sessions(sta);
 
        if (self_disconnected) {
                if (deauth)
@@ -933,8 +974,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_sta_send_apinfo(sdata, ifsta);
 
-       if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT)
+       if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) {
                ifsta->state = IEEE80211_STA_MLME_DISABLED;
+               ieee80211_rx_bss_remove(sdata, ifsta->bssid,
+                               sdata->local->hw.conf.channel->center_freq,
+                               ifsta->ssid, ifsta->ssid_len);
+       }
 
        rcu_read_unlock();
 
@@ -1017,6 +1062,9 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
                       sdata->dev->name, ifsta->bssid);
                ifsta->state = IEEE80211_STA_MLME_DISABLED;
                ieee80211_sta_send_apinfo(sdata, ifsta);
+               ieee80211_rx_bss_remove(sdata, ifsta->bssid,
+                               sdata->local->hw.conf.channel->center_freq,
+                               ifsta->ssid, ifsta->ssid_len);
                return;
        }
 
@@ -1503,13 +1551,22 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                                   struct ieee80211_bss *bss)
 {
        struct ieee80211_local *local = sdata->local;
-       int res, rates, i, j;
+       int res = 0, rates, i, j;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        u8 *pos;
        struct ieee80211_supported_band *sband;
        union iwreq_data wrqu;
 
+       if (local->ops->reset_tsf) {
+               /* Reset own TSF to allow time synchronization work. */
+               local->ops->reset_tsf(local_to_hw(local));
+       }
+
+       if ((ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) &&
+          memcmp(ifsta->bssid, bss->bssid, ETH_ALEN) == 0)
+               return res;
+
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
                            sdata->u.sta.ie_proberesp_len);
        if (!skb) {
@@ -1520,13 +1577,11 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
-       /* Remove possible STA entries from other IBSS networks. */
-       sta_info_flush_delayed(sdata);
-
-       if (local->ops->reset_tsf) {
-               /* Reset own TSF to allow time synchronization work. */
-               local->ops->reset_tsf(local_to_hw(local));
+       if (!(ifsta->flags & IEEE80211_STA_PREV_BSSID_SET)) {
+               /* Remove possible STA entries from other IBSS networks. */
+               sta_info_flush_delayed(sdata);
        }
+
        memcpy(ifsta->bssid, bss->bssid, ETH_ALEN);
        res = ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
        if (res)
@@ -1615,6 +1670,7 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_sta_def_wmm_params(sdata, bss);
 
+       ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
        ifsta->state = IEEE80211_STA_MLME_IBSS_JOINED;
        mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
 
@@ -1814,7 +1870,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
        struct ieee802_11_elems elems;
        struct ieee80211_local *local = sdata->local;
        u32 changed = 0;
-       bool erp_valid, directed_tim, is_mc = false;
+       bool erp_valid, directed_tim;
        u8 erp_value = 0;
 
        /* Process beacon from the current BSS */
@@ -1842,12 +1898,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
 
        if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK &&
            local->hw.conf.flags & IEEE80211_CONF_PS) {
-               directed_tim = check_tim(&elems, ifsta->aid, &is_mc);
-
-               if (directed_tim || is_mc) {
-                       local->hw.conf.flags &= ~IEEE80211_CONF_PS;
-                       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-                       ieee80211_send_nullfunc(local, sdata, 0);
+               directed_tim = ieee80211_check_tim(&elems, ifsta->aid);
+
+               if (directed_tim) {
+                       if (local->hw.conf.dynamic_ps_timeout > 0) {
+                               local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+                               ieee80211_hw_config(local,
+                                                   IEEE80211_CONF_CHANGE_PS);
+                               ieee80211_send_nullfunc(local, sdata, 0);
+                       } else {
+                               local->pspolling = true;
+
+                               /*
+                                * Here is assumed that the driver will be
+                                * able to send ps-poll frame and receive a
+                                * response even though power save mode is
+                                * enabled, but some drivers might require
+                                * to disable power save here. This needs
+                                * to be investigated.
+                                */
+                               ieee80211_send_pspoll(local, sdata);
+                       }
                }
        }
 
@@ -2178,19 +2249,18 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
        int i;
        int ret;
 
-#if 0
-       /* Easier testing, use fixed BSSID. */
-       memset(bssid, 0xfe, ETH_ALEN);
-#else
-       /* Generate random, not broadcast, locally administered BSSID. Mix in
-        * own MAC address to make sure that devices that do not have proper
-        * random number generator get different BSSID. */
-       get_random_bytes(bssid, ETH_ALEN);
-       for (i = 0; i < ETH_ALEN; i++)
-               bssid[i] ^= sdata->dev->dev_addr[i];
-       bssid[0] &= ~0x01;
-       bssid[0] |= 0x02;
-#endif
+       if (sdata->u.sta.flags & IEEE80211_STA_BSSID_SET) {
+               memcpy(bssid, ifsta->bssid, ETH_ALEN);
+       } else {
+               /* Generate random, not broadcast, locally administered BSSID. Mix in
+                * own MAC address to make sure that devices that do not have proper
+                * random number generator get different BSSID. */
+               get_random_bytes(bssid, ETH_ALEN);
+               for (i = 0; i < ETH_ALEN; i++)
+                       bssid[i] ^= sdata->dev->dev_addr[i];
+               bssid[0] &= ~0x01;
+               bssid[0] |= 0x02;
+       }
 
        printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n",
               sdata->dev->name, bssid);
@@ -2251,6 +2321,9 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
                    memcmp(ifsta->ssid, bss->ssid, bss->ssid_len) != 0
                    || !(bss->capability & WLAN_CAPABILITY_IBSS))
                        continue;
+               if ((ifsta->flags & IEEE80211_STA_BSSID_SET) &&
+                   memcmp(ifsta->bssid, bss->bssid, ETH_ALEN) != 0)
+                       continue;
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
                printk(KERN_DEBUG "   bssid=%pM found\n", bss->bssid);
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
@@ -2267,7 +2340,9 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
                       "%pM\n", bssid, ifsta->bssid);
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
 
-       if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
+       if (found &&
+           ((!(ifsta->flags & IEEE80211_STA_PREV_BSSID_SET)) ||
+            memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0)) {
                int ret;
                int search_freq;
 
@@ -2410,8 +2485,10 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
                                                         ifsta->ssid_len);
                        ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
                        set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
-               } else
+               } else {
+                       ifsta->assoc_scan_tries = 0;
                        ifsta->state = IEEE80211_STA_MLME_DISABLED;
+               }
        }
        return -1;
 }
@@ -2605,16 +2682,16 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size
                memset(ifsta->ssid, 0, sizeof(ifsta->ssid));
                memcpy(ifsta->ssid, ssid, len);
                ifsta->ssid_len = len;
-               ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
        }
 
+       ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+
        if (len)
                ifsta->flags |= IEEE80211_STA_SSID_SET;
        else
                ifsta->flags &= ~IEEE80211_STA_SSID_SET;
 
-       if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
-           !(ifsta->flags & IEEE80211_STA_BSSID_SET)) {
+       if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
                ifsta->ibss_join_req = jiffies;
                ifsta->state = IEEE80211_STA_MLME_IBSS_SEARCH;
                return ieee80211_sta_find_ibss(sdata, ifsta);
@@ -2634,36 +2711,25 @@ int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size
 int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
 {
        struct ieee80211_if_sta *ifsta;
-       int res;
-       bool valid;
 
        ifsta = &sdata->u.sta;
-       valid = is_valid_ether_addr(bssid);
 
-       if (memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) {
-               if(valid)
-                       memcpy(ifsta->bssid, bssid, ETH_ALEN);
-               else
-                       memset(ifsta->bssid, 0, ETH_ALEN);
-               res = 0;
-               /*
-                * Hack! See also ieee80211_sta_set_ssid.
-                */
-               if (netif_running(sdata->dev))
-                       res = ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
-               if (res) {
+       if (is_valid_ether_addr(bssid)) {
+               memcpy(ifsta->bssid, bssid, ETH_ALEN);
+               ifsta->flags |= IEEE80211_STA_BSSID_SET;
+       } else {
+               memset(ifsta->bssid, 0, ETH_ALEN);
+               ifsta->flags &= ~IEEE80211_STA_BSSID_SET;
+       }
+
+       if (netif_running(sdata->dev)) {
+               if (ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID)) {
                        printk(KERN_DEBUG "%s: Failed to config new BSSID to "
                               "the low-level driver\n", sdata->dev->name);
-                       return res;
                }
        }
 
-       if (valid)
-               ifsta->flags |= IEEE80211_STA_BSSID_SET;
-       else
-               ifsta->flags &= ~IEEE80211_STA_BSSID_SET;
-
-       return 0;
+       return ieee80211_sta_set_ssid(sdata, ifsta->ssid, ifsta->ssid_len);
 }
 
 int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, char *ie, size_t len)
@@ -2726,9 +2792,8 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
 
        if (sdata && sdata->vif.type == NL80211_IFTYPE_ADHOC) {
                ifsta = &sdata->u.sta;
-               if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) ||
-                   (!(ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED) &&
-                   !ieee80211_sta_active_ibss(sdata)))
+               if ((!(ifsta->flags & IEEE80211_STA_PREV_BSSID_SET)) ||
+                   !ieee80211_sta_active_ibss(sdata))
                        ieee80211_sta_find_ibss(sdata, ifsta);
        }