[PATCH] softmac: Fix inability to associate with WEP networks
[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_auth_queue_item *authptr;
74         int length = 0;
75
76 check_assoc_again:
77         mutex_lock(&sm->associnfo.mutex);
78         if((sm->associnfo.associating || sm->associnfo.associated) &&
79            (data->essid.flags && data->essid.length)) {
80                 dprintk(KERN_INFO PFX "Canceling existing associate request!\n");
81                 /* Cancel assoc work */
82                 cancel_delayed_work(&sm->associnfo.work);
83                 /* We don't have to do this, but it's a little cleaner */
84                 list_for_each_entry(authptr, &sm->auth_queue, list)
85                         cancel_delayed_work(&authptr->work);
86                 sm->associnfo.bssvalid = 0;
87                 sm->associnfo.bssfixed = 0;
88                 sm->associnfo.associating = 0;
89                 sm->associnfo.associated = 0;
90                 /* We must unlock to avoid deadlocks with the assoc workqueue
91                  * on the associnfo.mutex */
92                 mutex_unlock(&sm->associnfo.mutex);
93                 flush_scheduled_work();
94                 /* Avoid race! Check assoc status again. Maybe someone started an
95                  * association while we flushed. */
96                 goto check_assoc_again;
97         }
98
99         sm->associnfo.static_essid = 0;
100         sm->associnfo.assoc_wait = 0;
101
102         if (data->essid.flags && data->essid.length) {
103                 length = min((int)data->essid.length, IW_ESSID_MAX_SIZE);
104                 if (length) {
105                         memcpy(sm->associnfo.req_essid.data, extra, length);
106                         sm->associnfo.static_essid = 1;
107                 }
108         }
109
110         /* set our requested ESSID length.
111          * If applicable, we have already copied the data in */
112         sm->associnfo.req_essid.len = length;
113
114         sm->associnfo.associating = 1;
115         /* queue lower level code to do work (if necessary) */
116         schedule_delayed_work(&sm->associnfo.work, 0);
117 out:
118         mutex_unlock(&sm->associnfo.mutex);
119
120         return 0;
121 }
122 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
123
124 int
125 ieee80211softmac_wx_get_essid(struct net_device *net_dev,
126                               struct iw_request_info *info,
127                               union iwreq_data *data,
128                               char *extra)
129 {
130         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
131
132         mutex_lock(&sm->associnfo.mutex);
133         /* If all fails, return ANY (empty) */
134         data->essid.length = 0;
135         data->essid.flags = 0;  /* active */
136
137         /* If we have a statically configured ESSID then return it */
138         if (sm->associnfo.static_essid) {
139                 data->essid.length = sm->associnfo.req_essid.len;
140                 data->essid.flags = 1;  /* active */
141                 memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
142                 dprintk(KERN_INFO PFX "Getting essid from req_essid\n");
143         } else if (sm->associnfo.associated || sm->associnfo.associating) {
144         /* If we're associating/associated, return that */
145                 data->essid.length = sm->associnfo.associate_essid.len;
146                 data->essid.flags = 1;  /* active */
147                 memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
148                 dprintk(KERN_INFO PFX "Getting essid from associate_essid\n");
149         }
150         mutex_unlock(&sm->associnfo.mutex);
151
152         return 0;
153 }
154 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
155
156 int
157 ieee80211softmac_wx_set_rate(struct net_device *net_dev,
158                              struct iw_request_info *info,
159                              union iwreq_data *data,
160                              char *extra)
161 {
162         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
163         struct ieee80211_device *ieee = mac->ieee;
164         unsigned long flags;
165         s32 in_rate = data->bitrate.value;
166         u8 rate;
167         int is_ofdm = 0;
168         int err = -EINVAL;
169
170         if (in_rate == -1) {
171                 if (ieee->modulation & IEEE80211_OFDM_MODULATION)
172                         in_rate = 24000000;
173                 else
174                         in_rate = 11000000;
175         }
176
177         switch (in_rate) {
178         case 1000000:
179                 rate = IEEE80211_CCK_RATE_1MB;
180                 break;
181         case 2000000:
182                 rate = IEEE80211_CCK_RATE_2MB;
183                 break;
184         case 5500000:
185                 rate = IEEE80211_CCK_RATE_5MB;
186                 break;
187         case 11000000:
188                 rate = IEEE80211_CCK_RATE_11MB;
189                 break;
190         case 6000000:
191                 rate = IEEE80211_OFDM_RATE_6MB;
192                 is_ofdm = 1;
193                 break;
194         case 9000000:
195                 rate = IEEE80211_OFDM_RATE_9MB;
196                 is_ofdm = 1;
197                 break;
198         case 12000000:
199                 rate = IEEE80211_OFDM_RATE_12MB;
200                 is_ofdm = 1;
201                 break;
202         case 18000000:
203                 rate = IEEE80211_OFDM_RATE_18MB;
204                 is_ofdm = 1;
205                 break;
206         case 24000000:
207                 rate = IEEE80211_OFDM_RATE_24MB;
208                 is_ofdm = 1;
209                 break;
210         case 36000000:
211                 rate = IEEE80211_OFDM_RATE_36MB;
212                 is_ofdm = 1;
213                 break;
214         case 48000000:
215                 rate = IEEE80211_OFDM_RATE_48MB;
216                 is_ofdm = 1;
217                 break;
218         case 54000000:
219                 rate = IEEE80211_OFDM_RATE_54MB;
220                 is_ofdm = 1;
221                 break;
222         default:
223                 goto out;
224         }
225
226         spin_lock_irqsave(&mac->lock, flags);
227
228         /* Check if correct modulation for this PHY. */
229         if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
230                 goto out_unlock;
231
232         mac->txrates.user_rate = rate;
233         ieee80211softmac_recalc_txrates(mac);
234         err = 0;
235
236 out_unlock:
237         spin_unlock_irqrestore(&mac->lock, flags);
238 out:
239         return err;
240 }
241 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
242
243 int
244 ieee80211softmac_wx_get_rate(struct net_device *net_dev,
245                              struct iw_request_info *info,
246                              union iwreq_data *data,
247                              char *extra)
248 {
249         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
250         unsigned long flags;
251         int err = -EINVAL;
252
253         spin_lock_irqsave(&mac->lock, flags);
254
255         if (unlikely(!mac->running)) {
256                 err = -ENODEV;
257                 goto out_unlock;
258         }
259
260         switch (mac->txrates.default_rate) {
261         case IEEE80211_CCK_RATE_1MB:
262                 data->bitrate.value = 1000000;
263                 break;
264         case IEEE80211_CCK_RATE_2MB:
265                 data->bitrate.value = 2000000;
266                 break;
267         case IEEE80211_CCK_RATE_5MB:
268                 data->bitrate.value = 5500000;
269                 break;
270         case IEEE80211_CCK_RATE_11MB:
271                 data->bitrate.value = 11000000;
272                 break;
273         case IEEE80211_OFDM_RATE_6MB:
274                 data->bitrate.value = 6000000;
275                 break;
276         case IEEE80211_OFDM_RATE_9MB:
277                 data->bitrate.value = 9000000;
278                 break;
279         case IEEE80211_OFDM_RATE_12MB:
280                 data->bitrate.value = 12000000;
281                 break;
282         case IEEE80211_OFDM_RATE_18MB:
283                 data->bitrate.value = 18000000;
284                 break;
285         case IEEE80211_OFDM_RATE_24MB:
286                 data->bitrate.value = 24000000;
287                 break;
288         case IEEE80211_OFDM_RATE_36MB:
289                 data->bitrate.value = 36000000;
290                 break;
291         case IEEE80211_OFDM_RATE_48MB:
292                 data->bitrate.value = 48000000;
293                 break;
294         case IEEE80211_OFDM_RATE_54MB:
295                 data->bitrate.value = 54000000;
296                 break;
297         default:
298                 assert(0);
299                 goto out_unlock;
300         }
301         err = 0;
302 out_unlock:
303         spin_unlock_irqrestore(&mac->lock, flags);
304
305         return err;
306 }
307 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
308
309 int
310 ieee80211softmac_wx_get_wap(struct net_device *net_dev,
311                             struct iw_request_info *info,
312                             union iwreq_data *data,
313                             char *extra)
314 {
315         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
316         int err = 0;
317
318         mutex_lock(&mac->associnfo.mutex);
319         if (mac->associnfo.bssvalid)
320                 memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
321         else
322                 memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
323         data->ap_addr.sa_family = ARPHRD_ETHER;
324         mutex_unlock(&mac->associnfo.mutex);
325
326         return err;
327 }
328 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
329
330 int
331 ieee80211softmac_wx_set_wap(struct net_device *net_dev,
332                             struct iw_request_info *info,
333                             union iwreq_data *data,
334                             char *extra)
335 {
336         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
337
338         /* sanity check */
339         if (data->ap_addr.sa_family != ARPHRD_ETHER) {
340                 return -EINVAL;
341         }
342
343         mutex_lock(&mac->associnfo.mutex);
344         if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
345                 /* the bssid we have is not to be fixed any longer,
346                  * and we should reassociate to the best AP. */
347                 mac->associnfo.bssfixed = 0;
348                 /* force reassociation */
349                 mac->associnfo.bssvalid = 0;
350                 if (mac->associnfo.associated)
351                         schedule_delayed_work(&mac->associnfo.work, 0);
352         } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
353                 /* the bssid we have is no longer fixed */
354                 mac->associnfo.bssfixed = 0;
355         } else {
356                 if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
357                         if (mac->associnfo.associating || mac->associnfo.associated) {
358                         /* bssid unchanged and associated or associating - just return */
359                                 goto out;
360                         }
361                 } else {
362                         /* copy new value in data->ap_addr.sa_data to bssid */
363                         memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
364                 }
365                 /* tell the other code that this bssid should be used no matter what */
366                 mac->associnfo.bssfixed = 1;
367                 /* queue associate if new bssid or (old one again and not associated) */
368                 schedule_delayed_work(&mac->associnfo.work, 0);
369         }
370
371  out:
372         mutex_unlock(&mac->associnfo.mutex);
373
374         return 0;
375 }
376 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
377
378 int
379 ieee80211softmac_wx_set_genie(struct net_device *dev,
380                               struct iw_request_info *info,
381                               union iwreq_data *wrqu,
382                               char *extra)
383 {
384         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
385         unsigned long flags;
386         int err = 0;
387         char *buf;
388         int i;
389
390         mutex_lock(&mac->associnfo.mutex);
391         spin_lock_irqsave(&mac->lock, flags);
392         /* bleh. shouldn't be locked for that kmalloc... */
393
394         if (wrqu->data.length) {
395                 if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
396                         /* this is an IE, so the length must be
397                          * correct. Is it possible though that
398                          * more than one IE is passed in?
399                          */
400                         err = -EINVAL;
401                         goto out;
402                 }
403                 if (mac->wpa.IEbuflen <= wrqu->data.length) {
404                         buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
405                         if (!buf) {
406                                 err = -ENOMEM;
407                                 goto out;
408                         }
409                         kfree(mac->wpa.IE);
410                         mac->wpa.IE = buf;
411                         mac->wpa.IEbuflen = wrqu->data.length;
412                 }
413                 memcpy(mac->wpa.IE, extra, wrqu->data.length);
414                 dprintk(KERN_INFO PFX "generic IE set to ");
415                 for (i=0;i<wrqu->data.length;i++)
416                         dprintk("%.2x", (u8)mac->wpa.IE[i]);
417                 dprintk("\n");
418                 mac->wpa.IElen = wrqu->data.length;
419         } else {
420                 kfree(mac->wpa.IE);
421                 mac->wpa.IE = NULL;
422                 mac->wpa.IElen = 0;
423                 mac->wpa.IEbuflen = 0;
424         }
425
426  out:
427         spin_unlock_irqrestore(&mac->lock, flags);
428         mutex_unlock(&mac->associnfo.mutex);
429
430         return err;
431 }
432 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
433
434 int
435 ieee80211softmac_wx_get_genie(struct net_device *dev,
436                               struct iw_request_info *info,
437                               union iwreq_data *wrqu,
438                               char *extra)
439 {
440         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
441         unsigned long flags;
442         int err = 0;
443         int space = wrqu->data.length;
444
445         mutex_lock(&mac->associnfo.mutex);
446         spin_lock_irqsave(&mac->lock, flags);
447
448         wrqu->data.length = 0;
449
450         if (mac->wpa.IE && mac->wpa.IElen) {
451                 wrqu->data.length = mac->wpa.IElen;
452                 if (mac->wpa.IElen <= space)
453                         memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
454                 else
455                         err = -E2BIG;
456         }
457         spin_unlock_irqrestore(&mac->lock, flags);
458         mutex_unlock(&mac->associnfo.mutex);
459
460         return err;
461 }
462 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
463
464 int
465 ieee80211softmac_wx_set_mlme(struct net_device *dev,
466                              struct iw_request_info *info,
467                              union iwreq_data *wrqu,
468                              char *extra)
469 {
470         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
471         struct iw_mlme *mlme = (struct iw_mlme *)extra;
472         u16 reason = cpu_to_le16(mlme->reason_code);
473         struct ieee80211softmac_network *net;
474         int err = -EINVAL;
475
476         mutex_lock(&mac->associnfo.mutex);
477
478         if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
479                 printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
480                 goto out;
481         }
482
483         switch (mlme->cmd) {
484         case IW_MLME_DEAUTH:
485                 net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
486                 if (!net) {
487                         printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
488                         goto out;
489                 }
490                 err =  ieee80211softmac_deauth_req(mac, net, reason);
491                 goto out;
492         case IW_MLME_DISASSOC:
493                 ieee80211softmac_send_disassoc_req(mac, reason);
494                 mac->associnfo.associated = 0;
495                 mac->associnfo.associating = 0;
496                 err = 0;
497                 goto out;
498         default:
499                 err = -EOPNOTSUPP;
500         }
501
502 out:
503         mutex_unlock(&mac->associnfo.mutex);
504
505         return err;
506 }
507 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);