diff options
Diffstat (limited to 'net/wireless/sme.c')
-rw-r--r-- | net/wireless/sme.c | 542 |
1 files changed, 254 insertions, 288 deletions
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 81be95f3be74..ae7e2cbf45cb 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -1,5 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * SME code for cfg80211's connect emulation. | 2 | * SME code for cfg80211 |
3 | * both driver SME event handling and the SME implementation | ||
4 | * (for nl80211's connect() and wext) | ||
3 | * | 5 | * |
4 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | 6 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> |
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | 7 | * Copyright (C) 2009 Intel Corporation. All rights reserved. |
@@ -18,18 +20,24 @@ | |||
18 | #include "reg.h" | 20 | #include "reg.h" |
19 | #include "rdev-ops.h" | 21 | #include "rdev-ops.h" |
20 | 22 | ||
23 | /* | ||
24 | * Software SME in cfg80211, using auth/assoc/deauth calls to the | ||
25 | * driver. This is is for implementing nl80211's connect/disconnect | ||
26 | * and wireless extensions (if configured.) | ||
27 | */ | ||
28 | |||
21 | struct cfg80211_conn { | 29 | struct cfg80211_conn { |
22 | struct cfg80211_connect_params params; | 30 | struct cfg80211_connect_params params; |
23 | /* these are sub-states of the _CONNECTING sme_state */ | 31 | /* these are sub-states of the _CONNECTING sme_state */ |
24 | enum { | 32 | enum { |
25 | CFG80211_CONN_IDLE, | ||
26 | CFG80211_CONN_SCANNING, | 33 | CFG80211_CONN_SCANNING, |
27 | CFG80211_CONN_SCAN_AGAIN, | 34 | CFG80211_CONN_SCAN_AGAIN, |
28 | CFG80211_CONN_AUTHENTICATE_NEXT, | 35 | CFG80211_CONN_AUTHENTICATE_NEXT, |
29 | CFG80211_CONN_AUTHENTICATING, | 36 | CFG80211_CONN_AUTHENTICATING, |
30 | CFG80211_CONN_ASSOCIATE_NEXT, | 37 | CFG80211_CONN_ASSOCIATE_NEXT, |
31 | CFG80211_CONN_ASSOCIATING, | 38 | CFG80211_CONN_ASSOCIATING, |
32 | CFG80211_CONN_DEAUTH_ASSOC_FAIL, | 39 | CFG80211_CONN_DEAUTH, |
40 | CFG80211_CONN_CONNECTED, | ||
33 | } state; | 41 | } state; |
34 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; | 42 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; |
35 | u8 *ie; | 43 | u8 *ie; |
@@ -37,39 +45,16 @@ struct cfg80211_conn { | |||
37 | bool auto_auth, prev_bssid_valid; | 45 | bool auto_auth, prev_bssid_valid; |
38 | }; | 46 | }; |
39 | 47 | ||
40 | static bool cfg80211_is_all_idle(void) | 48 | static void cfg80211_sme_free(struct wireless_dev *wdev) |
41 | { | 49 | { |
42 | struct cfg80211_registered_device *rdev; | 50 | if (!wdev->conn) |
43 | struct wireless_dev *wdev; | 51 | return; |
44 | bool is_all_idle = true; | ||
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 | list_for_each_entry(wdev, &rdev->wdev_list, list) { | ||
53 | wdev_lock(wdev); | ||
54 | if (wdev->sme_state != CFG80211_SME_IDLE) | ||
55 | is_all_idle = false; | ||
56 | wdev_unlock(wdev); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | return is_all_idle; | ||
61 | } | ||
62 | 52 | ||
63 | static void disconnect_work(struct work_struct *work) | 53 | kfree(wdev->conn->ie); |
64 | { | 54 | kfree(wdev->conn); |
65 | rtnl_lock(); | 55 | wdev->conn = NULL; |
66 | if (cfg80211_is_all_idle()) | ||
67 | regulatory_hint_disconnect(); | ||
68 | rtnl_unlock(); | ||
69 | } | 56 | } |
70 | 57 | ||
71 | static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); | ||
72 | |||
73 | static int cfg80211_conn_scan(struct wireless_dev *wdev) | 58 | static int cfg80211_conn_scan(struct wireless_dev *wdev) |
74 | { | 59 | { |
75 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | 60 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); |
@@ -164,6 +149,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) | |||
164 | params = &wdev->conn->params; | 149 | params = &wdev->conn->params; |
165 | 150 | ||
166 | switch (wdev->conn->state) { | 151 | switch (wdev->conn->state) { |
152 | case CFG80211_CONN_SCANNING: | ||
153 | /* didn't find it during scan ... */ | ||
154 | return -ENOENT; | ||
167 | case CFG80211_CONN_SCAN_AGAIN: | 155 | case CFG80211_CONN_SCAN_AGAIN: |
168 | return cfg80211_conn_scan(wdev); | 156 | return cfg80211_conn_scan(wdev); |
169 | case CFG80211_CONN_AUTHENTICATE_NEXT: | 157 | case CFG80211_CONN_AUTHENTICATE_NEXT: |
@@ -200,12 +188,11 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) | |||
200 | WLAN_REASON_DEAUTH_LEAVING, | 188 | WLAN_REASON_DEAUTH_LEAVING, |
201 | false); | 189 | false); |
202 | return err; | 190 | return err; |
203 | case CFG80211_CONN_DEAUTH_ASSOC_FAIL: | 191 | case CFG80211_CONN_DEAUTH: |
204 | cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, | 192 | cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, |
205 | NULL, 0, | 193 | NULL, 0, |
206 | WLAN_REASON_DEAUTH_LEAVING, false); | 194 | WLAN_REASON_DEAUTH_LEAVING, false); |
207 | /* return an error so that we call __cfg80211_connect_result() */ | 195 | return 0; |
208 | return -EINVAL; | ||
209 | default: | 196 | default: |
210 | return 0; | 197 | return 0; |
211 | } | 198 | } |
@@ -229,7 +216,8 @@ void cfg80211_conn_work(struct work_struct *work) | |||
229 | wdev_unlock(wdev); | 216 | wdev_unlock(wdev); |
230 | continue; | 217 | continue; |
231 | } | 218 | } |
232 | if (wdev->sme_state != CFG80211_SME_CONNECTING || !wdev->conn) { | 219 | if (!wdev->conn || |
220 | wdev->conn->state == CFG80211_CONN_CONNECTED) { | ||
233 | wdev_unlock(wdev); | 221 | wdev_unlock(wdev); |
234 | continue; | 222 | continue; |
235 | } | 223 | } |
@@ -237,12 +225,14 @@ void cfg80211_conn_work(struct work_struct *work) | |||
237 | memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); | 225 | memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); |
238 | bssid = bssid_buf; | 226 | bssid = bssid_buf; |
239 | } | 227 | } |
240 | if (cfg80211_conn_do_work(wdev)) | 228 | if (cfg80211_conn_do_work(wdev)) { |
241 | __cfg80211_connect_result( | 229 | __cfg80211_connect_result( |
242 | wdev->netdev, bssid, | 230 | wdev->netdev, bssid, |
243 | NULL, 0, NULL, 0, | 231 | NULL, 0, NULL, 0, |
244 | WLAN_STATUS_UNSPECIFIED_FAILURE, | 232 | WLAN_STATUS_UNSPECIFIED_FAILURE, |
245 | false, NULL); | 233 | false, NULL); |
234 | cfg80211_sme_free(wdev); | ||
235 | } | ||
246 | wdev_unlock(wdev); | 236 | wdev_unlock(wdev); |
247 | } | 237 | } |
248 | 238 | ||
@@ -286,9 +276,6 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) | |||
286 | 276 | ||
287 | ASSERT_WDEV_LOCK(wdev); | 277 | ASSERT_WDEV_LOCK(wdev); |
288 | 278 | ||
289 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
290 | return; | ||
291 | |||
292 | if (!wdev->conn) | 279 | if (!wdev->conn) |
293 | return; | 280 | return; |
294 | 281 | ||
@@ -297,20 +284,10 @@ static void __cfg80211_sme_scan_done(struct net_device *dev) | |||
297 | return; | 284 | return; |
298 | 285 | ||
299 | bss = cfg80211_get_conn_bss(wdev); | 286 | bss = cfg80211_get_conn_bss(wdev); |
300 | if (bss) { | 287 | if (bss) |
301 | cfg80211_put_bss(&rdev->wiphy, bss); | 288 | cfg80211_put_bss(&rdev->wiphy, bss); |
302 | } else { | 289 | else |
303 | /* not found */ | 290 | schedule_work(&rdev->conn_work); |
304 | if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) | ||
305 | schedule_work(&rdev->conn_work); | ||
306 | else | ||
307 | __cfg80211_connect_result( | ||
308 | wdev->netdev, | ||
309 | wdev->conn->params.bssid, | ||
310 | NULL, 0, NULL, 0, | ||
311 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
312 | false, NULL); | ||
313 | } | ||
314 | } | 291 | } |
315 | 292 | ||
316 | void cfg80211_sme_scan_done(struct net_device *dev) | 293 | void cfg80211_sme_scan_done(struct net_device *dev) |
@@ -322,10 +299,8 @@ void cfg80211_sme_scan_done(struct net_device *dev) | |||
322 | wdev_unlock(wdev); | 299 | wdev_unlock(wdev); |
323 | } | 300 | } |
324 | 301 | ||
325 | void cfg80211_sme_rx_auth(struct net_device *dev, | 302 | void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) |
326 | const u8 *buf, size_t len) | ||
327 | { | 303 | { |
328 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
329 | struct wiphy *wiphy = wdev->wiphy; | 304 | struct wiphy *wiphy = wdev->wiphy; |
330 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 305 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
331 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | 306 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; |
@@ -333,11 +308,7 @@ void cfg80211_sme_rx_auth(struct net_device *dev, | |||
333 | 308 | ||
334 | ASSERT_WDEV_LOCK(wdev); | 309 | ASSERT_WDEV_LOCK(wdev); |
335 | 310 | ||
336 | /* should only RX auth frames when connecting */ | 311 | if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED) |
337 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
338 | return; | ||
339 | |||
340 | if (WARN_ON(!wdev->conn)) | ||
341 | return; | 312 | return; |
342 | 313 | ||
343 | if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && | 314 | if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && |
@@ -366,46 +337,226 @@ void cfg80211_sme_rx_auth(struct net_device *dev, | |||
366 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | 337 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; |
367 | schedule_work(&rdev->conn_work); | 338 | schedule_work(&rdev->conn_work); |
368 | } else if (status_code != WLAN_STATUS_SUCCESS) { | 339 | } else if (status_code != WLAN_STATUS_SUCCESS) { |
369 | __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, | 340 | __cfg80211_connect_result(wdev->netdev, mgmt->bssid, |
341 | NULL, 0, NULL, 0, | ||
370 | status_code, false, NULL); | 342 | status_code, false, NULL); |
371 | } else if (wdev->sme_state == CFG80211_SME_CONNECTING && | 343 | } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { |
372 | wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { | ||
373 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; | 344 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; |
374 | schedule_work(&rdev->conn_work); | 345 | schedule_work(&rdev->conn_work); |
375 | } | 346 | } |
376 | } | 347 | } |
377 | 348 | ||
378 | bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) | 349 | bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status) |
379 | { | 350 | { |
380 | struct wiphy *wiphy = wdev->wiphy; | 351 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); |
381 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
382 | 352 | ||
383 | if (WARN_ON(!wdev->conn)) | 353 | if (!wdev->conn) |
384 | return false; | 354 | return false; |
385 | 355 | ||
386 | if (!wdev->conn->prev_bssid_valid) | 356 | if (status == WLAN_STATUS_SUCCESS) { |
357 | wdev->conn->state = CFG80211_CONN_CONNECTED; | ||
387 | return false; | 358 | return false; |
359 | } | ||
388 | 360 | ||
389 | /* | 361 | if (wdev->conn->prev_bssid_valid) { |
390 | * Some stupid APs don't accept reassoc, so we | 362 | /* |
391 | * need to fall back to trying regular assoc. | 363 | * Some stupid APs don't accept reassoc, so we |
392 | */ | 364 | * need to fall back to trying regular assoc; |
393 | wdev->conn->prev_bssid_valid = false; | 365 | * return true so no event is sent to userspace. |
394 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; | 366 | */ |
367 | wdev->conn->prev_bssid_valid = false; | ||
368 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; | ||
369 | schedule_work(&rdev->conn_work); | ||
370 | return true; | ||
371 | } | ||
372 | |||
373 | wdev->conn->state = CFG80211_CONN_DEAUTH; | ||
395 | schedule_work(&rdev->conn_work); | 374 | schedule_work(&rdev->conn_work); |
375 | return false; | ||
376 | } | ||
396 | 377 | ||
397 | return true; | 378 | void cfg80211_sme_deauth(struct wireless_dev *wdev) |
379 | { | ||
380 | cfg80211_sme_free(wdev); | ||
398 | } | 381 | } |
399 | 382 | ||
400 | void cfg80211_sme_failed_assoc(struct wireless_dev *wdev) | 383 | void cfg80211_sme_auth_timeout(struct wireless_dev *wdev) |
401 | { | 384 | { |
402 | struct wiphy *wiphy = wdev->wiphy; | 385 | cfg80211_sme_free(wdev); |
403 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 386 | } |
404 | 387 | ||
405 | wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL; | 388 | void cfg80211_sme_disassoc(struct wireless_dev *wdev) |
389 | { | ||
390 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | ||
391 | |||
392 | if (!wdev->conn) | ||
393 | return; | ||
394 | |||
395 | wdev->conn->state = CFG80211_CONN_DEAUTH; | ||
406 | schedule_work(&rdev->conn_work); | 396 | schedule_work(&rdev->conn_work); |
407 | } | 397 | } |
408 | 398 | ||
399 | void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev) | ||
400 | { | ||
401 | cfg80211_sme_disassoc(wdev); | ||
402 | } | ||
403 | |||
404 | static int cfg80211_sme_connect(struct wireless_dev *wdev, | ||
405 | struct cfg80211_connect_params *connect, | ||
406 | const u8 *prev_bssid) | ||
407 | { | ||
408 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | ||
409 | struct cfg80211_bss *bss; | ||
410 | int err; | ||
411 | |||
412 | if (!rdev->ops->auth || !rdev->ops->assoc) | ||
413 | return -EOPNOTSUPP; | ||
414 | |||
415 | if (wdev->current_bss) | ||
416 | return -EALREADY; | ||
417 | |||
418 | if (WARN_ON(wdev->conn)) | ||
419 | return -EINPROGRESS; | ||
420 | |||
421 | wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); | ||
422 | if (!wdev->conn) | ||
423 | return -ENOMEM; | ||
424 | |||
425 | /* | ||
426 | * Copy all parameters, and treat explicitly IEs, BSSID, SSID. | ||
427 | */ | ||
428 | memcpy(&wdev->conn->params, connect, sizeof(*connect)); | ||
429 | if (connect->bssid) { | ||
430 | wdev->conn->params.bssid = wdev->conn->bssid; | ||
431 | memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); | ||
432 | } | ||
433 | |||
434 | if (connect->ie) { | ||
435 | wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, | ||
436 | GFP_KERNEL); | ||
437 | wdev->conn->params.ie = wdev->conn->ie; | ||
438 | if (!wdev->conn->ie) { | ||
439 | kfree(wdev->conn); | ||
440 | wdev->conn = NULL; | ||
441 | return -ENOMEM; | ||
442 | } | ||
443 | } | ||
444 | |||
445 | if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { | ||
446 | wdev->conn->auto_auth = true; | ||
447 | /* start with open system ... should mostly work */ | ||
448 | wdev->conn->params.auth_type = | ||
449 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
450 | } else { | ||
451 | wdev->conn->auto_auth = false; | ||
452 | } | ||
453 | |||
454 | wdev->conn->params.ssid = wdev->ssid; | ||
455 | wdev->conn->params.ssid_len = connect->ssid_len; | ||
456 | |||
457 | /* see if we have the bss already */ | ||
458 | bss = cfg80211_get_conn_bss(wdev); | ||
459 | |||
460 | if (prev_bssid) { | ||
461 | memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); | ||
462 | wdev->conn->prev_bssid_valid = true; | ||
463 | } | ||
464 | |||
465 | /* we're good if we have a matching bss struct */ | ||
466 | if (bss) { | ||
467 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
468 | err = cfg80211_conn_do_work(wdev); | ||
469 | cfg80211_put_bss(wdev->wiphy, bss); | ||
470 | } else { | ||
471 | /* otherwise we'll need to scan for the AP first */ | ||
472 | err = cfg80211_conn_scan(wdev); | ||
473 | |||
474 | /* | ||
475 | * If we can't scan right now, then we need to scan again | ||
476 | * after the current scan finished, since the parameters | ||
477 | * changed (unless we find a good AP anyway). | ||
478 | */ | ||
479 | if (err == -EBUSY) { | ||
480 | err = 0; | ||
481 | wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | if (err) | ||
486 | cfg80211_sme_free(wdev); | ||
487 | |||
488 | return err; | ||
489 | } | ||
490 | |||
491 | static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason) | ||
492 | { | ||
493 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | ||
494 | int err; | ||
495 | |||
496 | if (!wdev->conn) | ||
497 | return 0; | ||
498 | |||
499 | if (!rdev->ops->deauth) | ||
500 | return -EOPNOTSUPP; | ||
501 | |||
502 | if (wdev->conn->state == CFG80211_CONN_SCANNING || | ||
503 | wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) { | ||
504 | err = 0; | ||
505 | goto out; | ||
506 | } | ||
507 | |||
508 | /* wdev->conn->params.bssid must be set if > SCANNING */ | ||
509 | err = cfg80211_mlme_deauth(rdev, wdev->netdev, | ||
510 | wdev->conn->params.bssid, | ||
511 | NULL, 0, reason, false); | ||
512 | out: | ||
513 | cfg80211_sme_free(wdev); | ||
514 | return err; | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | * code shared for in-device and software SME | ||
519 | */ | ||
520 | |||
521 | static bool cfg80211_is_all_idle(void) | ||
522 | { | ||
523 | struct cfg80211_registered_device *rdev; | ||
524 | struct wireless_dev *wdev; | ||
525 | bool is_all_idle = true; | ||
526 | |||
527 | /* | ||
528 | * All devices must be idle as otherwise if you are actively | ||
529 | * scanning some new beacon hints could be learned and would | ||
530 | * count as new regulatory hints. | ||
531 | */ | ||
532 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | ||
533 | list_for_each_entry(wdev, &rdev->wdev_list, list) { | ||
534 | wdev_lock(wdev); | ||
535 | if (wdev->conn || wdev->current_bss) | ||
536 | is_all_idle = false; | ||
537 | wdev_unlock(wdev); | ||
538 | } | ||
539 | } | ||
540 | |||
541 | return is_all_idle; | ||
542 | } | ||
543 | |||
544 | static void disconnect_work(struct work_struct *work) | ||
545 | { | ||
546 | rtnl_lock(); | ||
547 | if (cfg80211_is_all_idle()) | ||
548 | regulatory_hint_disconnect(); | ||
549 | rtnl_unlock(); | ||
550 | } | ||
551 | |||
552 | static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); | ||
553 | |||
554 | |||
555 | /* | ||
556 | * API calls for drivers implementing connect/disconnect and | ||
557 | * SME event handling | ||
558 | */ | ||
559 | |||
409 | void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | 560 | void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, |
410 | const u8 *req_ie, size_t req_ie_len, | 561 | const u8 *req_ie, size_t req_ie_len, |
411 | const u8 *resp_ie, size_t resp_ie_len, | 562 | const u8 *resp_ie, size_t resp_ie_len, |
@@ -424,9 +575,6 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
424 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) | 575 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) |
425 | return; | 576 | return; |
426 | 577 | ||
427 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
428 | return; | ||
429 | |||
430 | nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, | 578 | nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, |
431 | bssid, req_ie, req_ie_len, | 579 | bssid, req_ie, req_ie_len, |
432 | resp_ie, resp_ie_len, | 580 | resp_ie, resp_ie_len, |
@@ -463,15 +611,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
463 | wdev->current_bss = NULL; | 611 | wdev->current_bss = NULL; |
464 | } | 612 | } |
465 | 613 | ||
466 | if (wdev->conn) | ||
467 | wdev->conn->state = CFG80211_CONN_IDLE; | ||
468 | |||
469 | if (status != WLAN_STATUS_SUCCESS) { | 614 | if (status != WLAN_STATUS_SUCCESS) { |
470 | wdev->sme_state = CFG80211_SME_IDLE; | ||
471 | if (wdev->conn) | ||
472 | kfree(wdev->conn->ie); | ||
473 | kfree(wdev->conn); | ||
474 | wdev->conn = NULL; | ||
475 | kfree(wdev->connect_keys); | 615 | kfree(wdev->connect_keys); |
476 | wdev->connect_keys = NULL; | 616 | wdev->connect_keys = NULL; |
477 | wdev->ssid_len = 0; | 617 | wdev->ssid_len = 0; |
@@ -480,21 +620,16 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
480 | } | 620 | } |
481 | 621 | ||
482 | if (!bss) | 622 | if (!bss) |
483 | bss = cfg80211_get_bss(wdev->wiphy, | 623 | bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, |
484 | wdev->conn ? wdev->conn->params.channel : | ||
485 | NULL, | ||
486 | bssid, | ||
487 | wdev->ssid, wdev->ssid_len, | 624 | wdev->ssid, wdev->ssid_len, |
488 | WLAN_CAPABILITY_ESS, | 625 | WLAN_CAPABILITY_ESS, |
489 | WLAN_CAPABILITY_ESS); | 626 | WLAN_CAPABILITY_ESS); |
490 | |||
491 | if (WARN_ON(!bss)) | 627 | if (WARN_ON(!bss)) |
492 | return; | 628 | return; |
493 | 629 | ||
494 | cfg80211_hold_bss(bss_from_pub(bss)); | 630 | cfg80211_hold_bss(bss_from_pub(bss)); |
495 | wdev->current_bss = bss_from_pub(bss); | 631 | wdev->current_bss = bss_from_pub(bss); |
496 | 632 | ||
497 | wdev->sme_state = CFG80211_SME_CONNECTED; | ||
498 | cfg80211_upload_connect_keys(wdev); | 633 | cfg80211_upload_connect_keys(wdev); |
499 | 634 | ||
500 | rcu_read_lock(); | 635 | rcu_read_lock(); |
@@ -530,8 +665,6 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
530 | struct cfg80211_event *ev; | 665 | struct cfg80211_event *ev; |
531 | unsigned long flags; | 666 | unsigned long flags; |
532 | 667 | ||
533 | CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); | ||
534 | |||
535 | ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); | 668 | ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); |
536 | if (!ev) | 669 | if (!ev) |
537 | return; | 670 | return; |
@@ -572,13 +705,8 @@ void __cfg80211_roamed(struct wireless_dev *wdev, | |||
572 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) | 705 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) |
573 | goto out; | 706 | goto out; |
574 | 707 | ||
575 | if (wdev->sme_state != CFG80211_SME_CONNECTED) | 708 | if (WARN_ON(!wdev->current_bss)) |
576 | goto out; | ||
577 | |||
578 | /* internal error -- how did we get to CONNECTED w/o BSS? */ | ||
579 | if (WARN_ON(!wdev->current_bss)) { | ||
580 | goto out; | 709 | goto out; |
581 | } | ||
582 | 710 | ||
583 | cfg80211_unhold_bss(wdev->current_bss); | 711 | cfg80211_unhold_bss(wdev->current_bss); |
584 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); | 712 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); |
@@ -628,8 +756,6 @@ void cfg80211_roamed(struct net_device *dev, | |||
628 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 756 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
629 | struct cfg80211_bss *bss; | 757 | struct cfg80211_bss *bss; |
630 | 758 | ||
631 | CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); | ||
632 | |||
633 | bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, | 759 | bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, |
634 | wdev->ssid_len, WLAN_CAPABILITY_ESS, | 760 | wdev->ssid_len, WLAN_CAPABILITY_ESS, |
635 | WLAN_CAPABILITY_ESS); | 761 | WLAN_CAPABILITY_ESS); |
@@ -651,8 +777,6 @@ void cfg80211_roamed_bss(struct net_device *dev, | |||
651 | struct cfg80211_event *ev; | 777 | struct cfg80211_event *ev; |
652 | unsigned long flags; | 778 | unsigned long flags; |
653 | 779 | ||
654 | CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); | ||
655 | |||
656 | if (WARN_ON(!bss)) | 780 | if (WARN_ON(!bss)) |
657 | return; | 781 | return; |
658 | 782 | ||
@@ -694,25 +818,14 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, | |||
694 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) | 818 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) |
695 | return; | 819 | return; |
696 | 820 | ||
697 | if (wdev->sme_state != CFG80211_SME_CONNECTED) | ||
698 | return; | ||
699 | |||
700 | if (wdev->current_bss) { | 821 | if (wdev->current_bss) { |
701 | cfg80211_unhold_bss(wdev->current_bss); | 822 | cfg80211_unhold_bss(wdev->current_bss); |
702 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); | 823 | cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); |
703 | } | 824 | } |
704 | 825 | ||
705 | wdev->current_bss = NULL; | 826 | wdev->current_bss = NULL; |
706 | wdev->sme_state = CFG80211_SME_IDLE; | ||
707 | wdev->ssid_len = 0; | 827 | wdev->ssid_len = 0; |
708 | 828 | ||
709 | if (wdev->conn) { | ||
710 | kfree(wdev->conn->ie); | ||
711 | wdev->conn->ie = NULL; | ||
712 | kfree(wdev->conn); | ||
713 | wdev->conn = NULL; | ||
714 | } | ||
715 | |||
716 | nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); | 829 | nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); |
717 | 830 | ||
718 | /* | 831 | /* |
@@ -741,8 +854,6 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, | |||
741 | struct cfg80211_event *ev; | 854 | struct cfg80211_event *ev; |
742 | unsigned long flags; | 855 | unsigned long flags; |
743 | 856 | ||
744 | CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); | ||
745 | |||
746 | ev = kzalloc(sizeof(*ev) + ie_len, gfp); | 857 | ev = kzalloc(sizeof(*ev) + ie_len, gfp); |
747 | if (!ev) | 858 | if (!ev) |
748 | return; | 859 | return; |
@@ -760,6 +871,9 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, | |||
760 | } | 871 | } |
761 | EXPORT_SYMBOL(cfg80211_disconnected); | 872 | EXPORT_SYMBOL(cfg80211_disconnected); |
762 | 873 | ||
874 | /* | ||
875 | * API calls for nl80211/wext compatibility code | ||
876 | */ | ||
763 | int cfg80211_connect(struct cfg80211_registered_device *rdev, | 877 | int cfg80211_connect(struct cfg80211_registered_device *rdev, |
764 | struct net_device *dev, | 878 | struct net_device *dev, |
765 | struct cfg80211_connect_params *connect, | 879 | struct cfg80211_connect_params *connect, |
@@ -767,14 +881,10 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
767 | const u8 *prev_bssid) | 881 | const u8 *prev_bssid) |
768 | { | 882 | { |
769 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 883 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
770 | struct cfg80211_bss *bss = NULL; | ||
771 | int err; | 884 | int err; |
772 | 885 | ||
773 | ASSERT_WDEV_LOCK(wdev); | 886 | ASSERT_WDEV_LOCK(wdev); |
774 | 887 | ||
775 | if (wdev->sme_state != CFG80211_SME_IDLE) | ||
776 | return -EALREADY; | ||
777 | |||
778 | if (WARN_ON(wdev->connect_keys)) { | 888 | if (WARN_ON(wdev->connect_keys)) { |
779 | kfree(wdev->connect_keys); | 889 | kfree(wdev->connect_keys); |
780 | wdev->connect_keys = NULL; | 890 | wdev->connect_keys = NULL; |
@@ -810,105 +920,22 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
810 | } | 920 | } |
811 | } | 921 | } |
812 | 922 | ||
813 | if (!rdev->ops->connect) { | 923 | wdev->connect_keys = connkeys; |
814 | if (!rdev->ops->auth || !rdev->ops->assoc) | 924 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); |
815 | return -EOPNOTSUPP; | 925 | wdev->ssid_len = connect->ssid_len; |
816 | |||
817 | if (WARN_ON(wdev->conn)) | ||
818 | return -EINPROGRESS; | ||
819 | |||
820 | wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); | ||
821 | if (!wdev->conn) | ||
822 | return -ENOMEM; | ||
823 | |||
824 | /* | ||
825 | * Copy all parameters, and treat explicitly IEs, BSSID, SSID. | ||
826 | */ | ||
827 | memcpy(&wdev->conn->params, connect, sizeof(*connect)); | ||
828 | if (connect->bssid) { | ||
829 | wdev->conn->params.bssid = wdev->conn->bssid; | ||
830 | memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); | ||
831 | } | ||
832 | 926 | ||
833 | if (connect->ie) { | 927 | if (!rdev->ops->connect) |
834 | wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, | 928 | err = cfg80211_sme_connect(wdev, connect, prev_bssid); |
835 | GFP_KERNEL); | 929 | else |
836 | wdev->conn->params.ie = wdev->conn->ie; | ||
837 | if (!wdev->conn->ie) { | ||
838 | kfree(wdev->conn); | ||
839 | wdev->conn = NULL; | ||
840 | return -ENOMEM; | ||
841 | } | ||
842 | } | ||
843 | |||
844 | if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { | ||
845 | wdev->conn->auto_auth = true; | ||
846 | /* start with open system ... should mostly work */ | ||
847 | wdev->conn->params.auth_type = | ||
848 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
849 | } else { | ||
850 | wdev->conn->auto_auth = false; | ||
851 | } | ||
852 | |||
853 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | ||
854 | wdev->ssid_len = connect->ssid_len; | ||
855 | wdev->conn->params.ssid = wdev->ssid; | ||
856 | wdev->conn->params.ssid_len = connect->ssid_len; | ||
857 | |||
858 | /* see if we have the bss already */ | ||
859 | bss = cfg80211_get_conn_bss(wdev); | ||
860 | |||
861 | wdev->sme_state = CFG80211_SME_CONNECTING; | ||
862 | wdev->connect_keys = connkeys; | ||
863 | |||
864 | if (prev_bssid) { | ||
865 | memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); | ||
866 | wdev->conn->prev_bssid_valid = true; | ||
867 | } | ||
868 | |||
869 | /* we're good if we have a matching bss struct */ | ||
870 | if (bss) { | ||
871 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
872 | err = cfg80211_conn_do_work(wdev); | ||
873 | cfg80211_put_bss(wdev->wiphy, bss); | ||
874 | } else { | ||
875 | /* otherwise we'll need to scan for the AP first */ | ||
876 | err = cfg80211_conn_scan(wdev); | ||
877 | /* | ||
878 | * If we can't scan right now, then we need to scan again | ||
879 | * after the current scan finished, since the parameters | ||
880 | * changed (unless we find a good AP anyway). | ||
881 | */ | ||
882 | if (err == -EBUSY) { | ||
883 | err = 0; | ||
884 | wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; | ||
885 | } | ||
886 | } | ||
887 | if (err) { | ||
888 | kfree(wdev->conn->ie); | ||
889 | kfree(wdev->conn); | ||
890 | wdev->conn = NULL; | ||
891 | wdev->sme_state = CFG80211_SME_IDLE; | ||
892 | wdev->connect_keys = NULL; | ||
893 | wdev->ssid_len = 0; | ||
894 | } | ||
895 | |||
896 | return err; | ||
897 | } else { | ||
898 | wdev->sme_state = CFG80211_SME_CONNECTING; | ||
899 | wdev->connect_keys = connkeys; | ||
900 | err = rdev_connect(rdev, dev, connect); | 930 | err = rdev_connect(rdev, dev, connect); |
901 | if (err) { | ||
902 | wdev->connect_keys = NULL; | ||
903 | wdev->sme_state = CFG80211_SME_IDLE; | ||
904 | return err; | ||
905 | } | ||
906 | 931 | ||
907 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | 932 | if (err) { |
908 | wdev->ssid_len = connect->ssid_len; | 933 | wdev->connect_keys = NULL; |
909 | 934 | wdev->ssid_len = 0; | |
910 | return 0; | 935 | return err; |
911 | } | 936 | } |
937 | |||
938 | return 0; | ||
912 | } | 939 | } |
913 | 940 | ||
914 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | 941 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, |
@@ -919,78 +946,17 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | |||
919 | 946 | ||
920 | ASSERT_WDEV_LOCK(wdev); | 947 | ASSERT_WDEV_LOCK(wdev); |
921 | 948 | ||
922 | if (wdev->sme_state == CFG80211_SME_IDLE) | ||
923 | return -EINVAL; | ||
924 | |||
925 | kfree(wdev->connect_keys); | 949 | kfree(wdev->connect_keys); |
926 | wdev->connect_keys = NULL; | 950 | wdev->connect_keys = NULL; |
927 | 951 | ||
928 | if (!rdev->ops->disconnect) { | 952 | if (wdev->conn) { |
929 | if (!rdev->ops->deauth) | 953 | err = cfg80211_sme_disconnect(wdev, reason); |
930 | return -EOPNOTSUPP; | 954 | } else if (!rdev->ops->disconnect) { |
931 | 955 | cfg80211_mlme_down(rdev, dev); | |
932 | /* was it connected by userspace SME? */ | 956 | err = 0; |
933 | if (!wdev->conn) { | ||
934 | cfg80211_mlme_down(rdev, dev); | ||
935 | goto disconnect; | ||
936 | } | ||
937 | |||
938 | if (wdev->sme_state == CFG80211_SME_CONNECTING && | ||
939 | (wdev->conn->state == CFG80211_CONN_SCANNING || | ||
940 | wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { | ||
941 | wdev->sme_state = CFG80211_SME_IDLE; | ||
942 | kfree(wdev->conn->ie); | ||
943 | kfree(wdev->conn); | ||
944 | wdev->conn = NULL; | ||
945 | wdev->ssid_len = 0; | ||
946 | return 0; | ||
947 | } | ||
948 | |||
949 | /* wdev->conn->params.bssid must be set if > SCANNING */ | ||
950 | err = cfg80211_mlme_deauth(rdev, dev, | ||
951 | wdev->conn->params.bssid, | ||
952 | NULL, 0, reason, false); | ||
953 | if (err) | ||
954 | return err; | ||
955 | } else { | 957 | } else { |
956 | err = rdev_disconnect(rdev, dev, reason); | 958 | err = rdev_disconnect(rdev, dev, reason); |
957 | if (err) | ||
958 | return err; | ||
959 | } | 959 | } |
960 | 960 | ||
961 | disconnect: | 961 | return err; |
962 | if (wdev->sme_state == CFG80211_SME_CONNECTED) | ||
963 | __cfg80211_disconnected(dev, NULL, 0, 0, false); | ||
964 | else if (wdev->sme_state == CFG80211_SME_CONNECTING) | ||
965 | __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, | ||
966 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
967 | wextev, NULL); | ||
968 | |||
969 | return 0; | ||
970 | } | ||
971 | |||
972 | void cfg80211_sme_disassoc(struct net_device *dev, | ||
973 | struct cfg80211_internal_bss *bss) | ||
974 | { | ||
975 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
976 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); | ||
977 | u8 bssid[ETH_ALEN]; | ||
978 | |||
979 | ASSERT_WDEV_LOCK(wdev); | ||
980 | |||
981 | if (!wdev->conn) | ||
982 | return; | ||
983 | |||
984 | if (wdev->conn->state == CFG80211_CONN_IDLE) | ||
985 | return; | ||
986 | |||
987 | /* | ||
988 | * Ok, so the association was made by this SME -- we don't | ||
989 | * want it any more so deauthenticate too. | ||
990 | */ | ||
991 | |||
992 | memcpy(bssid, bss->pub.bssid, ETH_ALEN); | ||
993 | |||
994 | cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, | ||
995 | WLAN_REASON_DEAUTH_LEAVING, false); | ||
996 | } | 962 | } |