cfg80211: let SME control reassociation vs. association
[safe/jmp/linux-2.6] / net / wireless / sme.c
1 /*
2  * SME code for cfg80211's connect emulation.
3  *
4  * Copyright 2009       Johannes Berg <johannes@sipsolutions.net>
5  * Copyright (C) 2009   Intel Corporation. All rights reserved.
6  */
7
8 #include <linux/etherdevice.h>
9 #include <linux/if_arp.h>
10 #include <linux/workqueue.h>
11 #include <net/cfg80211.h>
12 #include <net/rtnetlink.h>
13 #include "nl80211.h"
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         struct cfg80211_connect_params *params;
107         int err;
108
109         if (!wdev->conn)
110                 return 0;
111
112         params = &wdev->conn->params;
113
114         switch (wdev->conn->state) {
115         case CFG80211_CONN_SCAN_AGAIN:
116                 return cfg80211_conn_scan(wdev);
117         case CFG80211_CONN_AUTHENTICATE_NEXT:
118                 BUG_ON(!drv->ops->auth);
119                 wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
120                 return cfg80211_mlme_auth(drv, wdev->netdev,
121                                           params->channel, params->auth_type,
122                                           params->bssid,
123                                           params->ssid, params->ssid_len,
124                                           NULL, 0);
125         case CFG80211_CONN_ASSOCIATE_NEXT:
126                 BUG_ON(!drv->ops->assoc);
127                 wdev->conn->state = CFG80211_CONN_ASSOCIATING;
128                 /*
129                  * We could, later, implement roaming here and then actually
130                  * set prev_bssid to non-NULL. But then we need to be aware
131                  * that some APs don't like that -- so we'd need to retry
132                  * the association.
133                  */
134                 err = cfg80211_mlme_assoc(drv, wdev->netdev,
135                                           params->channel, params->bssid, NULL,
136                                           params->ssid, params->ssid_len,
137                                           params->ie, params->ie_len,
138                                           false, &params->crypto);
139                 if (err)
140                         cfg80211_mlme_deauth(drv, wdev->netdev, params->bssid,
141                                              NULL, 0, WLAN_REASON_DEAUTH_LEAVING);
142                 return err;
143         default:
144                 return 0;
145         }
146 }
147
148 void cfg80211_conn_work(struct work_struct *work)
149 {
150         struct cfg80211_registered_device *drv =
151                 container_of(work, struct cfg80211_registered_device, conn_work);
152         struct wireless_dev *wdev;
153
154         rtnl_lock();
155         mutex_lock(&drv->devlist_mtx);
156
157         list_for_each_entry(wdev, &drv->netdev_list, list) {
158                 if (!netif_running(wdev->netdev))
159                         continue;
160                 if (wdev->sme_state != CFG80211_SME_CONNECTING)
161                         continue;
162                 if (cfg80211_conn_do_work(wdev))
163                         cfg80211_connect_result(wdev->netdev,
164                                                 wdev->conn->params.bssid,
165                                                 NULL, 0, NULL, 0,
166                                                 WLAN_STATUS_UNSPECIFIED_FAILURE,
167                                                 GFP_ATOMIC);
168         }
169
170         mutex_unlock(&drv->devlist_mtx);
171         rtnl_unlock();
172 }
173
174 static bool cfg80211_get_conn_bss(struct wireless_dev *wdev)
175 {
176         struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy);
177         struct cfg80211_bss *bss;
178         u16 capa = WLAN_CAPABILITY_ESS;
179
180         if (wdev->conn->params.privacy)
181                 capa |= WLAN_CAPABILITY_PRIVACY;
182
183         bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid,
184                                wdev->conn->params.ssid,
185                                wdev->conn->params.ssid_len,
186                                WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
187                                capa);
188         if (!bss)
189                 return false;
190
191         memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
192         wdev->conn->params.bssid = wdev->conn->bssid;
193         wdev->conn->params.channel = bss->channel;
194         wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
195         schedule_work(&drv->conn_work);
196
197         cfg80211_put_bss(bss);
198         return true;
199 }
200
201 void cfg80211_sme_scan_done(struct net_device *dev)
202 {
203         struct wireless_dev *wdev = dev->ieee80211_ptr;
204         struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy);
205
206         if (wdev->sme_state != CFG80211_SME_CONNECTING)
207                 return;
208
209         if (WARN_ON(!wdev->conn))
210                 return;
211
212         if (wdev->conn->state != CFG80211_CONN_SCANNING &&
213             wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
214                 return;
215
216         if (!cfg80211_get_conn_bss(wdev)) {
217                 /* not found */
218                 if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
219                         schedule_work(&drv->conn_work);
220                 else
221                         cfg80211_connect_result(dev, wdev->conn->params.bssid,
222                                                 NULL, 0, NULL, 0,
223                                                 WLAN_STATUS_UNSPECIFIED_FAILURE,
224                                                 GFP_ATOMIC);
225                 return;
226         }
227 }
228
229 void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
230 {
231         struct wireless_dev *wdev = dev->ieee80211_ptr;
232         struct wiphy *wiphy = wdev->wiphy;
233         struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
234         struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
235         u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
236
237         /* should only RX auth frames when connecting */
238         if (wdev->sme_state != CFG80211_SME_CONNECTING)
239                 return;
240
241         if (WARN_ON(!wdev->conn))
242                 return;
243
244         if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
245             wdev->conn->auto_auth &&
246             wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
247                 /* select automatically between only open, shared, leap */
248                 switch (wdev->conn->params.auth_type) {
249                 case NL80211_AUTHTYPE_OPEN_SYSTEM:
250                         wdev->conn->params.auth_type =
251                                 NL80211_AUTHTYPE_SHARED_KEY;
252                         break;
253                 case NL80211_AUTHTYPE_SHARED_KEY:
254                         wdev->conn->params.auth_type =
255                                 NL80211_AUTHTYPE_NETWORK_EAP;
256                         break;
257                 default:
258                         /* huh? */
259                         wdev->conn->params.auth_type =
260                                 NL80211_AUTHTYPE_OPEN_SYSTEM;
261                         break;
262                 }
263                 wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
264                 schedule_work(&rdev->conn_work);
265         } else if (status_code != WLAN_STATUS_SUCCESS) {
266                 wdev->sme_state = CFG80211_SME_IDLE;
267                 kfree(wdev->conn);
268                 wdev->conn = NULL;
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 }
275
276 static void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
277                                       const u8 *req_ie, size_t req_ie_len,
278                                       const u8 *resp_ie, size_t resp_ie_len,
279                                       u16 status, bool wextev, gfp_t gfp)
280 {
281         struct wireless_dev *wdev = dev->ieee80211_ptr;
282         struct cfg80211_bss *bss;
283 #ifdef CONFIG_WIRELESS_EXT
284         union iwreq_data wrqu;
285 #endif
286
287         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
288                 return;
289
290         if (wdev->sme_state == CFG80211_SME_CONNECTED)
291                 nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev,
292                                     bssid, req_ie, req_ie_len,
293                                     resp_ie, resp_ie_len, gfp);
294         else
295                 nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
296                                             bssid, req_ie, req_ie_len,
297                                             resp_ie, resp_ie_len,
298                                             status, gfp);
299
300 #ifdef CONFIG_WIRELESS_EXT
301         if (wextev) {
302                 if (req_ie && status == WLAN_STATUS_SUCCESS) {
303                         memset(&wrqu, 0, sizeof(wrqu));
304                         wrqu.data.length = req_ie_len;
305                         wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie);
306                 }
307
308                 if (resp_ie && status == WLAN_STATUS_SUCCESS) {
309                         memset(&wrqu, 0, sizeof(wrqu));
310                         wrqu.data.length = resp_ie_len;
311                         wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
312                 }
313
314                 memset(&wrqu, 0, sizeof(wrqu));
315                 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
316                 if (bssid && status == WLAN_STATUS_SUCCESS)
317                         memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
318                 wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
319         }
320 #endif
321
322         if (status == WLAN_STATUS_SUCCESS &&
323             wdev->sme_state == CFG80211_SME_IDLE) {
324                 wdev->sme_state = CFG80211_SME_CONNECTED;
325                 return;
326         }
327
328         if (wdev->sme_state != CFG80211_SME_CONNECTING)
329                 return;
330
331         if (wdev->current_bss) {
332                 cfg80211_unhold_bss(wdev->current_bss);
333                 cfg80211_put_bss(&wdev->current_bss->pub);
334                 wdev->current_bss = NULL;
335         }
336
337         if (wdev->conn)
338                 wdev->conn->state = CFG80211_CONN_IDLE;
339
340         if (status == WLAN_STATUS_SUCCESS) {
341                 bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
342                                        wdev->ssid, wdev->ssid_len,
343                                        WLAN_CAPABILITY_ESS,
344                                        WLAN_CAPABILITY_ESS);
345
346                 if (WARN_ON(!bss))
347                         return;
348
349                 cfg80211_hold_bss(bss_from_pub(bss));
350                 wdev->current_bss = bss_from_pub(bss);
351
352                 wdev->sme_state = CFG80211_SME_CONNECTED;
353         } else {
354                 wdev->sme_state = CFG80211_SME_IDLE;
355                 kfree(wdev->conn);
356                 wdev->conn = NULL;
357         }
358 }
359
360 void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
361                              const u8 *req_ie, size_t req_ie_len,
362                              const u8 *resp_ie, size_t resp_ie_len,
363                              u16 status, gfp_t gfp)
364 {
365         bool wextev = status == WLAN_STATUS_SUCCESS;
366         __cfg80211_connect_result(dev, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, status, wextev, gfp);
367 }
368 EXPORT_SYMBOL(cfg80211_connect_result);
369
370 void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
371                      const u8 *req_ie, size_t req_ie_len,
372                      const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
373 {
374         struct wireless_dev *wdev = dev->ieee80211_ptr;
375         struct cfg80211_bss *bss;
376 #ifdef CONFIG_WIRELESS_EXT
377         union iwreq_data wrqu;
378 #endif
379
380         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
381                 return;
382
383         if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
384                 return;
385
386         /* internal error -- how did we get to CONNECTED w/o BSS? */
387         if (WARN_ON(!wdev->current_bss)) {
388                 return;
389         }
390
391         cfg80211_unhold_bss(wdev->current_bss);
392         cfg80211_put_bss(&wdev->current_bss->pub);
393         wdev->current_bss = NULL;
394
395         bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
396                                wdev->ssid, wdev->ssid_len,
397                                WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
398
399         if (WARN_ON(!bss))
400                 return;
401
402         cfg80211_hold_bss(bss_from_pub(bss));
403         wdev->current_bss = bss_from_pub(bss);
404
405         nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid,
406                             req_ie, req_ie_len, resp_ie, resp_ie_len, gfp);
407
408 #ifdef CONFIG_WIRELESS_EXT
409         if (req_ie) {
410                 memset(&wrqu, 0, sizeof(wrqu));
411                 wrqu.data.length = req_ie_len;
412                 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie);
413         }
414
415         if (resp_ie) {
416                 memset(&wrqu, 0, sizeof(wrqu));
417                 wrqu.data.length = resp_ie_len;
418                 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
419         }
420
421         memset(&wrqu, 0, sizeof(wrqu));
422         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
423         memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
424         wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
425 #endif
426 }
427 EXPORT_SYMBOL(cfg80211_roamed);
428
429 void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie,
430                              size_t ie_len, u16 reason, bool from_ap)
431 {
432         struct wireless_dev *wdev = dev->ieee80211_ptr;
433 #ifdef CONFIG_WIRELESS_EXT
434         union iwreq_data wrqu;
435 #endif
436
437         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
438                 return;
439
440         if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
441                 return;
442
443         if (wdev->current_bss) {
444                 cfg80211_unhold_bss(wdev->current_bss);
445                 cfg80211_put_bss(&wdev->current_bss->pub);
446         }
447
448         wdev->current_bss = NULL;
449         wdev->sme_state = CFG80211_SME_IDLE;
450
451         if (wdev->conn) {
452                 kfree(wdev->conn->ie);
453                 wdev->conn->ie = NULL;
454                 kfree(wdev->conn);
455                 wdev->conn = NULL;
456         }
457
458         nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev,
459                                   reason, ie, ie_len, from_ap, gfp);
460
461 #ifdef CONFIG_WIRELESS_EXT
462         memset(&wrqu, 0, sizeof(wrqu));
463         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
464         wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
465 #endif
466 }
467
468 void cfg80211_disconnected(struct net_device *dev, u16 reason,
469                            u8 *ie, size_t ie_len, gfp_t gfp)
470 {
471         __cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true);
472 }
473 EXPORT_SYMBOL(cfg80211_disconnected);
474
475 int cfg80211_connect(struct cfg80211_registered_device *rdev,
476                      struct net_device *dev,
477                      struct cfg80211_connect_params *connect)
478 {
479         int err;
480         struct wireless_dev *wdev = dev->ieee80211_ptr;
481
482         if (wdev->sme_state != CFG80211_SME_IDLE)
483                 return -EALREADY;
484
485         if (!rdev->ops->connect) {
486                 if (!rdev->ops->auth || !rdev->ops->assoc)
487                         return -EOPNOTSUPP;
488
489                 if (WARN_ON(wdev->conn))
490                         return -EINPROGRESS;
491
492                 wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
493                 if (!wdev->conn)
494                         return -ENOMEM;
495
496                 /*
497                  * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
498                  */
499                 memcpy(&wdev->conn->params, connect, sizeof(*connect));
500                 if (connect->bssid) {
501                         wdev->conn->params.bssid = wdev->conn->bssid;
502                         memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
503                 }
504
505                 if (connect->ie) {
506                         wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
507                                                 GFP_KERNEL);
508                         wdev->conn->params.ie = wdev->conn->ie;
509                         if (!wdev->conn->ie) {
510                                 kfree(wdev->conn);
511                                 wdev->conn = NULL;
512                                 return -ENOMEM;
513                         }
514                 }
515
516                 if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
517                         wdev->conn->auto_auth = true;
518                         /* start with open system ... should mostly work */
519                         wdev->conn->params.auth_type =
520                                 NL80211_AUTHTYPE_OPEN_SYSTEM;
521                 } else {
522                         wdev->conn->auto_auth = false;
523                 }
524
525                 memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
526                 wdev->ssid_len = connect->ssid_len;
527                 wdev->conn->params.ssid = wdev->ssid;
528                 wdev->conn->params.ssid_len = connect->ssid_len;
529
530                 /* don't care about result -- but fill bssid & channel */
531                 if (!wdev->conn->params.bssid || !wdev->conn->params.channel)
532                         cfg80211_get_conn_bss(wdev);
533
534                 wdev->sme_state = CFG80211_SME_CONNECTING;
535
536                 /* we're good if we have both BSSID and channel */
537                 if (wdev->conn->params.bssid && wdev->conn->params.channel) {
538                         wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
539                         err = cfg80211_conn_do_work(wdev);
540                 } else {
541                         /* otherwise we'll need to scan for the AP first */
542                         err = cfg80211_conn_scan(wdev);
543                         /*
544                          * If we can't scan right now, then we need to scan again
545                          * after the current scan finished, since the parameters
546                          * changed (unless we find a good AP anyway).
547                          */
548                         if (err == -EBUSY) {
549                                 err = 0;
550                                 wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
551                         }
552                 }
553                 if (err) {
554                         kfree(wdev->conn);
555                         wdev->conn = NULL;
556                         wdev->sme_state = CFG80211_SME_IDLE;
557                 }
558
559                 return err;
560         } else {
561                 wdev->sme_state = CFG80211_SME_CONNECTING;
562                 err = rdev->ops->connect(&rdev->wiphy, dev, connect);
563                 if (err) {
564                         wdev->sme_state = CFG80211_SME_IDLE;
565                         return err;
566                 }
567
568                 memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
569                 wdev->ssid_len = connect->ssid_len;
570
571                 return 0;
572         }
573 }
574
575 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
576                         struct net_device *dev, u16 reason, bool wextev)
577 {
578         struct wireless_dev *wdev = dev->ieee80211_ptr;
579         int err;
580
581         if (wdev->sme_state == CFG80211_SME_IDLE)
582                 return -EINVAL;
583
584         if (!rdev->ops->disconnect) {
585                 if (!rdev->ops->deauth)
586                         return -EOPNOTSUPP;
587
588                 /* was it connected by userspace SME? */
589                 if (!wdev->conn) {
590                         cfg80211_mlme_down(rdev, dev);
591                         return 0;
592                 }
593
594                 if (wdev->sme_state == CFG80211_SME_CONNECTING &&
595                     (wdev->conn->state == CFG80211_CONN_SCANNING ||
596                      wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
597                         wdev->sme_state = CFG80211_SME_IDLE;
598                         kfree(wdev->conn);
599                         wdev->conn = NULL;
600                         return 0;
601                 }
602
603                 /* wdev->conn->params.bssid must be set if > SCANNING */
604                 err = cfg80211_mlme_deauth(rdev, dev, wdev->conn->params.bssid,
605                                            NULL, 0, reason);
606                 if (err)
607                         return err;
608         } else {
609                 err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
610                 if (err)
611                         return err;
612         }
613
614         if (wdev->sme_state == CFG80211_SME_CONNECTED)
615                 __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false);
616         else if (wdev->sme_state == CFG80211_SME_CONNECTING)
617                 __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
618                                           WLAN_STATUS_UNSPECIFIED_FAILURE,
619                                           wextev, GFP_KERNEL);
620
621         return 0;
622 }
623
624 void cfg80211_sme_disassoc(struct net_device *dev, int idx)
625 {
626         struct wireless_dev *wdev = dev->ieee80211_ptr;
627         struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
628         u8 bssid[ETH_ALEN];
629
630         if (!wdev->conn)
631                 return;
632
633         if (wdev->conn->state == CFG80211_CONN_IDLE)
634                 return;
635
636         /*
637          * Ok, so the association was made by this SME -- we don't
638          * want it any more so deauthenticate too.
639          */
640
641         if (!wdev->auth_bsses[idx])
642                 return;
643
644         memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
645         if (cfg80211_mlme_deauth(rdev, dev, bssid,
646                                  NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
647                 /* whatever -- assume gone anyway */
648                 cfg80211_unhold_bss(wdev->auth_bsses[idx]);
649                 cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
650                 wdev->auth_bsses[idx] = NULL;
651         }
652 }