cfg80211: managed mode wext compatibility
[safe/jmp/linux-2.6] / net / wireless / wext-sme.c
1 /*
2  * cfg80211 wext compat for managed mode.
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 <net/cfg80211.h>
11 #include "nl80211.h"
12
13 static int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
14                                      struct wireless_dev *wdev)
15 {
16         int err;
17
18         if (!netif_running(wdev->netdev))
19                 return 0;
20
21         wdev->wext.connect.ie = wdev->wext.ie;
22         wdev->wext.connect.ie_len = wdev->wext.ie_len;
23         wdev->wext.connect.privacy = wdev->wext.default_key != -1;
24
25         err = 0;
26         if (wdev->wext.connect.ssid_len != 0)
27                 err = cfg80211_connect(rdev, wdev->netdev,
28                                         &wdev->wext.connect);
29
30         return err;
31 }
32
33 int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
34                               struct iw_request_info *info,
35                               struct iw_freq *freq, char *extra)
36 {
37         struct wireless_dev *wdev = dev->ieee80211_ptr;
38         struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
39         struct ieee80211_channel *chan;
40         int err;
41
42         /* call only for station! */
43         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
44                 return -EINVAL;
45
46         chan = cfg80211_wext_freq(wdev->wiphy, freq);
47         if (chan && IS_ERR(chan))
48                 return PTR_ERR(chan);
49
50         if (chan && (chan->flags & IEEE80211_CHAN_DISABLED))
51                 return -EINVAL;
52
53         if (wdev->wext.connect.channel == chan)
54                 return 0;
55
56         if (wdev->sme_state != CFG80211_SME_IDLE) {
57                 bool event = true;
58                 /* if SSID set, we'll try right again, avoid event */
59                 if (wdev->wext.connect.ssid_len)
60                         event = false;
61                 err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
62                                           dev, WLAN_REASON_DEAUTH_LEAVING,
63                                           event);
64                 if (err)
65                         return err;
66         }
67
68         wdev->wext.connect.channel = chan;
69
70         /* SSID is not set, we just want to switch channel */
71         if (wdev->wext.connect.ssid_len && chan) {
72                 if (!rdev->ops->set_channel)
73                         return -EOPNOTSUPP;
74
75                 return rdev->ops->set_channel(wdev->wiphy, chan,
76                                               NL80211_CHAN_NO_HT);
77         }
78
79         return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
80 }
81 /* temporary symbol - mark GPL - in the future the handler won't be */
82 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwfreq);
83
84 int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
85                               struct iw_request_info *info,
86                               struct iw_freq *freq, char *extra)
87 {
88         struct wireless_dev *wdev = dev->ieee80211_ptr;
89         struct ieee80211_channel *chan = NULL;
90
91         /* call only for station! */
92         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
93                 return -EINVAL;
94
95         if (wdev->current_bss)
96                 chan = wdev->current_bss->channel;
97         else if (wdev->wext.connect.channel)
98                 chan = wdev->wext.connect.channel;
99
100         if (chan) {
101                 freq->m = chan->center_freq;
102                 freq->e = 6;
103                 return 0;
104         }
105
106         /* no channel if not joining */
107         return -EINVAL;
108 }
109 /* temporary symbol - mark GPL - in the future the handler won't be */
110 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwfreq);
111
112 int cfg80211_mgd_wext_siwessid(struct net_device *dev,
113                                struct iw_request_info *info,
114                                struct iw_point *data, char *ssid)
115 {
116         struct wireless_dev *wdev = dev->ieee80211_ptr;
117         size_t len = data->length;
118         int err;
119
120         /* call only for station! */
121         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
122                 return -EINVAL;
123
124         if (!data->flags)
125                 len = 0;
126
127         /* iwconfig uses nul termination in SSID.. */
128         if (len > 0 && ssid[len - 1] == '\0')
129                 len--;
130
131         if (wdev->wext.connect.ssid && len &&
132             len == wdev->wext.connect.ssid_len &&
133             memcmp(wdev->wext.connect.ssid, ssid, len))
134                 return 0;
135
136         if (wdev->sme_state != CFG80211_SME_IDLE) {
137                 bool event = true;
138                 /* if SSID set now, we'll try to connect, avoid event */
139                 if (len)
140                         event = false;
141                 err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
142                                           dev, WLAN_REASON_DEAUTH_LEAVING,
143                                           event);
144                 if (err)
145                         return err;
146         }
147
148         wdev->wext.connect.ssid = wdev->wext.ssid;
149         memcpy(wdev->wext.ssid, ssid, len);
150         wdev->wext.connect.ssid_len = len;
151
152         wdev->wext.connect.crypto.control_port = false;
153
154         return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
155 }
156 /* temporary symbol - mark GPL - in the future the handler won't be */
157 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwessid);
158
159 int cfg80211_mgd_wext_giwessid(struct net_device *dev,
160                                struct iw_request_info *info,
161                                struct iw_point *data, char *ssid)
162 {
163         struct wireless_dev *wdev = dev->ieee80211_ptr;
164
165         /* call only for station! */
166         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
167                 return -EINVAL;
168
169         data->flags = 0;
170
171         if (wdev->ssid_len) {
172                 data->flags = 1;
173                 data->length = wdev->ssid_len;
174                 memcpy(ssid, wdev->ssid, data->length);
175         } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
176                 data->flags = 1;
177                 data->length = wdev->wext.connect.ssid_len;
178                 memcpy(ssid, wdev->wext.connect.ssid, data->length);
179         } else
180                 data->flags = 0;
181
182         return 0;
183 }
184 /* temporary symbol - mark GPL - in the future the handler won't be */
185 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwessid);
186
187 int cfg80211_mgd_wext_siwap(struct net_device *dev,
188                             struct iw_request_info *info,
189                             struct sockaddr *ap_addr, char *extra)
190 {
191         struct wireless_dev *wdev = dev->ieee80211_ptr;
192         u8 *bssid = ap_addr->sa_data;
193         int err;
194
195         /* call only for station! */
196         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
197                 return -EINVAL;
198
199         if (ap_addr->sa_family != ARPHRD_ETHER)
200                 return -EINVAL;
201
202         /* automatic mode */
203         if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
204                 bssid = NULL;
205
206         /* both automatic */
207         if (!bssid && !wdev->wext.connect.bssid)
208                 return 0;
209
210         /* fixed already - and no change */
211         if (wdev->wext.connect.bssid && bssid &&
212             compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0)
213                 return 0;
214
215         if (wdev->sme_state != CFG80211_SME_IDLE) {
216                 err = cfg80211_disconnect(wiphy_to_dev(wdev->wiphy),
217                                           dev, WLAN_REASON_DEAUTH_LEAVING,
218                                           false);
219                 if (err)
220                         return err;
221         }
222
223         if (bssid) {
224                 memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
225                 wdev->wext.connect.bssid = wdev->wext.bssid;
226         } else
227                 wdev->wext.connect.bssid = NULL;
228
229         return cfg80211_mgd_wext_connect(wiphy_to_dev(wdev->wiphy), wdev);
230 }
231 /* temporary symbol - mark GPL - in the future the handler won't be */
232 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_siwap);
233
234 int cfg80211_mgd_wext_giwap(struct net_device *dev,
235                             struct iw_request_info *info,
236                             struct sockaddr *ap_addr, char *extra)
237 {
238         struct wireless_dev *wdev = dev->ieee80211_ptr;
239
240         /* call only for station! */
241         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
242                 return -EINVAL;
243
244         ap_addr->sa_family = ARPHRD_ETHER;
245
246         if (wdev->current_bss)
247                 memcpy(ap_addr->sa_data, wdev->current_bss->bssid, ETH_ALEN);
248         else if (wdev->wext.connect.bssid)
249                 memcpy(ap_addr->sa_data, wdev->wext.connect.bssid, ETH_ALEN);
250         else
251                 memset(ap_addr->sa_data, 0, ETH_ALEN);
252
253         return 0;
254 }
255 /* temporary symbol - mark GPL - in the future the handler won't be */
256 EXPORT_SYMBOL_GPL(cfg80211_mgd_wext_giwap);
257
258 int cfg80211_wext_siwgenie(struct net_device *dev,
259                            struct iw_request_info *info,
260                            struct iw_point *data, char *extra)
261 {
262         struct wireless_dev *wdev = dev->ieee80211_ptr;
263         struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
264         u8 *ie = extra;
265         int ie_len = data->length, err;
266
267         if (wdev->iftype != NL80211_IFTYPE_STATION)
268                 return -EOPNOTSUPP;
269
270         if (!ie_len)
271                 ie = NULL;
272
273         /* no change */
274         if (wdev->wext.ie_len == ie_len &&
275             memcmp(wdev->wext.ie, ie, ie_len) == 0)
276                 return 0;
277
278         if (ie_len) {
279                 ie = kmemdup(extra, ie_len, GFP_KERNEL);
280                 if (!ie)
281                         return -ENOMEM;
282         } else
283                 ie = NULL;
284
285         kfree(wdev->wext.ie);
286         wdev->wext.ie = ie;
287         wdev->wext.ie_len = ie_len;
288
289         if (wdev->sme_state != CFG80211_SME_IDLE) {
290                 err = cfg80211_disconnect(rdev, dev,
291                                           WLAN_REASON_DEAUTH_LEAVING, false);
292                 if (err)
293                         return err;
294         }
295
296         /* userspace better not think we'll reconnect */
297         return 0;
298 }
299 EXPORT_SYMBOL_GPL(cfg80211_wext_siwgenie);
300
301 int cfg80211_wext_siwmlme(struct net_device *dev,
302                           struct iw_request_info *info,
303                           struct iw_point *data, char *extra)
304 {
305         struct wireless_dev *wdev = dev->ieee80211_ptr;
306         struct iw_mlme *mlme = (struct iw_mlme *)extra;
307         struct cfg80211_registered_device *rdev;
308
309         if (!wdev)
310                 return -EOPNOTSUPP;
311
312         rdev = wiphy_to_dev(wdev->wiphy);
313
314         if (wdev->iftype != NL80211_IFTYPE_STATION)
315                 return -EINVAL;
316
317         if (mlme->addr.sa_family != ARPHRD_ETHER)
318                 return -EINVAL;
319
320         switch (mlme->cmd) {
321         case IW_MLME_DEAUTH:
322         case IW_MLME_DISASSOC:
323                 return cfg80211_disconnect(rdev, dev, mlme->reason_code,
324                                            true);
325         default:
326                 return -EOPNOTSUPP;
327         }
328 }
329 EXPORT_SYMBOL_GPL(cfg80211_wext_siwmlme);