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