diff options
Diffstat (limited to 'net/wireless/sme.c')
| -rw-r--r-- | net/wireless/sme.c | 78 |
1 files changed, 61 insertions, 17 deletions
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index dc0fc4989d54..a8c2d6b877ae 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include <linux/etherdevice.h> | 8 | #include <linux/etherdevice.h> |
| 9 | #include <linux/if_arp.h> | 9 | #include <linux/if_arp.h> |
| 10 | #include <linux/slab.h> | ||
| 10 | #include <linux/workqueue.h> | 11 | #include <linux/workqueue.h> |
| 11 | #include <linux/wireless.h> | 12 | #include <linux/wireless.h> |
| 12 | #include <net/iw_handler.h> | 13 | #include <net/iw_handler.h> |
| @@ -34,6 +35,44 @@ struct cfg80211_conn { | |||
| 34 | bool auto_auth, prev_bssid_valid; | 35 | bool auto_auth, prev_bssid_valid; |
| 35 | }; | 36 | }; |
| 36 | 37 | ||
| 38 | static bool cfg80211_is_all_idle(void) | ||
| 39 | { | ||
| 40 | struct cfg80211_registered_device *rdev; | ||
| 41 | struct wireless_dev *wdev; | ||
| 42 | bool is_all_idle = true; | ||
| 43 | |||
| 44 | mutex_lock(&cfg80211_mutex); | ||
| 45 | |||
| 46 | /* | ||
| 47 | * All devices must be idle as otherwise if you are actively | ||
| 48 | * scanning some new beacon hints could be learned and would | ||
| 49 | * count as new regulatory hints. | ||
| 50 | */ | ||
| 51 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | ||
| 52 | cfg80211_lock_rdev(rdev); | ||
| 53 | list_for_each_entry(wdev, &rdev->netdev_list, list) { | ||
| 54 | wdev_lock(wdev); | ||
| 55 | if (wdev->sme_state != CFG80211_SME_IDLE) | ||
| 56 | is_all_idle = false; | ||
| 57 | wdev_unlock(wdev); | ||
| 58 | } | ||
| 59 | cfg80211_unlock_rdev(rdev); | ||
| 60 | } | ||
| 61 | |||
| 62 | mutex_unlock(&cfg80211_mutex); | ||
| 63 | |||
| 64 | return is_all_idle; | ||
| 65 | } | ||
| 66 | |||
| 67 | static void disconnect_work(struct work_struct *work) | ||
| 68 | { | ||
| 69 | if (!cfg80211_is_all_idle()) | ||
| 70 | return; | ||
| 71 | |||
| 72 | regulatory_hint_disconnect(); | ||
| 73 | } | ||
| 74 | |||
| 75 | static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); | ||
| 37 | 76 | ||
| 38 | static int cfg80211_conn_scan(struct wireless_dev *wdev) | 77 | static int cfg80211_conn_scan(struct wireless_dev *wdev) |
| 39 | { | 78 | { |
| @@ -132,7 +171,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) | |||
| 132 | params->ssid, params->ssid_len, | 171 | params->ssid, params->ssid_len, |
| 133 | NULL, 0, | 172 | NULL, 0, |
| 134 | params->key, params->key_len, | 173 | params->key, params->key_len, |
| 135 | params->key_idx); | 174 | params->key_idx, false); |
| 136 | case CFG80211_CONN_ASSOCIATE_NEXT: | 175 | case CFG80211_CONN_ASSOCIATE_NEXT: |
| 137 | BUG_ON(!rdev->ops->assoc); | 176 | BUG_ON(!rdev->ops->assoc); |
| 138 | wdev->conn->state = CFG80211_CONN_ASSOCIATING; | 177 | wdev->conn->state = CFG80211_CONN_ASSOCIATING; |
| @@ -147,12 +186,13 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) | |||
| 147 | if (err) | 186 | if (err) |
| 148 | __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, | 187 | __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, |
| 149 | NULL, 0, | 188 | NULL, 0, |
| 150 | WLAN_REASON_DEAUTH_LEAVING); | 189 | WLAN_REASON_DEAUTH_LEAVING, |
| 190 | false); | ||
| 151 | return err; | 191 | return err; |
| 152 | case CFG80211_CONN_DEAUTH_ASSOC_FAIL: | 192 | case CFG80211_CONN_DEAUTH_ASSOC_FAIL: |
| 153 | __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, | 193 | __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, |
| 154 | NULL, 0, | 194 | NULL, 0, |
| 155 | WLAN_REASON_DEAUTH_LEAVING); | 195 | WLAN_REASON_DEAUTH_LEAVING, false); |
| 156 | /* return an error so that we call __cfg80211_connect_result() */ | 196 | /* return an error so that we call __cfg80211_connect_result() */ |
| 157 | return -EINVAL; | 197 | return -EINVAL; |
| 158 | default: | 198 | default: |
| @@ -454,6 +494,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
| 454 | * - and country_ie[1] which is the IE length | 494 | * - and country_ie[1] which is the IE length |
| 455 | */ | 495 | */ |
| 456 | regulatory_hint_11d(wdev->wiphy, | 496 | regulatory_hint_11d(wdev->wiphy, |
| 497 | bss->channel->band, | ||
| 457 | country_ie + 2, | 498 | country_ie + 2, |
| 458 | country_ie[1]); | 499 | country_ie[1]); |
| 459 | } | 500 | } |
| @@ -477,12 +518,16 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
| 477 | ev->type = EVENT_CONNECT_RESULT; | 518 | ev->type = EVENT_CONNECT_RESULT; |
| 478 | if (bssid) | 519 | if (bssid) |
| 479 | memcpy(ev->cr.bssid, bssid, ETH_ALEN); | 520 | memcpy(ev->cr.bssid, bssid, ETH_ALEN); |
| 480 | ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); | 521 | if (req_ie_len) { |
| 481 | ev->cr.req_ie_len = req_ie_len; | 522 | ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); |
| 482 | memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); | 523 | ev->cr.req_ie_len = req_ie_len; |
| 483 | ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; | 524 | memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); |
| 484 | ev->cr.resp_ie_len = resp_ie_len; | 525 | } |
| 485 | memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); | 526 | if (resp_ie_len) { |
| 527 | ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; | ||
| 528 | ev->cr.resp_ie_len = resp_ie_len; | ||
| 529 | memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); | ||
| 530 | } | ||
| 486 | ev->cr.status = status; | 531 | ev->cr.status = status; |
| 487 | 532 | ||
| 488 | spin_lock_irqsave(&wdev->event_lock, flags); | 533 | spin_lock_irqsave(&wdev->event_lock, flags); |
| @@ -636,7 +681,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | |||
| 636 | continue; | 681 | continue; |
| 637 | bssid = wdev->auth_bsses[i]->pub.bssid; | 682 | bssid = wdev->auth_bsses[i]->pub.bssid; |
| 638 | ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, | 683 | ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, |
| 639 | WLAN_REASON_DEAUTH_LEAVING); | 684 | WLAN_REASON_DEAUTH_LEAVING, |
| 685 | false); | ||
| 640 | WARN(ret, "deauth failed: %d\n", ret); | 686 | WARN(ret, "deauth failed: %d\n", ret); |
| 641 | } | 687 | } |
| 642 | } | 688 | } |
| @@ -657,6 +703,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | |||
| 657 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | 703 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); |
| 658 | wdev->wext.connect.ssid_len = 0; | 704 | wdev->wext.connect.ssid_len = 0; |
| 659 | #endif | 705 | #endif |
| 706 | |||
| 707 | schedule_work(&cfg80211_disconnect_work); | ||
| 660 | } | 708 | } |
| 661 | 709 | ||
| 662 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | 710 | void cfg80211_disconnected(struct net_device *dev, u16 reason, |
| @@ -693,7 +741,6 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
| 693 | const u8 *prev_bssid) | 741 | const u8 *prev_bssid) |
| 694 | { | 742 | { |
| 695 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 743 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
| 696 | struct ieee80211_channel *chan; | ||
| 697 | struct cfg80211_bss *bss = NULL; | 744 | struct cfg80211_bss *bss = NULL; |
| 698 | int err; | 745 | int err; |
| 699 | 746 | ||
| @@ -702,10 +749,6 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
| 702 | if (wdev->sme_state != CFG80211_SME_IDLE) | 749 | if (wdev->sme_state != CFG80211_SME_IDLE) |
| 703 | return -EALREADY; | 750 | return -EALREADY; |
| 704 | 751 | ||
| 705 | chan = rdev_fixed_channel(rdev, wdev); | ||
| 706 | if (chan && chan != connect->channel) | ||
| 707 | return -EBUSY; | ||
| 708 | |||
| 709 | if (WARN_ON(wdev->connect_keys)) { | 752 | if (WARN_ON(wdev->connect_keys)) { |
| 710 | kfree(wdev->connect_keys); | 753 | kfree(wdev->connect_keys); |
| 711 | wdev->connect_keys = NULL; | 754 | wdev->connect_keys = NULL; |
| @@ -893,7 +936,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, | |||
| 893 | /* wdev->conn->params.bssid must be set if > SCANNING */ | 936 | /* wdev->conn->params.bssid must be set if > SCANNING */ |
| 894 | err = __cfg80211_mlme_deauth(rdev, dev, | 937 | err = __cfg80211_mlme_deauth(rdev, dev, |
| 895 | wdev->conn->params.bssid, | 938 | wdev->conn->params.bssid, |
| 896 | NULL, 0, reason); | 939 | NULL, 0, reason, false); |
| 897 | if (err) | 940 | if (err) |
| 898 | return err; | 941 | return err; |
| 899 | } else { | 942 | } else { |
| @@ -949,7 +992,8 @@ void cfg80211_sme_disassoc(struct net_device *dev, int idx) | |||
| 949 | 992 | ||
| 950 | memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); | 993 | memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); |
| 951 | if (__cfg80211_mlme_deauth(rdev, dev, bssid, | 994 | if (__cfg80211_mlme_deauth(rdev, dev, bssid, |
| 952 | NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { | 995 | NULL, 0, WLAN_REASON_DEAUTH_LEAVING, |
| 996 | false)) { | ||
| 953 | /* whatever -- assume gone anyway */ | 997 | /* whatever -- assume gone anyway */ |
| 954 | cfg80211_unhold_bss(wdev->auth_bsses[idx]); | 998 | cfg80211_unhold_bss(wdev->auth_bsses[idx]); |
| 955 | cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); | 999 | cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); |
