diff options
Diffstat (limited to 'net/wireless/sme.c')
-rw-r--r-- | net/wireless/sme.c | 395 |
1 files changed, 382 insertions, 13 deletions
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index fc117031d0bb..3abb04729873 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -12,6 +12,266 @@ | |||
12 | #include <net/rtnetlink.h> | 12 | #include <net/rtnetlink.h> |
13 | #include "nl80211.h" | 13 | #include "nl80211.h" |
14 | 14 | ||
15 | struct cfg80211_conn { | ||
16 | struct cfg80211_connect_params params; | ||
17 | /* these are sub-states of the _CONNECTING sme_state */ | ||
18 | enum { | ||
19 | CFG80211_CONN_IDLE, | ||
20 | CFG80211_CONN_SCANNING, | ||
21 | CFG80211_CONN_SCAN_AGAIN, | ||
22 | CFG80211_CONN_AUTHENTICATE_NEXT, | ||
23 | CFG80211_CONN_AUTHENTICATING, | ||
24 | CFG80211_CONN_ASSOCIATE_NEXT, | ||
25 | CFG80211_CONN_ASSOCIATING, | ||
26 | } state; | ||
27 | u8 bssid[ETH_ALEN]; | ||
28 | u8 *ie; | ||
29 | size_t ie_len; | ||
30 | bool auto_auth; | ||
31 | }; | ||
32 | |||
33 | |||
34 | static int cfg80211_conn_scan(struct wireless_dev *wdev) | ||
35 | { | ||
36 | struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); | ||
37 | struct cfg80211_scan_request *request; | ||
38 | int n_channels, err; | ||
39 | |||
40 | ASSERT_RTNL(); | ||
41 | |||
42 | if (drv->scan_req) | ||
43 | return -EBUSY; | ||
44 | |||
45 | if (wdev->conn->params.channel) { | ||
46 | n_channels = 1; | ||
47 | } else { | ||
48 | enum ieee80211_band band; | ||
49 | n_channels = 0; | ||
50 | |||
51 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
52 | if (!wdev->wiphy->bands[band]) | ||
53 | continue; | ||
54 | n_channels += wdev->wiphy->bands[band]->n_channels; | ||
55 | } | ||
56 | } | ||
57 | request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + | ||
58 | sizeof(request->channels[0]) * n_channels, | ||
59 | GFP_KERNEL); | ||
60 | if (!request) | ||
61 | return -ENOMEM; | ||
62 | |||
63 | request->channels = (void *)((char *)request + sizeof(*request)); | ||
64 | if (wdev->conn->params.channel) | ||
65 | request->channels[0] = wdev->conn->params.channel; | ||
66 | else { | ||
67 | int i = 0, j; | ||
68 | enum ieee80211_band band; | ||
69 | |||
70 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
71 | if (!wdev->wiphy->bands[band]) | ||
72 | continue; | ||
73 | for (j = 0; j < wdev->wiphy->bands[band]->n_channels; | ||
74 | i++, j++) | ||
75 | request->channels[i] = | ||
76 | &wdev->wiphy->bands[band]->channels[j]; | ||
77 | } | ||
78 | } | ||
79 | request->n_channels = n_channels; | ||
80 | request->ssids = (void *)(request->channels + n_channels); | ||
81 | request->n_ssids = 1; | ||
82 | |||
83 | memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, | ||
84 | wdev->conn->params.ssid_len); | ||
85 | request->ssids[0].ssid_len = wdev->conn->params.ssid_len; | ||
86 | |||
87 | request->ifidx = wdev->netdev->ifindex; | ||
88 | request->wiphy = &drv->wiphy; | ||
89 | |||
90 | drv->scan_req = request; | ||
91 | |||
92 | err = drv->ops->scan(wdev->wiphy, wdev->netdev, request); | ||
93 | if (!err) { | ||
94 | wdev->conn->state = CFG80211_CONN_SCANNING; | ||
95 | nl80211_send_scan_start(drv, wdev->netdev); | ||
96 | } else { | ||
97 | drv->scan_req = NULL; | ||
98 | kfree(request); | ||
99 | } | ||
100 | return err; | ||
101 | } | ||
102 | |||
103 | static int cfg80211_conn_do_work(struct wireless_dev *wdev) | ||
104 | { | ||
105 | struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); | ||
106 | union { | ||
107 | struct cfg80211_auth_request auth_req; | ||
108 | struct cfg80211_assoc_request assoc_req; | ||
109 | } u; | ||
110 | |||
111 | memset(&u, 0, sizeof(u)); | ||
112 | |||
113 | if (!wdev->conn) | ||
114 | return 0; | ||
115 | |||
116 | switch (wdev->conn->state) { | ||
117 | case CFG80211_CONN_SCAN_AGAIN: | ||
118 | return cfg80211_conn_scan(wdev); | ||
119 | case CFG80211_CONN_AUTHENTICATE_NEXT: | ||
120 | u.auth_req.chan = wdev->conn->params.channel; | ||
121 | u.auth_req.peer_addr = wdev->conn->params.bssid; | ||
122 | u.auth_req.ssid = wdev->conn->params.ssid; | ||
123 | u.auth_req.ssid_len = wdev->conn->params.ssid_len; | ||
124 | u.auth_req.auth_type = wdev->conn->params.auth_type; | ||
125 | u.auth_req.ie = NULL; | ||
126 | u.auth_req.ie_len = 0; | ||
127 | wdev->conn->state = CFG80211_CONN_AUTHENTICATING; | ||
128 | BUG_ON(!drv->ops->auth); | ||
129 | return drv->ops->auth(wdev->wiphy, wdev->netdev, &u.auth_req); | ||
130 | case CFG80211_CONN_ASSOCIATE_NEXT: | ||
131 | u.assoc_req.chan = wdev->conn->params.channel; | ||
132 | u.assoc_req.peer_addr = wdev->conn->params.bssid; | ||
133 | u.assoc_req.ssid = wdev->conn->params.ssid; | ||
134 | u.assoc_req.ssid_len = wdev->conn->params.ssid_len; | ||
135 | u.assoc_req.ie = wdev->conn->params.ie; | ||
136 | u.assoc_req.ie_len = wdev->conn->params.ie_len; | ||
137 | u.assoc_req.use_mfp = false; | ||
138 | memcpy(&u.assoc_req.crypto, &wdev->conn->params.crypto, | ||
139 | sizeof(u.assoc_req.crypto)); | ||
140 | wdev->conn->state = CFG80211_CONN_ASSOCIATING; | ||
141 | BUG_ON(!drv->ops->assoc); | ||
142 | return drv->ops->assoc(wdev->wiphy, wdev->netdev, | ||
143 | &u.assoc_req); | ||
144 | default: | ||
145 | return 0; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | void cfg80211_conn_work(struct work_struct *work) | ||
150 | { | ||
151 | struct cfg80211_registered_device *drv = | ||
152 | container_of(work, struct cfg80211_registered_device, conn_work); | ||
153 | struct wireless_dev *wdev; | ||
154 | |||
155 | rtnl_lock(); | ||
156 | mutex_lock(&drv->devlist_mtx); | ||
157 | |||
158 | list_for_each_entry(wdev, &drv->netdev_list, list) { | ||
159 | if (!netif_running(wdev->netdev)) | ||
160 | continue; | ||
161 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
162 | continue; | ||
163 | if (cfg80211_conn_do_work(wdev)) | ||
164 | cfg80211_connect_result(wdev->netdev, | ||
165 | wdev->conn->params.bssid, | ||
166 | NULL, 0, NULL, 0, | ||
167 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
168 | GFP_ATOMIC); | ||
169 | } | ||
170 | |||
171 | mutex_unlock(&drv->devlist_mtx); | ||
172 | rtnl_unlock(); | ||
173 | } | ||
174 | |||
175 | static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) | ||
176 | { | ||
177 | struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); | ||
178 | struct cfg80211_bss *bss; | ||
179 | u16 capa = WLAN_CAPABILITY_ESS; | ||
180 | |||
181 | if (wdev->conn->params.privacy) | ||
182 | capa |= WLAN_CAPABILITY_PRIVACY; | ||
183 | |||
184 | bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, | ||
185 | wdev->conn->params.ssid, | ||
186 | wdev->conn->params.ssid_len, | ||
187 | WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, | ||
188 | capa); | ||
189 | |||
190 | if (!bss) | ||
191 | return false; | ||
192 | |||
193 | memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); | ||
194 | wdev->conn->params.bssid = wdev->conn->bssid; | ||
195 | wdev->conn->params.channel = bss->channel; | ||
196 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
197 | schedule_work(&drv->conn_work); | ||
198 | |||
199 | cfg80211_put_bss(bss); | ||
200 | return true; | ||
201 | } | ||
202 | |||
203 | void cfg80211_sme_scan_done(struct net_device *dev) | ||
204 | { | ||
205 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
206 | struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); | ||
207 | |||
208 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
209 | return; | ||
210 | |||
211 | if (WARN_ON(!wdev->conn)) | ||
212 | return; | ||
213 | |||
214 | if (wdev->conn->state != CFG80211_CONN_SCANNING && | ||
215 | wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) | ||
216 | return; | ||
217 | |||
218 | if (!cfg80211_get_conn_bss(wdev)) { | ||
219 | /* not found */ | ||
220 | if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) | ||
221 | schedule_work(&drv->conn_work); | ||
222 | else | ||
223 | cfg80211_connect_result(dev, wdev->conn->params.bssid, | ||
224 | NULL, 0, NULL, 0, | ||
225 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
226 | GFP_ATOMIC); | ||
227 | return; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) | ||
232 | { | ||
233 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
234 | struct wiphy *wiphy = wdev->wiphy; | ||
235 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
236 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
237 | u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); | ||
238 | |||
239 | /* should only RX auth frames when connecting */ | ||
240 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
241 | return; | ||
242 | |||
243 | if (WARN_ON(!wdev->conn)) | ||
244 | return; | ||
245 | |||
246 | if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && | ||
247 | wdev->conn->auto_auth && | ||
248 | wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { | ||
249 | /* select automatically between only open, shared, leap */ | ||
250 | switch (wdev->conn->params.auth_type) { | ||
251 | case NL80211_AUTHTYPE_OPEN_SYSTEM: | ||
252 | wdev->conn->params.auth_type = | ||
253 | NL80211_AUTHTYPE_SHARED_KEY; | ||
254 | break; | ||
255 | case NL80211_AUTHTYPE_SHARED_KEY: | ||
256 | wdev->conn->params.auth_type = | ||
257 | NL80211_AUTHTYPE_NETWORK_EAP; | ||
258 | break; | ||
259 | default: | ||
260 | /* huh? */ | ||
261 | wdev->conn->params.auth_type = | ||
262 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
263 | break; | ||
264 | } | ||
265 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
266 | schedule_work(&rdev->conn_work); | ||
267 | } else if (status_code != WLAN_STATUS_SUCCESS) | ||
268 | wdev->sme_state = CFG80211_SME_IDLE; | ||
269 | else if (wdev->sme_state == CFG80211_SME_CONNECTING && | ||
270 | wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { | ||
271 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; | ||
272 | schedule_work(&rdev->conn_work); | ||
273 | } | ||
274 | } | ||
15 | 275 | ||
16 | void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | 276 | void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, |
17 | const u8 *req_ie, size_t req_ie_len, | 277 | const u8 *req_ie, size_t req_ie_len, |
@@ -27,7 +287,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
27 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | 287 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) |
28 | return; | 288 | return; |
29 | 289 | ||
30 | if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) | 290 | if (wdev->sme_state != CFG80211_SME_CONNECTING) |
31 | return; | 291 | return; |
32 | 292 | ||
33 | if (wdev->current_bss) { | 293 | if (wdev->current_bss) { |
@@ -53,6 +313,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
53 | wdev->sme_state = CFG80211_SME_IDLE; | 313 | wdev->sme_state = CFG80211_SME_IDLE; |
54 | } | 314 | } |
55 | 315 | ||
316 | if (wdev->conn) | ||
317 | wdev->conn->state = CFG80211_CONN_IDLE; | ||
318 | |||
56 | nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, | 319 | nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, |
57 | req_ie, req_ie_len, resp_ie, resp_ie_len, | 320 | req_ie, req_ie_len, resp_ie, resp_ie_len, |
58 | status, gfp); | 321 | status, gfp); |
@@ -72,7 +335,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
72 | 335 | ||
73 | memset(&wrqu, 0, sizeof(wrqu)); | 336 | memset(&wrqu, 0, sizeof(wrqu)); |
74 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | 337 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; |
75 | if (bssid) | 338 | if (bssid && status == WLAN_STATUS_SUCCESS) |
76 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); | 339 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); |
77 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | 340 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); |
78 | #endif | 341 | #endif |
@@ -138,9 +401,8 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, | |||
138 | } | 401 | } |
139 | EXPORT_SYMBOL(cfg80211_roamed); | 402 | EXPORT_SYMBOL(cfg80211_roamed); |
140 | 403 | ||
141 | static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, | 404 | void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, |
142 | u8 *ie, size_t ie_len, u16 reason, | 405 | size_t ie_len, u16 reason, bool from_ap) |
143 | bool from_ap) | ||
144 | { | 406 | { |
145 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 407 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
146 | #ifdef CONFIG_WIRELESS_EXT | 408 | #ifdef CONFIG_WIRELESS_EXT |
@@ -161,6 +423,11 @@ static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, | |||
161 | wdev->current_bss = NULL; | 423 | wdev->current_bss = NULL; |
162 | wdev->sme_state = CFG80211_SME_IDLE; | 424 | wdev->sme_state = CFG80211_SME_IDLE; |
163 | 425 | ||
426 | if (wdev->conn) { | ||
427 | kfree(wdev->conn->ie); | ||
428 | wdev->conn->ie = NULL; | ||
429 | } | ||
430 | |||
164 | nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, | 431 | nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, |
165 | reason, ie, ie_len, from_ap, gfp); | 432 | reason, ie, ie_len, from_ap, gfp); |
166 | 433 | ||
@@ -174,7 +441,7 @@ static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, | |||
174 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | 441 | void cfg80211_disconnected(struct net_device *dev, u16 reason, |
175 | u8 *ie, size_t ie_len, gfp_t gfp) | 442 | u8 *ie, size_t ie_len, gfp_t gfp) |
176 | { | 443 | { |
177 | __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); | 444 | __cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true); |
178 | } | 445 | } |
179 | EXPORT_SYMBOL(cfg80211_disconnected); | 446 | EXPORT_SYMBOL(cfg80211_disconnected); |
180 | 447 | ||
@@ -189,7 +456,74 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
189 | return -EALREADY; | 456 | return -EALREADY; |
190 | 457 | ||
191 | if (!rdev->ops->connect) { | 458 | if (!rdev->ops->connect) { |
192 | return -EOPNOTSUPP; | 459 | if (!rdev->ops->auth || !rdev->ops->assoc) |
460 | return -EOPNOTSUPP; | ||
461 | |||
462 | if (!wdev->conn) { | ||
463 | wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); | ||
464 | if (!wdev->conn) | ||
465 | return -ENOMEM; | ||
466 | } else | ||
467 | memset(wdev->conn, 0, sizeof(*wdev->conn)); | ||
468 | |||
469 | /* | ||
470 | * Copy all parameters, and treat explicitly IEs, BSSID, SSID. | ||
471 | */ | ||
472 | memcpy(&wdev->conn->params, connect, sizeof(*connect)); | ||
473 | if (connect->bssid) { | ||
474 | wdev->conn->params.bssid = wdev->conn->bssid; | ||
475 | memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); | ||
476 | } | ||
477 | |||
478 | if (connect->ie) { | ||
479 | wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, | ||
480 | GFP_KERNEL); | ||
481 | wdev->conn->params.ie = wdev->conn->ie; | ||
482 | if (!wdev->conn->ie) | ||
483 | return -ENOMEM; | ||
484 | } | ||
485 | |||
486 | if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { | ||
487 | wdev->conn->auto_auth = true; | ||
488 | /* start with open system ... should mostly work */ | ||
489 | wdev->conn->params.auth_type = | ||
490 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
491 | } else { | ||
492 | wdev->conn->auto_auth = false; | ||
493 | } | ||
494 | |||
495 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | ||
496 | wdev->ssid_len = connect->ssid_len; | ||
497 | wdev->conn->params.ssid = wdev->ssid; | ||
498 | wdev->conn->params.ssid_len = connect->ssid_len; | ||
499 | |||
500 | /* don't care about result -- but fill bssid & channel */ | ||
501 | if (!wdev->conn->params.bssid || !wdev->conn->params.channel) | ||
502 | cfg80211_get_conn_bss(wdev); | ||
503 | |||
504 | wdev->sme_state = CFG80211_SME_CONNECTING; | ||
505 | |||
506 | /* we're good if we have both BSSID and channel */ | ||
507 | if (wdev->conn->params.bssid && wdev->conn->params.channel) { | ||
508 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
509 | err = cfg80211_conn_do_work(wdev); | ||
510 | } else { | ||
511 | /* otherwise we'll need to scan for the AP first */ | ||
512 | err = cfg80211_conn_scan(wdev); | ||
513 | /* | ||
514 | * If we can't scan right now, then we need to scan again | ||
515 | * after the current scan finished, since the parameters | ||
516 | * changed (unless we find a good AP anyway). | ||
517 | */ | ||
518 | if (err == -EBUSY) { | ||
519 | err = 0; | ||
520 | wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; | ||
521 | } | ||
522 | } | ||
523 | if (err) | ||
524 | wdev->sme_state = CFG80211_SME_IDLE; | ||
525 | |||
526 | return err; | ||
193 | } else { | 527 | } else { |
194 | wdev->sme_state = CFG80211_SME_CONNECTING; | 528 | wdev->sme_state = CFG80211_SME_CONNECTING; |
195 | err = rdev->ops->connect(&rdev->wiphy, dev, connect); | 529 | err = rdev->ops->connect(&rdev->wiphy, dev, connect); |
@@ -197,28 +531,63 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
197 | wdev->sme_state = CFG80211_SME_IDLE; | 531 | wdev->sme_state = CFG80211_SME_IDLE; |
198 | return err; | 532 | return err; |
199 | } | 533 | } |
200 | } | ||
201 | 534 | ||
202 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | 535 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); |
203 | wdev->ssid_len = connect->ssid_len; | 536 | wdev->ssid_len = connect->ssid_len; |
204 | 537 | ||
205 | return 0; | 538 | return 0; |
539 | } | ||
206 | } | 540 | } |
207 | 541 | ||
208 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | 542 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, |
209 | struct net_device *dev, u16 reason) | 543 | struct net_device *dev, u16 reason) |
210 | { | 544 | { |
545 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
211 | int err; | 546 | int err; |
212 | 547 | ||
548 | if (wdev->sme_state == CFG80211_SME_IDLE) | ||
549 | return -EINVAL; | ||
550 | |||
213 | if (!rdev->ops->disconnect) { | 551 | if (!rdev->ops->disconnect) { |
214 | return -EOPNOTSUPP; | 552 | struct cfg80211_deauth_request deauth; |
553 | u8 bssid[ETH_ALEN]; | ||
554 | |||
555 | /* internal bug. */ | ||
556 | if (WARN_ON(!wdev->conn)) | ||
557 | return -EINVAL; | ||
558 | |||
559 | if (wdev->sme_state == CFG80211_SME_CONNECTING && | ||
560 | (wdev->conn->state == CFG80211_CONN_SCANNING || | ||
561 | wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { | ||
562 | wdev->sme_state = CFG80211_SME_IDLE; | ||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | if (!rdev->ops->deauth) | ||
567 | return -EOPNOTSUPP; | ||
568 | |||
569 | memset(&deauth, 0, sizeof(deauth)); | ||
570 | |||
571 | /* wdev->conn->params.bssid must be set if > SCANNING */ | ||
572 | memcpy(bssid, wdev->conn->params.bssid, ETH_ALEN); | ||
573 | deauth.peer_addr = bssid; | ||
574 | deauth.reason_code = reason; | ||
575 | |||
576 | err = rdev->ops->deauth(&rdev->wiphy, dev, &deauth); | ||
577 | if (err) | ||
578 | return err; | ||
215 | } else { | 579 | } else { |
216 | err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); | 580 | err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); |
217 | if (err) | 581 | if (err) |
218 | return err; | 582 | return err; |
219 | } | 583 | } |
220 | 584 | ||
221 | __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); | 585 | if (wdev->sme_state == CFG80211_SME_CONNECTED) |
586 | __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false); | ||
587 | else if (wdev->sme_state == CFG80211_SME_CONNECTING) | ||
588 | cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, | ||
589 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
590 | GFP_KERNEL); | ||
222 | 591 | ||
223 | return 0; | 592 | return 0; |
224 | } | 593 | } |