Merge git://oss.sgi.com:8090/xfs/xfs-2.6
[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                 /* FIXME: We don't correctly handle backing down to lower
181                    rates, so 801.11g devices start off at 11M for now. People
182                    can manually change it if they really need to, but 11M is
183                    more reliable. Note similar logic in
184                    ieee80211softmac_wx_set_rate() */
185                 if (ieee->modulation & IEEE80211_CCK_MODULATION)
186                         in_rate = 11000000;
187                 else
188                         in_rate = 54000000;
189         }
190
191         switch (in_rate) {
192         case 1000000:
193                 rate = IEEE80211_CCK_RATE_1MB;
194                 break;
195         case 2000000:
196                 rate = IEEE80211_CCK_RATE_2MB;
197                 break;
198         case 5500000:
199                 rate = IEEE80211_CCK_RATE_5MB;
200                 break;
201         case 11000000:
202                 rate = IEEE80211_CCK_RATE_11MB;
203                 break;
204         case 6000000:
205                 rate = IEEE80211_OFDM_RATE_6MB;
206                 is_ofdm = 1;
207                 break;
208         case 9000000:
209                 rate = IEEE80211_OFDM_RATE_9MB;
210                 is_ofdm = 1;
211                 break;
212         case 12000000:
213                 rate = IEEE80211_OFDM_RATE_12MB;
214                 is_ofdm = 1;
215                 break;
216         case 18000000:
217                 rate = IEEE80211_OFDM_RATE_18MB;
218                 is_ofdm = 1;
219                 break;
220         case 24000000:
221                 rate = IEEE80211_OFDM_RATE_24MB;
222                 is_ofdm = 1;
223                 break;
224         case 36000000:
225                 rate = IEEE80211_OFDM_RATE_36MB;
226                 is_ofdm = 1;
227                 break;
228         case 48000000:
229                 rate = IEEE80211_OFDM_RATE_48MB;
230                 is_ofdm = 1;
231                 break;
232         case 54000000:
233                 rate = IEEE80211_OFDM_RATE_54MB;
234                 is_ofdm = 1;
235                 break;
236         default:
237                 goto out;
238         }
239
240         spin_lock_irqsave(&mac->lock, flags);
241
242         /* Check if correct modulation for this PHY. */
243         if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
244                 goto out_unlock;
245
246         mac->txrates.user_rate = rate;
247         ieee80211softmac_recalc_txrates(mac);
248         err = 0;
249
250 out_unlock:
251         spin_unlock_irqrestore(&mac->lock, flags);
252 out:
253         return err;
254 }
255 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
256
257 int
258 ieee80211softmac_wx_get_rate(struct net_device *net_dev,
259                              struct iw_request_info *info,
260                              union iwreq_data *data,
261                              char *extra)
262 {
263         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
264         unsigned long flags;
265         int err = -EINVAL;
266
267         spin_lock_irqsave(&mac->lock, flags);
268
269         if (unlikely(!mac->running)) {
270                 err = -ENODEV;
271                 goto out_unlock;
272         }
273
274         switch (mac->txrates.default_rate) {
275         case IEEE80211_CCK_RATE_1MB:
276                 data->bitrate.value = 1000000;
277                 break;
278         case IEEE80211_CCK_RATE_2MB:
279                 data->bitrate.value = 2000000;
280                 break;
281         case IEEE80211_CCK_RATE_5MB:
282                 data->bitrate.value = 5500000;
283                 break;
284         case IEEE80211_CCK_RATE_11MB:
285                 data->bitrate.value = 11000000;
286                 break;
287         case IEEE80211_OFDM_RATE_6MB:
288                 data->bitrate.value = 6000000;
289                 break;
290         case IEEE80211_OFDM_RATE_9MB:
291                 data->bitrate.value = 9000000;
292                 break;
293         case IEEE80211_OFDM_RATE_12MB:
294                 data->bitrate.value = 12000000;
295                 break;
296         case IEEE80211_OFDM_RATE_18MB:
297                 data->bitrate.value = 18000000;
298                 break;
299         case IEEE80211_OFDM_RATE_24MB:
300                 data->bitrate.value = 24000000;
301                 break;
302         case IEEE80211_OFDM_RATE_36MB:
303                 data->bitrate.value = 36000000;
304                 break;
305         case IEEE80211_OFDM_RATE_48MB:
306                 data->bitrate.value = 48000000;
307                 break;
308         case IEEE80211_OFDM_RATE_54MB:
309                 data->bitrate.value = 54000000;
310                 break;
311         default:
312                 assert(0);
313                 goto out_unlock;
314         }
315         err = 0;
316 out_unlock:
317         spin_unlock_irqrestore(&mac->lock, flags);
318
319         return err;
320 }
321 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
322
323 int
324 ieee80211softmac_wx_get_wap(struct net_device *net_dev,
325                             struct iw_request_info *info,
326                             union iwreq_data *data,
327                             char *extra)
328 {
329         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
330         int err = 0;
331
332         mutex_lock(&mac->associnfo.mutex);
333         if (mac->associnfo.bssvalid)
334                 memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
335         else
336                 memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
337         data->ap_addr.sa_family = ARPHRD_ETHER;
338         mutex_unlock(&mac->associnfo.mutex);
339
340         return err;
341 }
342 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
343
344 int
345 ieee80211softmac_wx_set_wap(struct net_device *net_dev,
346                             struct iw_request_info *info,
347                             union iwreq_data *data,
348                             char *extra)
349 {
350         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
351
352         /* sanity check */
353         if (data->ap_addr.sa_family != ARPHRD_ETHER) {
354                 return -EINVAL;
355         }
356
357         mutex_lock(&mac->associnfo.mutex);
358         if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
359                 /* the bssid we have is not to be fixed any longer,
360                  * and we should reassociate to the best AP. */
361                 mac->associnfo.bssfixed = 0;
362                 /* force reassociation */
363                 mac->associnfo.bssvalid = 0;
364                 if (mac->associnfo.associated)
365                         schedule_delayed_work(&mac->associnfo.work, 0);
366         } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
367                 /* the bssid we have is no longer fixed */
368                 mac->associnfo.bssfixed = 0;
369         } else {
370                 if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
371                         if (mac->associnfo.associating || mac->associnfo.associated) {
372                         /* bssid unchanged and associated or associating - just return */
373                                 goto out;
374                         }
375                 } else {
376                         /* copy new value in data->ap_addr.sa_data to bssid */
377                         memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
378                 }
379                 /* tell the other code that this bssid should be used no matter what */
380                 mac->associnfo.bssfixed = 1;
381                 /* queue associate if new bssid or (old one again and not associated) */
382                 schedule_delayed_work(&mac->associnfo.work, 0);
383         }
384
385  out:
386         mutex_unlock(&mac->associnfo.mutex);
387
388         return 0;
389 }
390 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
391
392 int
393 ieee80211softmac_wx_set_genie(struct net_device *dev,
394                               struct iw_request_info *info,
395                               union iwreq_data *wrqu,
396                               char *extra)
397 {
398         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
399         unsigned long flags;
400         int err = 0;
401         char *buf;
402         int i;
403
404         mutex_lock(&mac->associnfo.mutex);
405         spin_lock_irqsave(&mac->lock, flags);
406         /* bleh. shouldn't be locked for that kmalloc... */
407
408         if (wrqu->data.length) {
409                 if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
410                         /* this is an IE, so the length must be
411                          * correct. Is it possible though that
412                          * more than one IE is passed in?
413                          */
414                         err = -EINVAL;
415                         goto out;
416                 }
417                 if (mac->wpa.IEbuflen <= wrqu->data.length) {
418                         buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
419                         if (!buf) {
420                                 err = -ENOMEM;
421                                 goto out;
422                         }
423                         kfree(mac->wpa.IE);
424                         mac->wpa.IE = buf;
425                         mac->wpa.IEbuflen = wrqu->data.length;
426                 }
427                 memcpy(mac->wpa.IE, extra, wrqu->data.length);
428                 dprintk(KERN_INFO PFX "generic IE set to ");
429                 for (i=0;i<wrqu->data.length;i++)
430                         dprintk("%.2x", (u8)mac->wpa.IE[i]);
431                 dprintk("\n");
432                 mac->wpa.IElen = wrqu->data.length;
433         } else {
434                 kfree(mac->wpa.IE);
435                 mac->wpa.IE = NULL;
436                 mac->wpa.IElen = 0;
437                 mac->wpa.IEbuflen = 0;
438         }
439
440  out:
441         spin_unlock_irqrestore(&mac->lock, flags);
442         mutex_unlock(&mac->associnfo.mutex);
443
444         return err;
445 }
446 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
447
448 int
449 ieee80211softmac_wx_get_genie(struct net_device *dev,
450                               struct iw_request_info *info,
451                               union iwreq_data *wrqu,
452                               char *extra)
453 {
454         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
455         unsigned long flags;
456         int err = 0;
457         int space = wrqu->data.length;
458
459         mutex_lock(&mac->associnfo.mutex);
460         spin_lock_irqsave(&mac->lock, flags);
461
462         wrqu->data.length = 0;
463
464         if (mac->wpa.IE && mac->wpa.IElen) {
465                 wrqu->data.length = mac->wpa.IElen;
466                 if (mac->wpa.IElen <= space)
467                         memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
468                 else
469                         err = -E2BIG;
470         }
471         spin_unlock_irqrestore(&mac->lock, flags);
472         mutex_unlock(&mac->associnfo.mutex);
473
474         return err;
475 }
476 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
477
478 int
479 ieee80211softmac_wx_set_mlme(struct net_device *dev,
480                              struct iw_request_info *info,
481                              union iwreq_data *wrqu,
482                              char *extra)
483 {
484         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
485         struct iw_mlme *mlme = (struct iw_mlme *)extra;
486         u16 reason = cpu_to_le16(mlme->reason_code);
487         struct ieee80211softmac_network *net;
488         int err = -EINVAL;
489
490         mutex_lock(&mac->associnfo.mutex);
491
492         if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
493                 printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
494                 goto out;
495         }
496
497         switch (mlme->cmd) {
498         case IW_MLME_DEAUTH:
499                 net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
500                 if (!net) {
501                         printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
502                         goto out;
503                 }
504                 err =  ieee80211softmac_deauth_req(mac, net, reason);
505                 goto out;
506         case IW_MLME_DISASSOC:
507                 ieee80211softmac_send_disassoc_req(mac, reason);
508                 mac->associnfo.associated = 0;
509                 mac->associnfo.associating = 0;
510                 err = 0;
511                 goto out;
512         default:
513                 err = -EOPNOTSUPP;
514         }
515
516 out:
517         mutex_unlock(&mac->associnfo.mutex);
518
519         return err;
520 }
521 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);