summaryrefslogtreecommitdiff
path: root/net/wireless/sme.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/sme.c')
-rw-r--r--net/wireless/sme.c252
1 files changed, 196 insertions, 56 deletions
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 066a19ef9d73..472e2412c781 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -38,6 +38,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
int n_channels, err;
ASSERT_RTNL();
+ ASSERT_RDEV_LOCK(drv);
+ ASSERT_WDEV_LOCK(wdev);
if (drv->scan_req)
return -EBUSY;
@@ -106,6 +108,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
struct cfg80211_connect_params *params;
int err;
+ ASSERT_WDEV_LOCK(wdev);
+
if (!wdev->conn)
return 0;
@@ -117,11 +121,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
case CFG80211_CONN_AUTHENTICATE_NEXT:
BUG_ON(!drv->ops->auth);
wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
- return cfg80211_mlme_auth(drv, wdev->netdev,
- params->channel, params->auth_type,
- params->bssid,
- params->ssid, params->ssid_len,
- NULL, 0);
+ return __cfg80211_mlme_auth(drv, wdev->netdev,
+ params->channel, params->auth_type,
+ params->bssid,
+ params->ssid, params->ssid_len,
+ NULL, 0);
case CFG80211_CONN_ASSOCIATE_NEXT:
BUG_ON(!drv->ops->assoc);
wdev->conn->state = CFG80211_CONN_ASSOCIATING;
@@ -131,14 +135,16 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
* that some APs don't like that -- so we'd need to retry
* the association.
*/
- err = cfg80211_mlme_assoc(drv, wdev->netdev,
- params->channel, params->bssid, NULL,
- params->ssid, params->ssid_len,
- params->ie, params->ie_len,
- false, &params->crypto);
+ err = __cfg80211_mlme_assoc(drv, wdev->netdev,
+ params->channel, params->bssid,
+ NULL,
+ params->ssid, params->ssid_len,
+ params->ie, params->ie_len,
+ false, &params->crypto);
if (err)
- cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid,
- NULL, 0, WLAN_REASON_DEAUTH_LEAVING);
+ __cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid,
+ NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING);
return err;
default:
return 0;
@@ -152,22 +158,31 @@ void cfg80211_conn_work(struct work_struct *work)
struct wireless_dev *wdev;
rtnl_lock();
+ cfg80211_lock_rdev(drv);
mutex_lock(&drv->devlist_mtx);
list_for_each_entry(wdev, &drv->netdev_list, list) {
- if (!netif_running(wdev->netdev))
+ wdev_lock(wdev);
+ if (!netif_running(wdev->netdev)) {
+ wdev_unlock(wdev);
continue;
- if (wdev->sme_state != CFG80211_SME_CONNECTING)
+ }
+ if (wdev->sme_state != CFG80211_SME_CONNECTING) {
+ wdev_unlock(wdev);
continue;
+ }
if (cfg80211_conn_do_work(wdev))
- cfg80211_connect_result(wdev->netdev,
- wdev->conn->params.bssid,
- NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_ATOMIC);
+ __cfg80211_connect_result(
+ wdev->netdev,
+ wdev->conn->params.bssid,
+ NULL, 0, NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ false);
+ wdev_unlock(wdev);
}
mutex_unlock(&drv->devlist_mtx);
+ cfg80211_unlock_rdev(drv);
rtnl_unlock();
}
@@ -177,6 +192,8 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev)
struct cfg80211_bss *bss;
u16 capa = WLAN_CAPABILITY_ESS;
+ ASSERT_WDEV_LOCK(wdev);
+
if (wdev->conn->params.privacy)
capa |= WLAN_CAPABILITY_PRIVACY;
@@ -198,11 +215,13 @@ static bool cfg80211_get_conn_bss(struct wireless_dev *wdev)
return true;
}
-void cfg80211_sme_scan_done(struct net_device *dev)
+static void __cfg80211_sme_scan_done(struct net_device *dev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy);
+ ASSERT_WDEV_LOCK(wdev);
+
if (wdev->sme_state != CFG80211_SME_CONNECTING)
return;
@@ -218,15 +237,26 @@ void cfg80211_sme_scan_done(struct net_device *dev)
if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
schedule_work(&drv->conn_work);
else
- cfg80211_connect_result(dev, wdev->conn->params.bssid,
- NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_ATOMIC);
- return;
+ __cfg80211_connect_result(
+ wdev->netdev,
+ wdev->conn->params.bssid,
+ NULL, 0, NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ false);
}
}
-void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
+void cfg80211_sme_scan_done(struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+ wdev_lock(wdev);
+ __cfg80211_sme_scan_done(dev);
+ wdev_unlock(wdev);
+}
+
+void cfg80211_sme_rx_auth(struct net_device *dev,
+ const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
@@ -234,6 +264,8 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
+ ASSERT_WDEV_LOCK(wdev);
+
/* should only RX auth frames when connecting */
if (wdev->sme_state != CFG80211_SME_CONNECTING)
return;
@@ -273,10 +305,10 @@ void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
}
}
-static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
- const u8 *req_ie, size_t req_ie_len,
- const u8 *resp_ie, size_t resp_ie_len,
- u16 status, bool wextev, gfp_t gfp)
+void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
+ const u8 *req_ie, size_t req_ie_len,
+ const u8 *resp_ie, size_t resp_ie_len,
+ u16 status, bool wextev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_bss *bss;
@@ -284,18 +316,20 @@ static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
union iwreq_data wrqu;
#endif
+ ASSERT_WDEV_LOCK(wdev);
+
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return;
if (wdev->sme_state == CFG80211_SME_CONNECTED)
nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev,
bssid, req_ie, req_ie_len,
- resp_ie, resp_ie_len, gfp);
+ resp_ie, resp_ie_len, GFP_KERNEL);
else
nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
bssid, req_ie, req_ie_len,
resp_ie, resp_ie_len,
- status, gfp);
+ status, GFP_KERNEL);
#ifdef CONFIG_WIRELESS_EXT
if (wextev) {
@@ -362,21 +396,43 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *resp_ie, size_t resp_ie_len,
u16 status, gfp_t gfp)
{
- bool wextev = status == WLAN_STATUS_SUCCESS;
- __cfg80211_connect_result(dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, wextev, gfp);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct cfg80211_event *ev;
+ unsigned long flags;
+
+ ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
+ if (!ev)
+ return;
+
+ ev->type = EVENT_CONNECT_RESULT;
+ 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);
+ ev->cr.status = status;
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ list_add_tail(&ev->list, &wdev->event_list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+ schedule_work(&rdev->event_work);
}
EXPORT_SYMBOL(cfg80211_connect_result);
-void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
- const u8 *req_ie, size_t req_ie_len,
- const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
+void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,
+ const u8 *req_ie, size_t req_ie_len,
+ const u8 *resp_ie, size_t resp_ie_len)
{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_bss *bss;
#ifdef CONFIG_WIRELESS_EXT
union iwreq_data wrqu;
#endif
+ ASSERT_WDEV_LOCK(wdev);
+
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return;
@@ -402,31 +458,62 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
cfg80211_hold_bss(bss_from_pub(bss));
wdev->current_bss = bss_from_pub(bss);
- nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid,
- req_ie, req_ie_len, resp_ie, resp_ie_len, gfp);
+ nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid,
+ req_ie, req_ie_len, resp_ie, resp_ie_len,
+ GFP_KERNEL);
#ifdef CONFIG_WIRELESS_EXT
if (req_ie) {
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = req_ie_len;
- wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie);
+ wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
+ &wrqu, req_ie);
}
if (resp_ie) {
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = resp_ie_len;
- wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
+ wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
+ &wrqu, resp_ie);
}
memset(&wrqu, 0, sizeof(wrqu));
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
- wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+ wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
#endif
}
+
+void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
+ const u8 *req_ie, size_t req_ie_len,
+ const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct cfg80211_event *ev;
+ unsigned long flags;
+
+ ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
+ if (!ev)
+ return;
+
+ ev->type = EVENT_ROAMED;
+ memcpy(ev->rm.bssid, bssid, ETH_ALEN);
+ ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
+ ev->rm.req_ie_len = req_ie_len;
+ memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
+ ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
+ ev->rm.resp_ie_len = resp_ie_len;
+ memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ list_add_tail(&ev->list, &wdev->event_list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+ schedule_work(&rdev->event_work);
+}
EXPORT_SYMBOL(cfg80211_roamed);
-void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie,
+void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
@@ -434,6 +521,8 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie,
union iwreq_data wrqu;
#endif
+ ASSERT_WDEV_LOCK(wdev);
+
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return;
@@ -456,7 +545,7 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie,
}
nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev,
- reason, ie, ie_len, from_ap, gfp);
+ reason, ie, ie_len, from_ap);
#ifdef CONFIG_WIRELESS_EXT
memset(&wrqu, 0, sizeof(wrqu));
@@ -468,16 +557,36 @@ void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie,
void cfg80211_disconnected(struct net_device *dev, u16 reason,
u8 *ie, size_t ie_len, gfp_t gfp)
{
- __cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true);
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct cfg80211_event *ev;
+ unsigned long flags;
+
+ ev = kzalloc(sizeof(*ev) + ie_len, gfp);
+ if (!ev)
+ return;
+
+ ev->type = EVENT_DISCONNECTED;
+ ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
+ ev->dc.ie_len = ie_len;
+ memcpy((void *)ev->dc.ie, ie, ie_len);
+ ev->dc.reason = reason;
+
+ spin_lock_irqsave(&wdev->event_lock, flags);
+ list_add_tail(&ev->list, &wdev->event_list);
+ spin_unlock_irqrestore(&wdev->event_lock, flags);
+ schedule_work(&rdev->event_work);
}
EXPORT_SYMBOL(cfg80211_disconnected);
-int cfg80211_connect(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_connect_params *connect)
+int __cfg80211_connect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *connect)
{
- int err;
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
if (wdev->sme_state != CFG80211_SME_IDLE)
return -EALREADY;
@@ -572,12 +681,27 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
}
}
-int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
- struct net_device *dev, u16 reason, bool wextev)
+int cfg80211_connect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *connect)
+{
+ int err;
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = __cfg80211_connect(rdev, dev, connect);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return err;
+}
+
+int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u16 reason, bool wextev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
+ ASSERT_WDEV_LOCK(wdev);
+
if (wdev->sme_state == CFG80211_SME_IDLE)
return -EINVAL;
@@ -601,8 +725,9 @@ 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);
+ err = __cfg80211_mlme_deauth(rdev, dev,
+ wdev->conn->params.bssid,
+ NULL, 0, reason);
if (err)
return err;
} else {
@@ -612,21 +737,36 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
}
if (wdev->sme_state == CFG80211_SME_CONNECTED)
- __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false);
+ __cfg80211_disconnected(dev, NULL, 0, 0, false);
else if (wdev->sme_state == CFG80211_SME_CONNECTING)
__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
- wextev, GFP_KERNEL);
+ wextev);
return 0;
}
+int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ u16 reason, bool wextev)
+{
+ int err;
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = __cfg80211_disconnect(rdev, dev, reason, wextev);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return err;
+}
+
void cfg80211_sme_disassoc(struct net_device *dev, int idx)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
u8 bssid[ETH_ALEN];
+ ASSERT_WDEV_LOCK(wdev);
+
if (!wdev->conn)
return;