mac80211: Add WME information element for IBSS
[cascardo/linux.git] / net / mac80211 / ibss.c
index d4e84b2..ff60c02 100644 (file)
@@ -43,6 +43,8 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
 {
        u16 auth_alg, auth_transaction, status_code;
 
+       lockdep_assert_held(&sdata->u.ibss.mtx);
+
        if (len < 24 + 6)
                return;
 
@@ -78,6 +80,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        u32 bss_change;
        u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
 
+       lockdep_assert_held(&ifibss->mtx);
+
        /* Reset own TSF to allow time synchronization work. */
        drv_reset_tsf(local);
 
@@ -169,6 +173,19 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                memcpy(skb_put(skb, ifibss->ie_len),
                       ifibss->ie, ifibss->ie_len);
 
+       if (local->hw.queues >= 4) {
+               pos = skb_put(skb, 9);
+               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+               *pos++ = 7; /* len */
+               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+               *pos++ = 0x50;
+               *pos++ = 0xf2;
+               *pos++ = 2; /* WME */
+               *pos++ = 0; /* WME info */
+               *pos++ = 1; /* WME ver */
+               *pos++ = 0; /* U-APSD no in use */
+       }
+
        rcu_assign_pointer(ifibss->presp, skb);
 
        sdata->vif.bss_conf.beacon_int = beacon_int;
@@ -205,6 +222,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
        int i, j;
        u16 beacon_int = cbss->beacon_interval;
 
+       lockdep_assert_held(&sdata->u.ibss.mtx);
+
        if (beacon_int < 10)
                beacon_int = 10;
 
@@ -260,37 +279,45 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
        if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
                return;
 
-       if (sdata->vif.type == NL80211_IFTYPE_ADHOC && elems->supp_rates &&
+       if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
            memcmp(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) {
-               supp_rates = ieee80211_sta_get_rates(local, elems, band);
 
                rcu_read_lock();
-
                sta = sta_info_get(sdata, mgmt->sa);
-               if (sta) {
-                       u32 prev_rates;
 
-                       prev_rates = sta->sta.supp_rates[band];
-                       /* make sure mandatory rates are always added */
-                       sta->sta.supp_rates[band] = supp_rates |
-                               ieee80211_mandatory_rates(local, band);
+               if (elems->supp_rates) {
+                       supp_rates = ieee80211_sta_get_rates(local, elems,
+                                                            band);
+                       if (sta) {
+                               u32 prev_rates;
+
+                               prev_rates = sta->sta.supp_rates[band];
+                               /* make sure mandatory rates are always added */
+                               sta->sta.supp_rates[band] = supp_rates |
+                                       ieee80211_mandatory_rates(local, band);
 
-                       if (sta->sta.supp_rates[band] != prev_rates) {
+                               if (sta->sta.supp_rates[band] != prev_rates) {
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
-                               printk(KERN_DEBUG "%s: updated supp_rates set "
-                                   "for %pM based on beacon/probe_response "
-                                   "(0x%x -> 0x%x)\n",
-                                   sdata->name, sta->sta.addr,
-                                   prev_rates, sta->sta.supp_rates[band]);
+                                       printk(KERN_DEBUG
+                                               "%s: updated supp_rates set "
+                                               "for %pM based on beacon"
+                                               "/probe_resp (0x%x -> 0x%x)\n",
+                                               sdata->name, sta->sta.addr,
+                                               prev_rates,
+                                               sta->sta.supp_rates[band]);
 #endif
-                               rate_control_rate_init(sta);
-                       }
-                       rcu_read_unlock();
-               } else {
-                       rcu_read_unlock();
-                       ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
-                                              supp_rates, GFP_KERNEL);
+                                       rate_control_rate_init(sta);
+                               }
+                       } else
+                               sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid,
+                                               mgmt->sa, supp_rates,
+                                               GFP_ATOMIC);
                }
+
+               if (sta && elems->wmm_info)
+                       set_sta_flags(sta, WLAN_STA_WME);
+
+               rcu_read_unlock();
        }
 
        bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
@@ -421,8 +448,8 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
                return NULL;
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-       printk(KERN_DEBUG "%s: Adding new IBSS station %pM (dev=%s)\n",
-              wiphy_name(local->hw.wiphy), addr, sdata->name);
+       wiphy_debug(local->hw.wiphy, "Adding new IBSS station %pM (dev=%s)\n",
+                   addr, sdata->name);
 #endif
 
        sta = sta_info_alloc(sdata, addr, gfp);
@@ -449,6 +476,8 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
        int active = 0;
        struct sta_info *sta;
 
+       lockdep_assert_held(&sdata->u.ibss.mtx);
+
        rcu_read_lock();
 
        list_for_each_entry_rcu(sta, &local->sta_list, list) {
@@ -473,6 +502,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 
+       lockdep_assert_held(&ifibss->mtx);
+
        mod_timer(&ifibss->timer,
                  round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
 
@@ -505,6 +536,8 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
        u16 capability;
        int i;
 
+       lockdep_assert_held(&ifibss->mtx);
+
        if (ifibss->fixed_bssid) {
                memcpy(bssid, ifibss->bssid, ETH_ALEN);
        } else {
@@ -549,6 +582,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
        int active_ibss;
        u16 capability;
 
+       lockdep_assert_held(&ifibss->mtx);
+
        active_ibss = ieee80211_sta_active_ibss(sdata);
 #ifdef CONFIG_MAC80211_IBSS_DEBUG
        printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n",
@@ -637,6 +672,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_mgmt *resp;
        u8 *pos, *end;
 
+       lockdep_assert_held(&ifibss->mtx);
+
        if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
            len < 24 + 2 || !ifibss->presp)
                return;
@@ -740,6 +777,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        mgmt = (struct ieee80211_mgmt *) skb->data;
        fc = le16_to_cpu(mgmt->frame_control);
 
+       mutex_lock(&sdata->u.ibss.mtx);
+
        switch (fc & IEEE80211_FCTL_STYPE) {
        case IEEE80211_STYPE_PROBE_REQ:
                ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len);
@@ -756,14 +795,23 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
                break;
        }
+
+       mutex_unlock(&sdata->u.ibss.mtx);
 }
 
 void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 
-       if (!test_and_clear_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request))
-               return;
+       mutex_lock(&ifibss->mtx);
+
+       /*
+        * Work could be scheduled after scan or similar
+        * when we aren't even joined (or trying) with a
+        * network.
+        */
+       if (!ifibss->ssid_len)
+               goto out;
 
        switch (ifibss->state) {
        case IEEE80211_IBSS_MLME_SEARCH:
@@ -776,15 +824,9 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
                WARN_ON(1);
                break;
        }
