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