Merge branch 'master' into for-davem
[cascardo/linux.git] / net / wireless / sme.c
index dc0fc49..8ddf5ae 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/etherdevice.h>
 #include <linux/if_arp.h>
+#include <linux/slab.h>
 #include <linux/workqueue.h>
 #include <linux/wireless.h>
 #include <net/iw_handler.h>
@@ -34,6 +35,44 @@ struct cfg80211_conn {
        bool auto_auth, prev_bssid_valid;
 };
 
+bool cfg80211_is_all_idle(void)
+{
+       struct cfg80211_registered_device *rdev;
+       struct wireless_dev *wdev;
+       bool is_all_idle = true;
+
+       mutex_lock(&cfg80211_mutex);
+
+       /*
+        * All devices must be idle as otherwise if you are actively
+        * scanning some new beacon hints could be learned and would
+        * count as new regulatory hints.
+        */
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               cfg80211_lock_rdev(rdev);
+               list_for_each_entry(wdev, &rdev->netdev_list, list) {
+                       wdev_lock(wdev);
+                       if (wdev->sme_state != CFG80211_SME_IDLE)
+                               is_all_idle = false;
+                       wdev_unlock(wdev);
+               }
+               cfg80211_unlock_rdev(rdev);
+       }
+
+       mutex_unlock(&cfg80211_mutex);
+
+       return is_all_idle;
+}
+
+static void disconnect_work(struct work_struct *work)
+{
+       if (!cfg80211_is_all_idle())
+               return;
+
+       regulatory_hint_disconnect();
+}
+
+static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
 
 static int cfg80211_conn_scan(struct wireless_dev *wdev)
 {
@@ -132,7 +171,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                                            params->ssid, params->ssid_len,
                                            NULL, 0,
                                            params->key, params->key_len,
-                                           params->key_idx);
+                                           params->key_idx, false);
        case CFG80211_CONN_ASSOCIATE_NEXT:
                BUG_ON(!rdev->ops->assoc);
                wdev->conn->state = CFG80211_CONN_ASSOCIATING;
@@ -147,12 +186,13 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
                if (err)
                        __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                               NULL, 0,
-                                              WLAN_REASON_DEAUTH_LEAVING);
+                                              WLAN_REASON_DEAUTH_LEAVING,
+                                              false);
                return err;
        case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
                __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
                                       NULL, 0,
-                                      WLAN_REASON_DEAUTH_LEAVING);
+                                      WLAN_REASON_DEAUTH_LEAVING, false);
                /* return an error so that we call __cfg80211_connect_result() */
                return -EINVAL;
        default:
@@ -454,6 +494,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
         * - and country_ie[1] which is the IE length
         */
        regulatory_hint_11d(wdev->wiphy,
+                           bss->channel->band,
                            country_ie + 2,
                            country_ie[1]);
 }
@@ -477,12 +518,16 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        ev->type = EVENT_CONNECT_RESULT;
        if (bssid)
                memcpy(ev->cr.bssid, bssid, ETH_ALEN);
-       ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
-       ev->cr.req_ie_len = req_ie_len;
-       memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
-       ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
-       ev->cr.resp_ie_len = resp_ie_len;
-       memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
+       if (req_ie_len) {
+               ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
+               ev->cr.req_ie_len = req_ie_len;
+               memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
+       }
+       if (resp_ie_len) {
+               ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
+               ev->cr.resp_ie_len = resp_ie_len;
+               memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
+       }
        ev->cr.status = status;
 
        spin_lock_irqsave(&wdev->event_lock, flags);
@@ -636,7 +681,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
                                continue;
                        bssid = wdev->auth_bsses[i]->pub.bssid;
                        ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
-                                               WLAN_REASON_DEAUTH_LEAVING);
+                                               WLAN_REASON_DEAUTH_LEAVING,
+                                               false);
                        WARN(ret, "deauth failed: %d\n", ret);
                }
        }
@@ -657,6 +703,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
        wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
        wdev->wext.connect.ssid_len = 0;
 #endif
+
+       schedule_work(&cfg80211_disconnect_work);
 }
 
 void cfg80211_disconnected(struct net_device *dev, u16 reason,
@@ -893,7 +941,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
                /* wdev->conn->params.bssid must be set if > SCANNING */
                err = __cfg80211_mlme_deauth(rdev, dev,
                                             wdev->conn->params.bssid,
-                                            NULL, 0, reason);
+                                            NULL, 0, reason, false);
                if (err)
                        return err;
        } else {
@@ -949,7 +997,8 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx)
 
        memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
        if (__cfg80211_mlme_deauth(rdev, dev, bssid,
-                                  NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
+                                  NULL, 0, WLAN_REASON_DEAUTH_LEAVING,
+                                  false)) {
                /* whatever -- assume gone anyway */
                cfg80211_unhold_bss(wdev->auth_bsses[idx]);
                cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);