-}
 
-static void ieee80211_queue_ibss_work(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-       struct ieee80211_local *local = sdata->local;
-
-       set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
-       ieee80211_queue_work(&local->hw, &sdata->work);
+ out:
+       mutex_unlock(&ifibss->mtx);
 }
 
 static void ieee80211_ibss_timer(unsigned long data)
@@ -799,7 +841,7 @@ static void ieee80211_ibss_timer(unsigned long data)
                return;
        }
 
-       ieee80211_queue_ibss_work(sdata);
+       ieee80211_queue_work(&local->hw, &sdata->work);
 }
 
 #ifdef CONFIG_PM
@@ -828,6 +870,7 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
 
        setup_timer(&ifibss->timer, ieee80211_ibss_timer,
                    (unsigned long) sdata);
+       mutex_init(&ifibss->mtx);
 }
 
 /* scan finished notification */
@@ -841,10 +884,8 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)
                        continue;
                if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
                        continue;
-               if (!sdata->u.ibss.ssid_len)
-                       continue;
                sdata->u.ibss.last_scan_completed = jiffies;
-               ieee80211_queue_ibss_work(sdata);
+               ieee80211_queue_work(&local->hw, &sdata->work);
        }
        mutex_unlock(&local->iflist_mtx);
 }
@@ -854,6 +895,17 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 {
        struct sk_buff *skb;
 
+       skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
+                           36 /* bitrates */ +
+                           34 /* SSID */ +
+                           3  /* DS params */ +
+                           4  /* IBSS params */ +
+                           params->ie_len);
+       if (!skb)
+               return -ENOMEM;
+
+       mutex_lock(&sdata->u.ibss.mtx);
+
        if (params->bssid) {
                memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
                sdata->u.ibss.fixed_bssid = true;
@@ -882,33 +934,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
                        sdata->u.ibss.ie_len = params->ie_len;
        }
 
-       skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
-                           36 /* bitrates */ +
-                           34 /* SSID */ +
-                           3  /* DS params */ +
-                           4  /* IBSS params */ +
-                           params->ie_len);
-       if (!skb)
-               return -ENOMEM;
-
        sdata->u.ibss.skb = skb;
        sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;
        sdata->u.ibss.ibss_join_req = jiffies;
 
        memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN);
-
-       /*
-        * The ssid_len setting below is used to see whether
-        * we are active, and we need all other settings
-        * before that may get visible.
-        */
-       mb();
-
        sdata->u.ibss.ssid_len = params->ssid_len;
 
+       mutex_unlock(&sdata->u.ibss.mtx);
+
+       mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&sdata->local->mtx);
 
-       set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 
        return 0;
@@ -921,7 +959,9 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        struct cfg80211_bss *cbss;
        u16 capability;
-       int active_ibss = 0;
+       int active_ibss;
+
+       mutex_lock(&sdata->u.ibss.mtx);
 
        active_ibss = ieee80211_sta_active_ibss(sdata);
 
@@ -943,11 +983,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
                }
        }
 
-       del_timer_sync(&sdata->u.ibss.timer);
-       clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
-       cancel_work_sync(&sdata->work);
-       clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
-
        sta_info_flush(sdata->local, sdata);
 
        /* remove beacon */
@@ -964,7 +999,13 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
        memset(sdata->u.ibss.bssid, 0, ETH_ALEN);
        sdata->u.ibss.ssid_len = 0;
 
+       del_timer_sync(&sdata->u.ibss.timer);
+
+       mutex_unlock(&sdata->u.ibss.mtx);
+
+       mutex_lock(&local->mtx);
        ieee80211_recalc_idle(sdata->local);
+       mutex_unlock(&local->mtx);
 
        return 0;
 }