Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-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 check_assoc_again:
78         mutex_lock(&sm->associnfo.mutex);
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                         sm->associnfo.associating = 0;
102                         sm->associnfo.associated = 0;
103                         /* We must unlock to avoid deadlocks with the assoc workqueue
104                          * on the associnfo.mutex */
105                         mutex_unlock(&sm->associnfo.mutex);
106                         flush_scheduled_work();
107                         /* Avoid race! Check assoc status again. Maybe someone started an
108                          * association while we flushed. */
109                         goto check_assoc_again;
110                 }
111         }
112
113         sm->associnfo.static_essid = 0;
114         sm->associnfo.assoc_wait = 0;
115
116         if (data->essid.flags && data->essid.length) {
117                 length = min((int)data->essid.length, IW_ESSID_MAX_SIZE);
118                 if (length) {
119                         memcpy(sm->associnfo.req_essid.data, extra, length);
120                         sm->associnfo.static_essid = 1;
121                 }
122         }
123
124         /* set our requested ESSID length.
125          * If applicable, we have already copied the data in */
126         sm->associnfo.req_essid.len = length;
127
128         sm->associnfo.associating = 1;
129         /* queue lower level code to do work (if necessary) */
130         schedule_delayed_work(&sm->associnfo.work, 0);
131 out:
132         mutex_unlock(&sm->associnfo.mutex);
133
134         return 0;
135 }
136 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
137
138 int
139 ieee80211softmac_wx_get_essid(struct net_device *net_dev,
140                               struct iw_request_info *info,
141                               union iwreq_data *data,
142                               char *extra)
143 {
144         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
145
146         mutex_lock(&sm->associnfo.mutex);
147         /* If all fails, return ANY (empty) */
148         data->essid.length = 0;
149         data->essid.flags = 0;  /* active */
150
151         /* If we have a statically configured ESSID then return it */
152         if (sm->associnfo.static_essid) {
153                 data->essid.length = sm->associnfo.req_essid.len;
154                 data->essid.flags = 1;  /* active */
155                 memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
156         }
157
158         /* If we're associating/associated, return that */
159         if (sm->associnfo.associated || sm->associnfo.associating) {
160                 data->essid.length = sm->associnfo.associate_essid.len;
161                 data->essid.flags = 1;  /* active */
162                 memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
163         }
164         mutex_unlock(&sm->associnfo.mutex);
165
166         return 0;
167 }
168 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
169
170 int
171 ieee80211softmac_wx_set_rate(struct net_device *net_dev,
172                              struct iw_request_info *info,
173                              union iwreq_data *data,
174                              char *extra)
175 {
176         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
177         struct ieee80211_device *ieee = mac->ieee;
178         unsigned long flags;
179         s32 in_rate = data->bitrate.value;
180         u8 rate;
181         int is_ofdm = 0;
182         int err = -EINVAL;
183
184         if (in_rate == -1) {
185                 if (ieee->modulation & IEEE80211_OFDM_MODULATION)
186                         in_rate = 24000000;
187                 else
188                         in_rate = 11000000;
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);