Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / net / ieee80211 / softmac / ieee80211softmac_wx.c
1 /*
2  * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them
3  *
4  * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
5  *                          Joseph Jezak <josejx@gentoo.org>
6  *                          Larry Finger <Larry.Finger@lwfinger.net>
7  *                          Danny van Dyk <kugelfang@gentoo.org>
8  *                          Michael Buesch <mbuesch@freenet.de>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of version 2 of the GNU General Public License as
12  * published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
22  *
23  * The full GNU General Public License is included in this distribution in the
24  * file called COPYING.
25  */
26
27 #include "ieee80211softmac_priv.h"
28
29 #include <net/iw_handler.h>
30 /* for is_broadcast_ether_addr and is_zero_ether_addr */
31 #include <linux/etherdevice.h>
32
33 int
34 ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
35                                  struct iw_request_info *info,
36                                  union iwreq_data *data,
37                                  char *extra)
38 {
39         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
40         return ieee80211softmac_start_scan(sm);
41 }
42 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan);
43
44
45 /* if we're still scanning, return -EAGAIN so that userspace tools
46  * can get the complete scan results, otherwise return 0. */
47 int
48 ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
49                                      struct iw_request_info *info,
50                                      union iwreq_data *data,
51                                      char *extra)
52 {
53         unsigned long flags;
54         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
55
56         spin_lock_irqsave(&sm->lock, flags);
57         if (sm->scanning) {
58                 spin_unlock_irqrestore(&sm->lock, flags);
59                 return -EAGAIN;
60         }
61         spin_unlock_irqrestore(&sm->lock, flags);
62         return ieee80211_wx_get_scan(sm->ieee, info, data, extra);
63 }
64 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results);
65
66 int
67 ieee80211softmac_wx_set_essid(struct net_device *net_dev,
68                               struct iw_request_info *info,
69                               union iwreq_data *data,
70                               char *extra)
71 {
72         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
73         struct ieee80211softmac_network *n;
74         struct ieee80211softmac_auth_queue_item *authptr;
75         int length = 0;
76
77         mutex_lock(&sm->associnfo.mutex);
78
79         /* Check if we're already associating to this or another network
80          * If it's another network, cancel and start over with our new network
81          * If it's our network, ignore the change, we're already doing it!
82          */
83         if((sm->associnfo.associating || sm->associnfo.associated) &&
84            (data->essid.flags && data->essid.length)) {
85                 /* Get the associating network */
86                 n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid);
87                 if(n && n->essid.len == data->essid.length &&
88                    !memcmp(n->essid.data, extra, n->essid.len)) {
89                         dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n",
90                                 MAC_ARG(sm->associnfo.bssid));
91                         goto out;
92                 } else {
93                         dprintk(KERN_INFO PFX "Canceling existing associate request!\n");
94                         /* Cancel assoc work */
95                         cancel_delayed_work(&sm->associnfo.work);
96                         /* We don't have to do this, but it's a little cleaner */
97                         list_for_each_entry(authptr, &sm->auth_queue, list)
98                                 cancel_delayed_work(&authptr->work);
99                         sm->associnfo.bssvalid = 0;
100                         sm->associnfo.bssfixed = 0;
101                         flush_scheduled_work();
102                         sm->associnfo.associating = 0;
103                         sm->associnfo.associated = 0;
104                 }
105         }
106
107
108         sm->associnfo.static_essid = 0;
109         sm->associnfo.assoc_wait = 0;
110
111         if (data->essid.flags && data->essid.length) {
112                 length = min((int)data->essid.length, IW_ESSID_MAX_SIZE);
113                 if (length) {
114                         memcpy(sm->associnfo.req_essid.data, extra, length);
115                         sm->associnfo.static_essid = 1;
116                 }
117         }
118
119         /* set our requested ESSID length.
120          * If applicable, we have already copied the data in */
121         sm->associnfo.req_essid.len = length;
122
123         sm->associnfo.associating = 1;
124         /* queue lower level code to do work (if necessary) */
125         schedule_delayed_work(&sm->associnfo.work, 0);
126 out:
127         mutex_unlock(&sm->associnfo.mutex);
128
129         return 0;
130 }
131 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
132
133 int
134 ieee80211softmac_wx_get_essid(struct net_device *net_dev,
135                               struct iw_request_info *info,
136                               union iwreq_data *data,
137                               char *extra)
138 {
139         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
140
141         mutex_lock(&sm->associnfo.mutex);
142         /* If all fails, return ANY (empty) */
143         data->essid.length = 0;
144         data->essid.flags = 0;  /* active */
145
146         /* If we have a statically configured ESSID then return it */
147         if (sm->associnfo.static_essid) {
148                 data->essid.length = sm->associnfo.req_essid.len;
149                 data->essid.flags = 1;  /* active */
150                 memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
151         }
152
153         /* If we're associating/associated, return that */
154         if (sm->associnfo.associated || sm->associnfo.associating) {
155                 data->essid.length = sm->associnfo.associate_essid.len;
156                 data->essid.flags = 1;  /* active */
157                 memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
158         }
159         mutex_unlock(&sm->associnfo.mutex);
160
161         return 0;
162 }
163 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
164
165 int
166 ieee80211softmac_wx_set_rate(struct net_device *net_dev,
167                              struct iw_request_info *info,
168                              union iwreq_data *data,
169                              char *extra)
170 {
171         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
172         struct ieee80211_device *ieee = mac->ieee;
173         unsigned long flags;
174         s32 in_rate = data->bitrate.value;
175         u8 rate;
176         int is_ofdm = 0;
177         int err = -EINVAL;
178
179         if (in_rate == -1) {
180                 if (ieee->modulation & IEEE80211_OFDM_MODULATION)
181                         in_rate = 24000000;
182                 else
183                         in_rate = 11000000;
184         }
185
186         switch (in_rate) {
187         case 1000000:
188                 rate = IEEE80211_CCK_RATE_1MB;
189                 break;
190         case 2000000:
191                 rate = IEEE80211_CCK_RATE_2MB;
192                 break;
193         case 5500000:
194                 rate = IEEE80211_CCK_RATE_5MB;
195                 break;
196         case 11000000:
197                 rate = IEEE80211_CCK_RATE_11MB;
198                 break;
199         case 6000000:
200                 rate = IEEE80211_OFDM_RATE_6MB;
201                 is_ofdm = 1;
202                 break;
203         case 9000000:
204                 rate = IEEE80211_OFDM_RATE_9MB;
205                 is_ofdm = 1;
206                 break;
207         case 12000000:
208                 rate = IEEE80211_OFDM_RATE_12MB;
209                 is_ofdm = 1;
210                 break;
211         case 18000000:
212                 rate = IEEE80211_OFDM_RATE_18MB;
213                 is_ofdm = 1;
214                 break;
215         case 24000000:
216                 rate = IEEE80211_OFDM_RATE_24MB;
217                 is_ofdm = 1;
218                 break;
219         case 36000000:
220                 rate = IEEE80211_OFDM_RATE_36MB;
221                 is_ofdm = 1;
222                 break;
223         case 48000000:
224                 rate = IEEE80211_OFDM_RATE_48MB;
225                 is_ofdm = 1;
226                 break;
227         case 54000000:
228                 rate = IEEE80211_OFDM_RATE_54MB;
229                 is_ofdm = 1;
230                 break;
231         default:
232                 goto out;
233         }
234
235         spin_lock_irqsave(&mac->lock, flags);
236
237         /* Check if correct modulation for this PHY. */
238         if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
239                 goto out_unlock;
240
241         mac->txrates.user_rate = rate;
242         ieee80211softmac_recalc_txrates(mac);
243         err = 0;
244
245 out_unlock:
246         spin_unlock_irqrestore(&mac->lock, flags);
247 out:
248         return err;
249 }
250 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
251
252 int
253 ieee80211softmac_wx_get_rate(struct net_device *net_dev,
254                              struct iw_request_info *info,
255                              union iwreq_data *data,
256                              char *extra)
257 {
258         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
259         unsigned long flags;
260         int err = -EINVAL;
261
262         spin_lock_irqsave(&mac->lock, flags);
263
264         if (unlikely(!mac->running)) {
265                 err = -ENODEV;
266                 goto out_unlock;
267         }
268
269         switch (mac->txrates.default_rate) {
270         case IEEE80211_CCK_RATE_1MB:
271                 data->bitrate.value = 1000000;
272                 break;
273         case IEEE80211_CCK_RATE_2MB:
274                 data->bitrate.value = 2000000;
275                 break;
276         case IEEE80211_CCK_RATE_5MB:
277                 data->bitrate.value = 5500000;
278                 break;
279         case IEEE80211_CCK_RATE_11MB:
280                 data->bitrate.value = 11000000;
281                 break;
282         case IEEE80211_OFDM_RATE_6MB:
283                 data->bitrate.value = 6000000;
284                 break;
285         case IEEE80211_OFDM_RATE_9MB:
286                 data->bitrate.value = 9000000;
287                 break;
288         case IEEE80211_OFDM_RATE_12MB:
289                 data->bitrate.value = 12000000;
290                 break;
291         case IEEE80211_OFDM_RATE_18MB:
292                 data->bitrate.value = 18000000;
293                 break;
294         case IEEE80211_OFDM_RATE_24MB:
295                 data->bitrate.value = 24000000;
296                 break;
297         case IEEE80211_OFDM_RATE_36MB:
298                 data->bitrate.value = 36000000;
299                 break;
300         case IEEE80211_OFDM_RATE_48MB:
301                 data->bitrate.value = 48000000;
302                 break;
303         case IEEE80211_OFDM_RATE_54MB:
304                 data->bitrate.value = 54000000;
305                 break;
306         default:
307                 assert(0);
308                 goto out_unlock;
309         }
310         err = 0;
311 out_unlock:
312         spin_unlock_irqrestore(&mac->lock, flags);
313
314         return err;
315 }
316 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
317
318 int
319 ieee80211softmac_wx_get_wap(struct net_device *net_dev,
320                             struct iw_request_info *info,
321                             union iwreq_data *data,
322                             char *extra)
323 {
324         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
325         int err = 0;
326
327         mutex_lock(&mac->associnfo.mutex);
328         if (mac->associnfo.bssvalid)
329                 memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
330         else
331                 memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
332         data->ap_addr.sa_family = ARPHRD_ETHER;
333         mutex_unlock(&mac->associnfo.mutex);
334
335         return err;
336 }
337 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
338
339 int
340 ieee80211softmac_wx_set_wap(struct net_device *net_dev,
341                             struct iw_request_info *info,
342                             union iwreq_data *data,
343                             char *extra)
344 {
345         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
346
347         /* sanity check */
348         if (data->ap_addr.sa_family != ARPHRD_ETHER) {
349                 return -EINVAL;
350         }
351
352         mutex_lock(&mac->associnfo.mutex);
353         if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
354                 /* the bssid we have is not to be fixed any longer,
355                  * and we should reassociate to the best AP. */
356                 mac->associnfo.bssfixed = 0;
357                 /* force reassociation */
358                 mac->associnfo.bssvalid = 0;
359                 if (mac->associnfo.associated)
360                         schedule_delayed_work(&mac->associnfo.work, 0);
361         } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
362                 /* the bssid we have is no longer fixed */
363                 mac->associnfo.bssfixed = 0;
364         } else {
365                 if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
366                         if (mac->associnfo.associating || mac->associnfo.associated) {
367                         /* bssid unchanged and associated or associating - just return */
368                                 goto out;
369                         }
370                 } else {
371                         /* copy new value in data->ap_addr.sa_data to bssid */
372                         memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
373                 }
374                 /* tell the other code that this bssid should be used no matter what */
375                 mac->associnfo.bssfixed = 1;
376                 /* queue associate if new bssid or (old one again and not associated) */
377                 schedule_delayed_work(&mac->associnfo.work, 0);
378         }
379
380  out:
381         mutex_unlock(&mac->associnfo.mutex);
382
383         return 0;
384 }
385 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
386
387 int
388 ieee80211softmac_wx_set_genie(struct net_device *dev,
389                               struct iw_request_info *info,
390                               union iwreq_data *wrqu,
391                               char *extra)
392 {
393         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
394         unsigned long flags;
395         int err = 0;
396         char *buf;
397         int i;
398
399         mutex_lock(&mac->associnfo.mutex);
400         spin_lock_irqsave(&mac->lock, flags);
401         /* bleh. shouldn't be locked for that kmalloc... */
402
403         if (wrqu->data.length) {
404                 if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
405                         /* this is an IE, so the length must be
406                          * correct. Is it possible though that
407                          * more than one IE is passed in?
408                          */
409                         err = -EINVAL;
410                         goto out;
411                 }
412                 if (mac->wpa.IEbuflen <= wrqu->data.length) {
413                         buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
414                         if (!buf) {
415                                 err = -ENOMEM;
416                                 goto out;
417                         }
418                         kfree(mac->wpa.IE);
419                         mac->wpa.IE = buf;
420                         mac->wpa.IEbuflen = wrqu->data.length;
421                 }
422                 memcpy(mac->wpa.IE, extra, wrqu->data.length);
423                 dprintk(KERN_INFO PFX "generic IE set to ");
424                 for (i=0;i<wrqu->data.length;i++)
425                         dprintk("%.2x", (u8)mac->wpa.IE[i]);
426                 dprintk("\n");
427                 mac->wpa.IElen = wrqu->data.length;
428         } else {
429                 kfree(mac->wpa.IE);
430                 mac->wpa.IE = NULL;
431                 mac->wpa.IElen = 0;
432                 mac->wpa.IEbuflen = 0;
433         }
434
435  out:
436         spin_unlock_irqrestore(&mac->lock, flags);
437         mutex_unlock(&mac->associnfo.mutex);
438
439         return err;
440 }
441 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
442
443 int
444 ieee80211softmac_wx_get_genie(struct net_device *dev,
445                               struct iw_request_info *info,
446                               union iwreq_data *wrqu,
447                               char *extra)
448 {
449         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
450         unsigned long flags;
451         int err = 0;
452         int space = wrqu->data.length;
453
454         mutex_lock(&mac->associnfo.mutex);
455         spin_lock_irqsave(&mac->lock, flags);
456
457         wrqu->data.length = 0;
458
459         if (mac->wpa.IE && mac->wpa.IElen) {
460                 wrqu->data.length = mac->wpa.IElen;
461                 if (mac->wpa.IElen <= space)
462                         memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
463                 else
464                         err = -E2BIG;
465         }
466         spin_unlock_irqrestore(&mac->lock, flags);
467         mutex_unlock(&mac->associnfo.mutex);
468
469         return err;
470 }
471 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
472
473 int
474 ieee80211softmac_wx_set_mlme(struct net_device *dev,
475                              struct iw_request_info *info,
476                              union iwreq_data *wrqu,
477                              char *extra)
478 {
479         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
480         struct iw_mlme *mlme = (struct iw_mlme *)extra;
481         u16 reason = cpu_to_le16(mlme->reason_code);
482         struct ieee80211softmac_network *net;
483         int err = -EINVAL;
484
485         mutex_lock(&mac->associnfo.mutex);
486
487         if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
488                 printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
489                 goto out;
490         }
491
492         switch (mlme->cmd) {
493         case IW_MLME_DEAUTH:
494                 net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
495                 if (!net) {
496                         printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
497                         goto out;
498                 }
499                 err =  ieee80211softmac_deauth_req(mac, net, reason);
500                 goto out;
501         case IW_MLME_DISASSOC:
502                 ieee80211softmac_send_disassoc_req(mac, reason);
503                 mac->associnfo.associated = 0;
504                 mac->associnfo.associating = 0;
505                 err = 0;
506                 goto out;
507         default:
508                 err = -EOPNOTSUPP;
509         }
510
511 out:
512         mutex_unlock(&mac->associnfo.mutex);
513
514         return err;
515 }
516 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);