]> git.karo-electronics.de Git - mv-sheeva.git/blob - net/wireless/sme.c
cfg80211: connect/disconnect API
[mv-sheeva.git] / 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
16 void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
17                              const u8 *req_ie, size_t req_ie_len,
18                              const u8 *resp_ie, size_t resp_ie_len,
19                              u16 status, gfp_t gfp)
20 {
21         struct wireless_dev *wdev = dev->ieee80211_ptr;
22         struct cfg80211_bss *bss;
23 #ifdef CONFIG_WIRELESS_EXT
24         union iwreq_data wrqu;
25 #endif
26
27         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
28                 return;
29
30         if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING))
31                 return;
32
33         if (wdev->current_bss) {
34                 cfg80211_unhold_bss(wdev->current_bss);
35                 cfg80211_put_bss(wdev->current_bss);
36                 wdev->current_bss = NULL;
37         }
38
39         if (status == WLAN_STATUS_SUCCESS) {
40                 bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
41                                        wdev->ssid, wdev->ssid_len,
42                                        WLAN_CAPABILITY_ESS,
43                                        WLAN_CAPABILITY_ESS);
44
45                 if (WARN_ON(!bss))
46                         return;
47
48                 cfg80211_hold_bss(bss);
49                 wdev->current_bss = bss;
50
51                 wdev->sme_state = CFG80211_SME_CONNECTED;
52         } else {
53                 wdev->sme_state = CFG80211_SME_IDLE;
54         }
55
56         nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid,
57                                     req_ie, req_ie_len, resp_ie, resp_ie_len,
58                                     status, gfp);
59
60 #ifdef CONFIG_WIRELESS_EXT
61         if (req_ie && status == WLAN_STATUS_SUCCESS) {
62                 memset(&wrqu, 0, sizeof(wrqu));
63                 wrqu.data.length = req_ie_len;
64                 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie);
65         }
66
67         if (resp_ie && status == WLAN_STATUS_SUCCESS) {
68                 memset(&wrqu, 0, sizeof(wrqu));
69                 wrqu.data.length = resp_ie_len;
70                 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
71         }
72
73         memset(&wrqu, 0, sizeof(wrqu));
74         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
75         if (bssid)
76                 memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
77         wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
78 #endif
79 }
80 EXPORT_SYMBOL(cfg80211_connect_result);
81
82 void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
83                      const u8 *req_ie, size_t req_ie_len,
84                      const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
85 {
86         struct wireless_dev *wdev = dev->ieee80211_ptr;
87         struct cfg80211_bss *bss;
88 #ifdef CONFIG_WIRELESS_EXT
89         union iwreq_data wrqu;
90 #endif
91
92         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
93                 return;
94
95         if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
96                 return;
97
98         /* internal error -- how did we get to CONNECTED w/o BSS? */
99         if (WARN_ON(!wdev->current_bss)) {
100                 return;
101         }
102
103         cfg80211_unhold_bss(wdev->current_bss);
104         cfg80211_put_bss(wdev->current_bss);
105         wdev->current_bss = NULL;
106
107         bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
108                                wdev->ssid, wdev->ssid_len,
109                                WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
110
111         if (WARN_ON(!bss))
112                 return;
113
114         cfg80211_hold_bss(bss);
115         wdev->current_bss = bss;
116
117         nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid,
118                             req_ie, req_ie_len, resp_ie, resp_ie_len, gfp);
119
120 #ifdef CONFIG_WIRELESS_EXT
121         if (req_ie) {
122                 memset(&wrqu, 0, sizeof(wrqu));
123                 wrqu.data.length = req_ie_len;
124                 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie);
125         }
126
127         if (resp_ie) {
128                 memset(&wrqu, 0, sizeof(wrqu));
129                 wrqu.data.length = resp_ie_len;
130                 wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
131         }
132
133         memset(&wrqu, 0, sizeof(wrqu));
134         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
135         memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
136         wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
137 #endif
138 }
139 EXPORT_SYMBOL(cfg80211_roamed);
140
141 static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp,
142                                     u8 *ie, size_t ie_len, u16 reason,
143                                     bool from_ap)
144 {
145         struct wireless_dev *wdev = dev->ieee80211_ptr;
146 #ifdef CONFIG_WIRELESS_EXT
147         union iwreq_data wrqu;
148 #endif
149
150         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
151                 return;
152
153         if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
154                 return;
155
156         if (wdev->current_bss) {
157                 cfg80211_unhold_bss(wdev->current_bss);
158                 cfg80211_put_bss(wdev->current_bss);
159         }
160
161         wdev->current_bss = NULL;
162         wdev->sme_state = CFG80211_SME_IDLE;
163
164         nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev,
165                                   reason, ie, ie_len, from_ap, gfp);
166
167 #ifdef CONFIG_WIRELESS_EXT
168         memset(&wrqu, 0, sizeof(wrqu));
169         wrqu.ap_addr.sa_family = ARPHRD_ETHER;
170         wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
171 #endif
172 }
173
174 void cfg80211_disconnected(struct net_device *dev, u16 reason,
175                            u8 *ie, size_t ie_len, gfp_t gfp)
176 {
177         __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp);
178 }
179 EXPORT_SYMBOL(cfg80211_disconnected);
180
181 int cfg80211_connect(struct cfg80211_registered_device *rdev,
182                      struct net_device *dev,
183                      struct cfg80211_connect_params *connect)
184 {
185         int err;
186         struct wireless_dev *wdev = dev->ieee80211_ptr;
187
188         if (wdev->sme_state != CFG80211_SME_IDLE)
189                 return -EALREADY;
190
191         if (!rdev->ops->connect) {
192                 return -EOPNOTSUPP;
193         } else {
194                 wdev->sme_state = CFG80211_SME_CONNECTING;
195                 err = rdev->ops->connect(&rdev->wiphy, dev, connect);
196                 if (err) {
197                         wdev->sme_state = CFG80211_SME_IDLE;
198                         return err;
199                 }
200         }
201
202         memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
203         wdev->ssid_len = connect->ssid_len;
204
205         return 0;
206 }
207
208 int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
209                         struct net_device *dev, u16 reason)
210 {
211         int err;
212
213         if (!rdev->ops->disconnect) {
214                 return -EOPNOTSUPP;
215         } else {
216                 err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
217                 if (err)
218                         return err;
219         }
220
221         __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL);
222
223         return 0;
224 }