1b23aa676SSamuel Ortiz /* 2b23aa676SSamuel Ortiz * SME code for cfg80211's connect emulation. 3b23aa676SSamuel Ortiz * 4b23aa676SSamuel Ortiz * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 5b23aa676SSamuel Ortiz * Copyright (C) 2009 Intel Corporation. All rights reserved. 6b23aa676SSamuel Ortiz */ 7b23aa676SSamuel Ortiz 8b23aa676SSamuel Ortiz #include <linux/etherdevice.h> 9b23aa676SSamuel Ortiz #include <linux/if_arp.h> 10b23aa676SSamuel Ortiz #include <linux/workqueue.h> 11b23aa676SSamuel Ortiz #include <net/cfg80211.h> 12b23aa676SSamuel Ortiz #include <net/rtnetlink.h> 13b23aa676SSamuel Ortiz #include "nl80211.h" 14b23aa676SSamuel Ortiz 156829c878SJohannes Berg struct cfg80211_conn { 166829c878SJohannes Berg struct cfg80211_connect_params params; 176829c878SJohannes Berg /* these are sub-states of the _CONNECTING sme_state */ 186829c878SJohannes Berg enum { 196829c878SJohannes Berg CFG80211_CONN_IDLE, 206829c878SJohannes Berg CFG80211_CONN_SCANNING, 216829c878SJohannes Berg CFG80211_CONN_SCAN_AGAIN, 226829c878SJohannes Berg CFG80211_CONN_AUTHENTICATE_NEXT, 236829c878SJohannes Berg CFG80211_CONN_AUTHENTICATING, 246829c878SJohannes Berg CFG80211_CONN_ASSOCIATE_NEXT, 256829c878SJohannes Berg CFG80211_CONN_ASSOCIATING, 266829c878SJohannes Berg } state; 276829c878SJohannes Berg u8 bssid[ETH_ALEN]; 286829c878SJohannes Berg u8 *ie; 296829c878SJohannes Berg size_t ie_len; 306829c878SJohannes Berg bool auto_auth; 316829c878SJohannes Berg }; 326829c878SJohannes Berg 336829c878SJohannes Berg 346829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev) 356829c878SJohannes Berg { 3679c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 376829c878SJohannes Berg struct cfg80211_scan_request *request; 386829c878SJohannes Berg int n_channels, err; 396829c878SJohannes Berg 406829c878SJohannes Berg ASSERT_RTNL(); 4179c97e97SJohannes Berg ASSERT_RDEV_LOCK(rdev); 42667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 436829c878SJohannes Berg 4479c97e97SJohannes Berg if (rdev->scan_req) 456829c878SJohannes Berg return -EBUSY; 466829c878SJohannes Berg 476829c878SJohannes Berg if (wdev->conn->params.channel) { 486829c878SJohannes Berg n_channels = 1; 496829c878SJohannes Berg } else { 506829c878SJohannes Berg enum ieee80211_band band; 516829c878SJohannes Berg n_channels = 0; 526829c878SJohannes Berg 536829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 546829c878SJohannes Berg if (!wdev->wiphy->bands[band]) 556829c878SJohannes Berg continue; 566829c878SJohannes Berg n_channels += wdev->wiphy->bands[band]->n_channels; 576829c878SJohannes Berg } 586829c878SJohannes Berg } 596829c878SJohannes Berg request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + 606829c878SJohannes Berg sizeof(request->channels[0]) * n_channels, 616829c878SJohannes Berg GFP_KERNEL); 626829c878SJohannes Berg if (!request) 636829c878SJohannes Berg return -ENOMEM; 646829c878SJohannes Berg 656829c878SJohannes Berg request->channels = (void *)((char *)request + sizeof(*request)); 666829c878SJohannes Berg if (wdev->conn->params.channel) 676829c878SJohannes Berg request->channels[0] = wdev->conn->params.channel; 686829c878SJohannes Berg else { 696829c878SJohannes Berg int i = 0, j; 706829c878SJohannes Berg enum ieee80211_band band; 716829c878SJohannes Berg 726829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 736829c878SJohannes Berg if (!wdev->wiphy->bands[band]) 746829c878SJohannes Berg continue; 756829c878SJohannes Berg for (j = 0; j < wdev->wiphy->bands[band]->n_channels; 766829c878SJohannes Berg i++, j++) 776829c878SJohannes Berg request->channels[i] = 786829c878SJohannes Berg &wdev->wiphy->bands[band]->channels[j]; 796829c878SJohannes Berg } 806829c878SJohannes Berg } 816829c878SJohannes Berg request->n_channels = n_channels; 826829c878SJohannes Berg request->ssids = (void *)(request->channels + n_channels); 836829c878SJohannes Berg request->n_ssids = 1; 846829c878SJohannes Berg 856829c878SJohannes Berg memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, 866829c878SJohannes Berg wdev->conn->params.ssid_len); 876829c878SJohannes Berg request->ssids[0].ssid_len = wdev->conn->params.ssid_len; 886829c878SJohannes Berg 89*463d0183SJohannes Berg request->dev = wdev->netdev; 9079c97e97SJohannes Berg request->wiphy = &rdev->wiphy; 916829c878SJohannes Berg 9279c97e97SJohannes Berg rdev->scan_req = request; 936829c878SJohannes Berg 9479c97e97SJohannes Berg err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); 956829c878SJohannes Berg if (!err) { 966829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCANNING; 9779c97e97SJohannes Berg nl80211_send_scan_start(rdev, wdev->netdev); 98*463d0183SJohannes Berg dev_hold(wdev->netdev); 996829c878SJohannes Berg } else { 10079c97e97SJohannes Berg rdev->scan_req = NULL; 1016829c878SJohannes Berg kfree(request); 1026829c878SJohannes Berg } 1036829c878SJohannes Berg return err; 1046829c878SJohannes Berg } 1056829c878SJohannes Berg 1066829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev) 1076829c878SJohannes Berg { 10879c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 10919957bb3SJohannes Berg struct cfg80211_connect_params *params; 11019957bb3SJohannes Berg int err; 1116829c878SJohannes Berg 112667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 113667503ddSJohannes Berg 1146829c878SJohannes Berg if (!wdev->conn) 1156829c878SJohannes Berg return 0; 1166829c878SJohannes Berg 11719957bb3SJohannes Berg params = &wdev->conn->params; 11819957bb3SJohannes Berg 1196829c878SJohannes Berg switch (wdev->conn->state) { 1206829c878SJohannes Berg case CFG80211_CONN_SCAN_AGAIN: 1216829c878SJohannes Berg return cfg80211_conn_scan(wdev); 1226829c878SJohannes Berg case CFG80211_CONN_AUTHENTICATE_NEXT: 12379c97e97SJohannes Berg BUG_ON(!rdev->ops->auth); 12419957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATING; 12579c97e97SJohannes Berg return __cfg80211_mlme_auth(rdev, wdev->netdev, 12619957bb3SJohannes Berg params->channel, params->auth_type, 12719957bb3SJohannes Berg params->bssid, 12819957bb3SJohannes Berg params->ssid, params->ssid_len, 129fffd0934SJohannes Berg NULL, 0, 130fffd0934SJohannes Berg params->key, params->key_len, 131fffd0934SJohannes Berg params->key_idx); 1326829c878SJohannes Berg case CFG80211_CONN_ASSOCIATE_NEXT: 13379c97e97SJohannes Berg BUG_ON(!rdev->ops->assoc); 13419957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATING; 1353e5d7649SJohannes Berg /* 1363e5d7649SJohannes Berg * We could, later, implement roaming here and then actually 1373e5d7649SJohannes Berg * set prev_bssid to non-NULL. But then we need to be aware 1383e5d7649SJohannes Berg * that some APs don't like that -- so we'd need to retry 1393e5d7649SJohannes Berg * the association. 1403e5d7649SJohannes Berg */ 14179c97e97SJohannes Berg err = __cfg80211_mlme_assoc(rdev, wdev->netdev, 142667503ddSJohannes Berg params->channel, params->bssid, 143667503ddSJohannes Berg NULL, 14419957bb3SJohannes Berg params->ssid, params->ssid_len, 14519957bb3SJohannes Berg params->ie, params->ie_len, 14619957bb3SJohannes Berg false, ¶ms->crypto); 14719957bb3SJohannes Berg if (err) 14879c97e97SJohannes Berg __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 149667503ddSJohannes Berg NULL, 0, 150667503ddSJohannes Berg WLAN_REASON_DEAUTH_LEAVING); 15119957bb3SJohannes Berg return err; 1526829c878SJohannes Berg default: 1536829c878SJohannes Berg return 0; 1546829c878SJohannes Berg } 1556829c878SJohannes Berg } 1566829c878SJohannes Berg 1576829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work) 1586829c878SJohannes Berg { 15979c97e97SJohannes Berg struct cfg80211_registered_device *rdev = 1606829c878SJohannes Berg container_of(work, struct cfg80211_registered_device, conn_work); 1616829c878SJohannes Berg struct wireless_dev *wdev; 1626829c878SJohannes Berg 1636829c878SJohannes Berg rtnl_lock(); 16479c97e97SJohannes Berg cfg80211_lock_rdev(rdev); 16579c97e97SJohannes Berg mutex_lock(&rdev->devlist_mtx); 1666829c878SJohannes Berg 16779c97e97SJohannes Berg list_for_each_entry(wdev, &rdev->netdev_list, list) { 168667503ddSJohannes Berg wdev_lock(wdev); 169667503ddSJohannes Berg if (!netif_running(wdev->netdev)) { 170667503ddSJohannes Berg wdev_unlock(wdev); 1716829c878SJohannes Berg continue; 172667503ddSJohannes Berg } 173667503ddSJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) { 174667503ddSJohannes Berg wdev_unlock(wdev); 1756829c878SJohannes Berg continue; 176667503ddSJohannes Berg } 1776829c878SJohannes Berg if (cfg80211_conn_do_work(wdev)) 178667503ddSJohannes Berg __cfg80211_connect_result( 179667503ddSJohannes Berg wdev->netdev, 1806829c878SJohannes Berg wdev->conn->params.bssid, 1816829c878SJohannes Berg NULL, 0, NULL, 0, 1826829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 183667503ddSJohannes Berg false); 184667503ddSJohannes Berg wdev_unlock(wdev); 1856829c878SJohannes Berg } 1866829c878SJohannes Berg 18779c97e97SJohannes Berg mutex_unlock(&rdev->devlist_mtx); 18879c97e97SJohannes Berg cfg80211_unlock_rdev(rdev); 1896829c878SJohannes Berg rtnl_unlock(); 1906829c878SJohannes Berg } 1916829c878SJohannes Berg 1926829c878SJohannes Berg static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) 1936829c878SJohannes Berg { 19479c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 1956829c878SJohannes Berg struct cfg80211_bss *bss; 1966829c878SJohannes Berg u16 capa = WLAN_CAPABILITY_ESS; 1976829c878SJohannes Berg 198667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 199667503ddSJohannes Berg 2006829c878SJohannes Berg if (wdev->conn->params.privacy) 2016829c878SJohannes Berg capa |= WLAN_CAPABILITY_PRIVACY; 2026829c878SJohannes Berg 2036829c878SJohannes Berg bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, 2046829c878SJohannes Berg wdev->conn->params.ssid, 2056829c878SJohannes Berg wdev->conn->params.ssid_len, 2066829c878SJohannes Berg WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, 2076829c878SJohannes Berg capa); 2086829c878SJohannes Berg if (!bss) 2096829c878SJohannes Berg return false; 2106829c878SJohannes Berg 2116829c878SJohannes Berg memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); 2126829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 2136829c878SJohannes Berg wdev->conn->params.channel = bss->channel; 2146829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 21579c97e97SJohannes Berg schedule_work(&rdev->conn_work); 2166829c878SJohannes Berg 2176829c878SJohannes Berg cfg80211_put_bss(bss); 2186829c878SJohannes Berg return true; 2196829c878SJohannes Berg } 2206829c878SJohannes Berg 221667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev) 2226829c878SJohannes Berg { 2236829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 22479c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 2256829c878SJohannes Berg 226667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 227667503ddSJohannes Berg 2286829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 2296829c878SJohannes Berg return; 2306829c878SJohannes Berg 231d4b1a687SZhu Yi if (!wdev->conn) 2326829c878SJohannes Berg return; 2336829c878SJohannes Berg 2346829c878SJohannes Berg if (wdev->conn->state != CFG80211_CONN_SCANNING && 2356829c878SJohannes Berg wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) 2366829c878SJohannes Berg return; 2376829c878SJohannes Berg 2386829c878SJohannes Berg if (!cfg80211_get_conn_bss(wdev)) { 2396829c878SJohannes Berg /* not found */ 2406829c878SJohannes Berg if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) 24179c97e97SJohannes Berg schedule_work(&rdev->conn_work); 2426829c878SJohannes Berg else 243667503ddSJohannes Berg __cfg80211_connect_result( 244667503ddSJohannes Berg wdev->netdev, 245667503ddSJohannes Berg wdev->conn->params.bssid, 2466829c878SJohannes Berg NULL, 0, NULL, 0, 2476829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 248667503ddSJohannes Berg false); 2496829c878SJohannes Berg } 2506829c878SJohannes Berg } 2516829c878SJohannes Berg 252667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev) 253667503ddSJohannes Berg { 254667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 255667503ddSJohannes Berg 256667503ddSJohannes Berg wdev_lock(wdev); 257667503ddSJohannes Berg __cfg80211_sme_scan_done(dev); 258667503ddSJohannes Berg wdev_unlock(wdev); 259667503ddSJohannes Berg } 260667503ddSJohannes Berg 261667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev, 262667503ddSJohannes Berg const u8 *buf, size_t len) 2636829c878SJohannes Berg { 2646829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 2656829c878SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 2666829c878SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 2676829c878SJohannes Berg struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 2686829c878SJohannes Berg u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); 2696829c878SJohannes Berg 270667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 271667503ddSJohannes Berg 2726829c878SJohannes Berg /* should only RX auth frames when connecting */ 2736829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 2746829c878SJohannes Berg return; 2756829c878SJohannes Berg 2766829c878SJohannes Berg if (WARN_ON(!wdev->conn)) 2776829c878SJohannes Berg return; 2786829c878SJohannes Berg 2796829c878SJohannes Berg if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && 2806829c878SJohannes Berg wdev->conn->auto_auth && 2816829c878SJohannes Berg wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { 2826829c878SJohannes Berg /* select automatically between only open, shared, leap */ 2836829c878SJohannes Berg switch (wdev->conn->params.auth_type) { 2846829c878SJohannes Berg case NL80211_AUTHTYPE_OPEN_SYSTEM: 285fffd0934SJohannes Berg if (wdev->connect_keys) 2866829c878SJohannes Berg wdev->conn->params.auth_type = 2876829c878SJohannes Berg NL80211_AUTHTYPE_SHARED_KEY; 288fffd0934SJohannes Berg else 289fffd0934SJohannes Berg wdev->conn->params.auth_type = 290fffd0934SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 2916829c878SJohannes Berg break; 2926829c878SJohannes Berg case NL80211_AUTHTYPE_SHARED_KEY: 2936829c878SJohannes Berg wdev->conn->params.auth_type = 2946829c878SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 2956829c878SJohannes Berg break; 2966829c878SJohannes Berg default: 2976829c878SJohannes Berg /* huh? */ 2986829c878SJohannes Berg wdev->conn->params.auth_type = 2996829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 3006829c878SJohannes Berg break; 3016829c878SJohannes Berg } 3026829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 3036829c878SJohannes Berg schedule_work(&rdev->conn_work); 30419957bb3SJohannes Berg } else if (status_code != WLAN_STATUS_SUCCESS) { 3054bde0f7dSJohannes Berg __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, 3064bde0f7dSJohannes Berg status_code, false); 30719957bb3SJohannes Berg } else if (wdev->sme_state == CFG80211_SME_CONNECTING && 3086829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { 3096829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 3106829c878SJohannes Berg schedule_work(&rdev->conn_work); 3116829c878SJohannes Berg } 3126829c878SJohannes Berg } 313b23aa676SSamuel Ortiz 314667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 315b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 316b23aa676SSamuel Ortiz const u8 *resp_ie, size_t resp_ie_len, 317667503ddSJohannes Berg u16 status, bool wextev) 318b23aa676SSamuel Ortiz { 319b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 320b23aa676SSamuel Ortiz struct cfg80211_bss *bss; 321b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 322b23aa676SSamuel Ortiz union iwreq_data wrqu; 323b23aa676SSamuel Ortiz #endif 324b23aa676SSamuel Ortiz 325667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 326667503ddSJohannes Berg 327b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 328b23aa676SSamuel Ortiz return; 329b23aa676SSamuel Ortiz 330e45cd82aSJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTED) 331e45cd82aSJohannes Berg nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, 332e45cd82aSJohannes Berg bssid, req_ie, req_ie_len, 333667503ddSJohannes Berg resp_ie, resp_ie_len, GFP_KERNEL); 334e45cd82aSJohannes Berg else 335e45cd82aSJohannes Berg nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, 336e45cd82aSJohannes Berg bssid, req_ie, req_ie_len, 337e45cd82aSJohannes Berg resp_ie, resp_ie_len, 338667503ddSJohannes Berg status, GFP_KERNEL); 339e45cd82aSJohannes Berg 340e45cd82aSJohannes Berg #ifdef CONFIG_WIRELESS_EXT 341e45cd82aSJohannes Berg if (wextev) { 342e45cd82aSJohannes Berg if (req_ie && status == WLAN_STATUS_SUCCESS) { 343e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 344e45cd82aSJohannes Berg wrqu.data.length = req_ie_len; 3453409ff77SZhu Yi wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); 346e45cd82aSJohannes Berg } 347e45cd82aSJohannes Berg 348e45cd82aSJohannes Berg if (resp_ie && status == WLAN_STATUS_SUCCESS) { 349e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 350e45cd82aSJohannes Berg wrqu.data.length = resp_ie_len; 351e45cd82aSJohannes Berg wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); 352e45cd82aSJohannes Berg } 353e45cd82aSJohannes Berg 354e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 355e45cd82aSJohannes Berg wrqu.ap_addr.sa_family = ARPHRD_ETHER; 356e45cd82aSJohannes Berg if (bssid && status == WLAN_STATUS_SUCCESS) 357e45cd82aSJohannes Berg memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 358e45cd82aSJohannes Berg wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 359e45cd82aSJohannes Berg } 360e45cd82aSJohannes Berg #endif 361e45cd82aSJohannes Berg 362e45cd82aSJohannes Berg if (status == WLAN_STATUS_SUCCESS && 363fffd0934SJohannes Berg wdev->sme_state == CFG80211_SME_IDLE) 364fffd0934SJohannes Berg goto success; 365e45cd82aSJohannes Berg 3666829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 367b23aa676SSamuel Ortiz return; 368b23aa676SSamuel Ortiz 369b23aa676SSamuel Ortiz if (wdev->current_bss) { 370b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 37119957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 372b23aa676SSamuel Ortiz wdev->current_bss = NULL; 373b23aa676SSamuel Ortiz } 374b23aa676SSamuel Ortiz 37519957bb3SJohannes Berg if (wdev->conn) 37619957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_IDLE; 37719957bb3SJohannes Berg 378fffd0934SJohannes Berg if (status != WLAN_STATUS_SUCCESS) { 379fffd0934SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 380fffd0934SJohannes Berg kfree(wdev->conn); 381fffd0934SJohannes Berg wdev->conn = NULL; 382fffd0934SJohannes Berg kfree(wdev->connect_keys); 383fffd0934SJohannes Berg wdev->connect_keys = NULL; 384fffd0934SJohannes Berg return; 385fffd0934SJohannes Berg } 386fffd0934SJohannes Berg 387b23aa676SSamuel Ortiz bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, 388b23aa676SSamuel Ortiz wdev->ssid, wdev->ssid_len, 389b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS, 390b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS); 391b23aa676SSamuel Ortiz 392b23aa676SSamuel Ortiz if (WARN_ON(!bss)) 393b23aa676SSamuel Ortiz return; 394b23aa676SSamuel Ortiz 39519957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 39619957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 397b23aa676SSamuel Ortiz 398fffd0934SJohannes Berg success: 399b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTED; 400fffd0934SJohannes Berg cfg80211_upload_connect_keys(wdev); 401b23aa676SSamuel Ortiz } 402f2129354SJohannes Berg 403f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 404f2129354SJohannes Berg const u8 *req_ie, size_t req_ie_len, 405f2129354SJohannes Berg const u8 *resp_ie, size_t resp_ie_len, 406f2129354SJohannes Berg u16 status, gfp_t gfp) 407f2129354SJohannes Berg { 408667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 409667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 410667503ddSJohannes Berg struct cfg80211_event *ev; 411667503ddSJohannes Berg unsigned long flags; 412667503ddSJohannes Berg 413667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 414667503ddSJohannes Berg if (!ev) 415667503ddSJohannes Berg return; 416667503ddSJohannes Berg 417667503ddSJohannes Berg ev->type = EVENT_CONNECT_RESULT; 418667503ddSJohannes Berg memcpy(ev->cr.bssid, bssid, ETH_ALEN); 419667503ddSJohannes Berg ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); 420667503ddSJohannes Berg ev->cr.req_ie_len = req_ie_len; 421667503ddSJohannes Berg memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); 422667503ddSJohannes Berg ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 423667503ddSJohannes Berg ev->cr.resp_ie_len = resp_ie_len; 424667503ddSJohannes Berg memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); 425667503ddSJohannes Berg ev->cr.status = status; 426667503ddSJohannes Berg 427667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 428667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 429667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 430667503ddSJohannes Berg schedule_work(&rdev->event_work); 431f2129354SJohannes Berg } 432b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result); 433b23aa676SSamuel Ortiz 434667503ddSJohannes Berg void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, 435b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 436667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len) 437b23aa676SSamuel Ortiz { 438b23aa676SSamuel Ortiz struct cfg80211_bss *bss; 439b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 440b23aa676SSamuel Ortiz union iwreq_data wrqu; 441b23aa676SSamuel Ortiz #endif 442b23aa676SSamuel Ortiz 443667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 444667503ddSJohannes Berg 445b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 446b23aa676SSamuel Ortiz return; 447b23aa676SSamuel Ortiz 448b23aa676SSamuel Ortiz if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) 449b23aa676SSamuel Ortiz return; 450b23aa676SSamuel Ortiz 451b23aa676SSamuel Ortiz /* internal error -- how did we get to CONNECTED w/o BSS? */ 452b23aa676SSamuel Ortiz if (WARN_ON(!wdev->current_bss)) { 453b23aa676SSamuel Ortiz return; 454b23aa676SSamuel Ortiz } 455b23aa676SSamuel Ortiz 456b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 45719957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 458b23aa676SSamuel Ortiz wdev->current_bss = NULL; 459b23aa676SSamuel Ortiz 460b23aa676SSamuel Ortiz bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, 461b23aa676SSamuel Ortiz wdev->ssid, wdev->ssid_len, 462b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); 463b23aa676SSamuel Ortiz 464b23aa676SSamuel Ortiz if (WARN_ON(!bss)) 465b23aa676SSamuel Ortiz return; 466b23aa676SSamuel Ortiz 46719957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 46819957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 469b23aa676SSamuel Ortiz 470667503ddSJohannes Berg nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid, 471667503ddSJohannes Berg req_ie, req_ie_len, resp_ie, resp_ie_len, 472667503ddSJohannes Berg GFP_KERNEL); 473b23aa676SSamuel Ortiz 474b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 475b23aa676SSamuel Ortiz if (req_ie) { 476b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 477b23aa676SSamuel Ortiz wrqu.data.length = req_ie_len; 4783409ff77SZhu Yi wireless_send_event(wdev->netdev, IWEVASSOCREQIE, 479667503ddSJohannes Berg &wrqu, req_ie); 480b23aa676SSamuel Ortiz } 481b23aa676SSamuel Ortiz 482b23aa676SSamuel Ortiz if (resp_ie) { 483b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 484b23aa676SSamuel Ortiz wrqu.data.length = resp_ie_len; 485667503ddSJohannes Berg wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, 486667503ddSJohannes Berg &wrqu, resp_ie); 487b23aa676SSamuel Ortiz } 488b23aa676SSamuel Ortiz 489b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 490b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 491b23aa676SSamuel Ortiz memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 492667503ddSJohannes Berg wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 493b23aa676SSamuel Ortiz #endif 494b23aa676SSamuel Ortiz } 495667503ddSJohannes Berg 496667503ddSJohannes Berg void cfg80211_roamed(struct net_device *dev, const u8 *bssid, 497667503ddSJohannes Berg const u8 *req_ie, size_t req_ie_len, 498667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) 499667503ddSJohannes Berg { 500667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 501667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 502667503ddSJohannes Berg struct cfg80211_event *ev; 503667503ddSJohannes Berg unsigned long flags; 504667503ddSJohannes Berg 505667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 506667503ddSJohannes Berg if (!ev) 507667503ddSJohannes Berg return; 508667503ddSJohannes Berg 509667503ddSJohannes Berg ev->type = EVENT_ROAMED; 510667503ddSJohannes Berg memcpy(ev->rm.bssid, bssid, ETH_ALEN); 511667503ddSJohannes Berg ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); 512667503ddSJohannes Berg ev->rm.req_ie_len = req_ie_len; 513667503ddSJohannes Berg memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); 514667503ddSJohannes Berg ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 515667503ddSJohannes Berg ev->rm.resp_ie_len = resp_ie_len; 516667503ddSJohannes Berg memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); 517667503ddSJohannes Berg 518667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 519667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 520667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 521667503ddSJohannes Berg schedule_work(&rdev->event_work); 522667503ddSJohannes Berg } 523b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_roamed); 524b23aa676SSamuel Ortiz 525667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, 5266829c878SJohannes Berg size_t ie_len, u16 reason, bool from_ap) 527b23aa676SSamuel Ortiz { 528b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 529fffd0934SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 530fffd0934SJohannes Berg int i; 531b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 532b23aa676SSamuel Ortiz union iwreq_data wrqu; 533b23aa676SSamuel Ortiz #endif 534b23aa676SSamuel Ortiz 535667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 536667503ddSJohannes Berg 537b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 538b23aa676SSamuel Ortiz return; 539b23aa676SSamuel Ortiz 540b23aa676SSamuel Ortiz if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) 541b23aa676SSamuel Ortiz return; 542b23aa676SSamuel Ortiz 543b23aa676SSamuel Ortiz if (wdev->current_bss) { 544b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 54519957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 546b23aa676SSamuel Ortiz } 547b23aa676SSamuel Ortiz 548b23aa676SSamuel Ortiz wdev->current_bss = NULL; 549b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 550b23aa676SSamuel Ortiz 5516829c878SJohannes Berg if (wdev->conn) { 5526829c878SJohannes Berg kfree(wdev->conn->ie); 5536829c878SJohannes Berg wdev->conn->ie = NULL; 55419957bb3SJohannes Berg kfree(wdev->conn); 55519957bb3SJohannes Berg wdev->conn = NULL; 5566829c878SJohannes Berg } 5576829c878SJohannes Berg 558fffd0934SJohannes Berg nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); 559fffd0934SJohannes Berg 560fffd0934SJohannes Berg /* 561fffd0934SJohannes Berg * Delete all the keys ... pairwise keys can't really 562fffd0934SJohannes Berg * exist any more anyway, but default keys might. 563fffd0934SJohannes Berg */ 564fffd0934SJohannes Berg if (rdev->ops->del_key) 565fffd0934SJohannes Berg for (i = 0; i < 6; i++) 566fffd0934SJohannes Berg rdev->ops->del_key(wdev->wiphy, dev, i, NULL); 567b23aa676SSamuel Ortiz 568b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 569b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 570b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 571b23aa676SSamuel Ortiz wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 572b23aa676SSamuel Ortiz #endif 573b23aa676SSamuel Ortiz } 574b23aa676SSamuel Ortiz 575b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason, 576b23aa676SSamuel Ortiz u8 *ie, size_t ie_len, gfp_t gfp) 577b23aa676SSamuel Ortiz { 578667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 579667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 580667503ddSJohannes Berg struct cfg80211_event *ev; 581667503ddSJohannes Berg unsigned long flags; 582667503ddSJohannes Berg 583667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + ie_len, gfp); 584667503ddSJohannes Berg if (!ev) 585667503ddSJohannes Berg return; 586667503ddSJohannes Berg 587667503ddSJohannes Berg ev->type = EVENT_DISCONNECTED; 588667503ddSJohannes Berg ev->dc.ie = ((u8 *)ev) + sizeof(*ev); 589667503ddSJohannes Berg ev->dc.ie_len = ie_len; 590667503ddSJohannes Berg memcpy((void *)ev->dc.ie, ie, ie_len); 591667503ddSJohannes Berg ev->dc.reason = reason; 592667503ddSJohannes Berg 593667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 594667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 595667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 596667503ddSJohannes Berg schedule_work(&rdev->event_work); 597b23aa676SSamuel Ortiz } 598b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected); 599b23aa676SSamuel Ortiz 600667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev, 601b23aa676SSamuel Ortiz struct net_device *dev, 602fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 603fffd0934SJohannes Berg struct cfg80211_cached_keys *connkeys) 604b23aa676SSamuel Ortiz { 605b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 606667503ddSJohannes Berg int err; 607667503ddSJohannes Berg 608667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 609b23aa676SSamuel Ortiz 610b23aa676SSamuel Ortiz if (wdev->sme_state != CFG80211_SME_IDLE) 611b23aa676SSamuel Ortiz return -EALREADY; 612b23aa676SSamuel Ortiz 613fffd0934SJohannes Berg if (WARN_ON(wdev->connect_keys)) { 614fffd0934SJohannes Berg kfree(wdev->connect_keys); 615fffd0934SJohannes Berg wdev->connect_keys = NULL; 616fffd0934SJohannes Berg } 617fffd0934SJohannes Berg 618fffd0934SJohannes Berg if (connkeys && connkeys->def >= 0) { 619fffd0934SJohannes Berg int idx; 620fffd0934SJohannes Berg 621fffd0934SJohannes Berg idx = connkeys->def; 622fffd0934SJohannes Berg /* If given a WEP key we may need it for shared key auth */ 623fffd0934SJohannes Berg if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 || 624fffd0934SJohannes Berg connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) { 625fffd0934SJohannes Berg connect->key_idx = idx; 626fffd0934SJohannes Berg connect->key = connkeys->params[idx].key; 627fffd0934SJohannes Berg connect->key_len = connkeys->params[idx].key_len; 628fffd0934SJohannes Berg } 629fffd0934SJohannes Berg } 630fffd0934SJohannes Berg 631b23aa676SSamuel Ortiz if (!rdev->ops->connect) { 6326829c878SJohannes Berg if (!rdev->ops->auth || !rdev->ops->assoc) 633b23aa676SSamuel Ortiz return -EOPNOTSUPP; 6346829c878SJohannes Berg 63519957bb3SJohannes Berg if (WARN_ON(wdev->conn)) 63619957bb3SJohannes Berg return -EINPROGRESS; 63719957bb3SJohannes Berg 6386829c878SJohannes Berg wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); 6396829c878SJohannes Berg if (!wdev->conn) 6406829c878SJohannes Berg return -ENOMEM; 6416829c878SJohannes Berg 6426829c878SJohannes Berg /* 6436829c878SJohannes Berg * Copy all parameters, and treat explicitly IEs, BSSID, SSID. 6446829c878SJohannes Berg */ 6456829c878SJohannes Berg memcpy(&wdev->conn->params, connect, sizeof(*connect)); 6466829c878SJohannes Berg if (connect->bssid) { 6476829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 6486829c878SJohannes Berg memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); 6496829c878SJohannes Berg } 6506829c878SJohannes Berg 6516829c878SJohannes Berg if (connect->ie) { 6526829c878SJohannes Berg wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, 6536829c878SJohannes Berg GFP_KERNEL); 6546829c878SJohannes Berg wdev->conn->params.ie = wdev->conn->ie; 65519957bb3SJohannes Berg if (!wdev->conn->ie) { 65619957bb3SJohannes Berg kfree(wdev->conn); 65719957bb3SJohannes Berg wdev->conn = NULL; 6586829c878SJohannes Berg return -ENOMEM; 6596829c878SJohannes Berg } 66019957bb3SJohannes Berg } 6616829c878SJohannes Berg 6626829c878SJohannes Berg if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { 6636829c878SJohannes Berg wdev->conn->auto_auth = true; 6646829c878SJohannes Berg /* start with open system ... should mostly work */ 6656829c878SJohannes Berg wdev->conn->params.auth_type = 6666829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 6676829c878SJohannes Berg } else { 6686829c878SJohannes Berg wdev->conn->auto_auth = false; 6696829c878SJohannes Berg } 6706829c878SJohannes Berg 6716829c878SJohannes Berg memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 6726829c878SJohannes Berg wdev->ssid_len = connect->ssid_len; 6736829c878SJohannes Berg wdev->conn->params.ssid = wdev->ssid; 6746829c878SJohannes Berg wdev->conn->params.ssid_len = connect->ssid_len; 6756829c878SJohannes Berg 6766829c878SJohannes Berg /* don't care about result -- but fill bssid & channel */ 6776829c878SJohannes Berg if (!wdev->conn->params.bssid || !wdev->conn->params.channel) 6786829c878SJohannes Berg cfg80211_get_conn_bss(wdev); 6796829c878SJohannes Berg 6806829c878SJohannes Berg wdev->sme_state = CFG80211_SME_CONNECTING; 681fffd0934SJohannes Berg wdev->connect_keys = connkeys; 6826829c878SJohannes Berg 6836829c878SJohannes Berg /* we're good if we have both BSSID and channel */ 6846829c878SJohannes Berg if (wdev->conn->params.bssid && wdev->conn->params.channel) { 6856829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 6866829c878SJohannes Berg err = cfg80211_conn_do_work(wdev); 6876829c878SJohannes Berg } else { 6886829c878SJohannes Berg /* otherwise we'll need to scan for the AP first */ 6896829c878SJohannes Berg err = cfg80211_conn_scan(wdev); 6906829c878SJohannes Berg /* 6916829c878SJohannes Berg * If we can't scan right now, then we need to scan again 6926829c878SJohannes Berg * after the current scan finished, since the parameters 6936829c878SJohannes Berg * changed (unless we find a good AP anyway). 6946829c878SJohannes Berg */ 6956829c878SJohannes Berg if (err == -EBUSY) { 6966829c878SJohannes Berg err = 0; 6976829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; 6986829c878SJohannes Berg } 6996829c878SJohannes Berg } 70019957bb3SJohannes Berg if (err) { 70119957bb3SJohannes Berg kfree(wdev->conn); 70219957bb3SJohannes Berg wdev->conn = NULL; 7036829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 704fffd0934SJohannes Berg wdev->connect_keys = NULL; 70519957bb3SJohannes Berg } 7066829c878SJohannes Berg 7076829c878SJohannes Berg return err; 708b23aa676SSamuel Ortiz } else { 709b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTING; 710fffd0934SJohannes Berg wdev->connect_keys = connkeys; 711b23aa676SSamuel Ortiz err = rdev->ops->connect(&rdev->wiphy, dev, connect); 712b23aa676SSamuel Ortiz if (err) { 713fffd0934SJohannes Berg wdev->connect_keys = NULL; 714b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 715b23aa676SSamuel Ortiz return err; 716b23aa676SSamuel Ortiz } 717b23aa676SSamuel Ortiz 718b23aa676SSamuel Ortiz memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 719b23aa676SSamuel Ortiz wdev->ssid_len = connect->ssid_len; 720b23aa676SSamuel Ortiz 721b23aa676SSamuel Ortiz return 0; 722b23aa676SSamuel Ortiz } 7236829c878SJohannes Berg } 724b23aa676SSamuel Ortiz 725667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev, 726667503ddSJohannes Berg struct net_device *dev, 727fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 728fffd0934SJohannes Berg struct cfg80211_cached_keys *connkeys) 729667503ddSJohannes Berg { 730667503ddSJohannes Berg int err; 731667503ddSJohannes Berg 732667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 733fffd0934SJohannes Berg err = __cfg80211_connect(rdev, dev, connect, connkeys); 734667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 735667503ddSJohannes Berg 736667503ddSJohannes Berg return err; 737667503ddSJohannes Berg } 738667503ddSJohannes Berg 739667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, 740f2129354SJohannes Berg struct net_device *dev, u16 reason, bool wextev) 741b23aa676SSamuel Ortiz { 7426829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 743b23aa676SSamuel Ortiz int err; 744b23aa676SSamuel Ortiz 745667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 746667503ddSJohannes Berg 7476829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_IDLE) 7486829c878SJohannes Berg return -EINVAL; 7496829c878SJohannes Berg 750fffd0934SJohannes Berg kfree(wdev->connect_keys); 751fffd0934SJohannes Berg wdev->connect_keys = NULL; 752fffd0934SJohannes Berg 753b23aa676SSamuel Ortiz if (!rdev->ops->disconnect) { 75419957bb3SJohannes Berg if (!rdev->ops->deauth) 75519957bb3SJohannes Berg return -EOPNOTSUPP; 7566829c878SJohannes Berg 75719957bb3SJohannes Berg /* was it connected by userspace SME? */ 75819957bb3SJohannes Berg if (!wdev->conn) { 75919957bb3SJohannes Berg cfg80211_mlme_down(rdev, dev); 76019957bb3SJohannes Berg return 0; 76119957bb3SJohannes Berg } 7626829c878SJohannes Berg 7636829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTING && 7646829c878SJohannes Berg (wdev->conn->state == CFG80211_CONN_SCANNING || 7656829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { 7666829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 76719957bb3SJohannes Berg kfree(wdev->conn); 76819957bb3SJohannes Berg wdev->conn = NULL; 7696829c878SJohannes Berg return 0; 7706829c878SJohannes Berg } 7716829c878SJohannes Berg 7726829c878SJohannes Berg /* wdev->conn->params.bssid must be set if > SCANNING */ 773667503ddSJohannes Berg err = __cfg80211_mlme_deauth(rdev, dev, 774667503ddSJohannes Berg wdev->conn->params.bssid, 77519957bb3SJohannes Berg NULL, 0, reason); 7766829c878SJohannes Berg if (err) 7776829c878SJohannes Berg return err; 778b23aa676SSamuel Ortiz } else { 779b23aa676SSamuel Ortiz err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); 780b23aa676SSamuel Ortiz if (err) 781b23aa676SSamuel Ortiz return err; 782b23aa676SSamuel Ortiz } 783b23aa676SSamuel Ortiz 7846829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTED) 785667503ddSJohannes Berg __cfg80211_disconnected(dev, NULL, 0, 0, false); 7866829c878SJohannes Berg else if (wdev->sme_state == CFG80211_SME_CONNECTING) 787f2129354SJohannes Berg __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, 7886829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 789667503ddSJohannes Berg wextev); 790b23aa676SSamuel Ortiz 791b23aa676SSamuel Ortiz return 0; 792b23aa676SSamuel Ortiz } 79319957bb3SJohannes Berg 794667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev, 795667503ddSJohannes Berg struct net_device *dev, 796667503ddSJohannes Berg u16 reason, bool wextev) 797667503ddSJohannes Berg { 798667503ddSJohannes Berg int err; 799667503ddSJohannes Berg 800667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 801667503ddSJohannes Berg err = __cfg80211_disconnect(rdev, dev, reason, wextev); 802667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 803667503ddSJohannes Berg 804667503ddSJohannes Berg return err; 805667503ddSJohannes Berg } 806667503ddSJohannes Berg 80719957bb3SJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev, int idx) 80819957bb3SJohannes Berg { 80919957bb3SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 81019957bb3SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 81119957bb3SJohannes Berg u8 bssid[ETH_ALEN]; 81219957bb3SJohannes Berg 813667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 814667503ddSJohannes Berg 81519957bb3SJohannes Berg if (!wdev->conn) 81619957bb3SJohannes Berg return; 81719957bb3SJohannes Berg 81819957bb3SJohannes Berg if (wdev->conn->state == CFG80211_CONN_IDLE) 81919957bb3SJohannes Berg return; 82019957bb3SJohannes Berg 82119957bb3SJohannes Berg /* 82219957bb3SJohannes Berg * Ok, so the association was made by this SME -- we don't 82319957bb3SJohannes Berg * want it any more so deauthenticate too. 82419957bb3SJohannes Berg */ 82519957bb3SJohannes Berg 82619957bb3SJohannes Berg if (!wdev->auth_bsses[idx]) 82719957bb3SJohannes Berg return; 82819957bb3SJohannes Berg 82919957bb3SJohannes Berg memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); 830ec3f1490SJohannes Berg if (__cfg80211_mlme_deauth(rdev, dev, bssid, 83119957bb3SJohannes Berg NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { 83219957bb3SJohannes Berg /* whatever -- assume gone anyway */ 83319957bb3SJohannes Berg cfg80211_unhold_bss(wdev->auth_bsses[idx]); 83419957bb3SJohannes Berg cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); 83519957bb3SJohannes Berg wdev->auth_bsses[idx] = NULL; 83619957bb3SJohannes Berg } 83719957bb3SJohannes Berg } 838