Pull trivial into test branch
[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         switch (mac->txrates.default_rate) {
269         case IEEE80211_CCK_RATE_1MB:
270                 data->bitrate.value = 1000000;
271                 break;
272         case IEEE80211_CCK_RATE_2MB:
273                 data->bitrate.value = 2000000;
274                 break;
275         case IEEE80211_CCK_RATE_5MB:
276                 data->bitrate.value = 5500000;
277                 break;
278         case IEEE80211_CCK_RATE_11MB:
279                 data->bitrate.value = 11000000;
280                 break;
281         case IEEE80211_OFDM_RATE_6MB:
282                 data->bitrate.value = 6000000;
283                 break;
284         case IEEE80211_OFDM_RATE_9MB:
285                 data->bitrate.value = 9000000;
286                 break;
287         case IEEE80211_OFDM_RATE_12MB:
288                 data->bitrate.value = 12000000;
289                 break;
290         case IEEE80211_OFDM_RATE_18MB:
291                 data->bitrate.value = 18000000;
292                 break;
293         case IEEE80211_OFDM_RATE_24MB:
294                 data->bitrate.value = 24000000;
295                 break;
296         case IEEE80211_OFDM_RATE_36MB:
297                 data->bitrate.value = 36000000;
298                 break;
299         case IEEE80211_OFDM_RATE_48MB:
300                 data->bitrate.value = 48000000;
301                 break;
302         case IEEE80211_OFDM_RATE_54MB:
303                 data->bitrate.value = 54000000;
304                 break;
305         default:
306                 assert(0);
307                 goto out_unlock;
308         }
309         err = 0;
310 out_unlock:
311         spin_unlock_irqrestore(&mac->lock, flags);
312
313         return err;
314 }
315 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
316
317 int
318 ieee80211softmac_wx_get_wap(struct net_device *net_dev,
319                             struct iw_request_info *info,
320                             union iwreq_data *data,
321                             char *extra)
322 {
323         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
324         int err = 0;
325
326         mutex_lock(&mac->associnfo.mutex);
327         if (mac->associnfo.bssvalid)
328                 memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
329         else
330                 memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
331         data->ap_addr.sa_family = ARPHRD_ETHER;
332         mutex_unlock(&mac->associnfo.mutex);
333
334         return err;
335 }
336 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
337
338 int
339 ieee80211softmac_wx_set_wap(struct net_device *net_dev,
340                             struct iw_request_info *info,
341                             union iwreq_data *data,
342                             char *extra)
343 {
344         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
345
346         /* sanity check */
347         if (data->ap_addr.sa_family != ARPHRD_ETHER) {
348                 return -EINVAL;
349         }
350
351         mutex_lock(&mac->associnfo.mutex);
352         if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
353                 /* the bssid we have is not to be fixed any longer,
354                  * and we should reassociate to the best AP. */
355                 mac->associnfo.bssfixed = 0;
356                 /* force reassociation */
357                 mac->associnfo.bssvalid = 0;
358                 if (mac->associnfo.associated)
359                         schedule_delayed_work(&mac->associnfo.work, 0);
360         } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
361                 /* the bssid we have is no longer fixed */
362                 mac->associnfo.bssfixed = 0;
363         } else {
364                 if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
365                         if (mac->associnfo.associating || mac->associnfo.associated) {
366                         /* bssid unchanged and associated or associating - just return */
367                                 goto out;
368                         }
369                 } else {
370                         /* copy new value in data->ap_addr.sa_data to bssid */
371                         memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
372                 }
373                 /* tell the other code that this bssid should be used no matter what */
374                 mac->associnfo.bssfixed = 1;
375                 /* queue associate if new bssid or (old one again and not associated) */
376                 schedule_delayed_work(&mac->associnfo.work, 0);
377         }
378
379  out:
380         mutex_unlock(&mac->associnfo.mutex);
381
382         return 0;
383 }
384 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
385
386 int
387 ieee80211softmac_wx_set_genie(struct net_device *dev,
388                               struct iw_request_info *info,
389                               union iwreq_data *wrqu,
390                               char *extra)
391 {
392         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
393         unsigned long flags;
394         int err = 0;
395         char *buf;
396         int i;
397
398         mutex_lock(&mac->associnfo.mutex);
399         spin_lock_irqsave(&mac->lock, flags);
400         /* bleh. shouldn't be locked for that kmalloc... */
401
402         if (wrqu->data.length) {
403                 if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
404                         /* this is an IE, so the length must be
405                          * correct. Is it possible though that
406                          * more than one IE is passed in?
407                          */
408                         err = -EINVAL;
409                         goto out;
410                 }
411                 if (mac->wpa.IEbuflen <= wrqu->data.length) {
412                         buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
413                         if (!buf) {
414                                 err = -ENOMEM;
415                                 goto out;
416                         }
417                         kfree(mac->wpa.IE);
418                         mac->wpa.IE = buf;
419                         mac->wpa.IEbuflen = wrqu->data.length;
420                 }
421                 memcpy(mac->wpa.IE, extra, wrqu->data.length);
422                 dprintk(KERN_INFO PFX "generic IE set to ");
423                 for (i=0;i<wrqu->data.length;i++)
424                         dprintk("%.2x", (u8)mac->wpa.IE[i]);
425                 dprintk("\n");
426                 mac->wpa.IElen = wrqu->data.length;
427         } else {
428                 kfree(mac->wpa.IE);
429                 mac->wpa.IE = NULL;
430                 mac->wpa.IElen = 0;
431                 mac->wpa.IEbuflen = 0;
432         }
433
434  out:   
435         spin_unlock_irqrestore(&mac->lock, flags);
436         mutex_unlock(&mac->associnfo.mutex);
437
438         return err;
439 }
440 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
441
442 int
443 ieee80211softmac_wx_get_genie(struct net_device *dev,
444                               struct iw_request_info *info,
445                               union iwreq_data *wrqu,
446                               char *extra)
447 {
448         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
449         unsigned long flags;
450         int err = 0;
451         int space = wrqu->data.length;
452
453         mutex_lock(&mac->associnfo.mutex);
454         spin_lock_irqsave(&mac->lock, flags);
455         
456         wrqu->data.length = 0;
457         
458         if (mac->wpa.IE && mac->wpa.IElen) {
459                 wrqu->data.length = mac->wpa.IElen;
460                 if (mac->wpa.IElen <= space)
461                         memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
462                 else
463                         err = -E2BIG;
464         }
465         spin_unlock_irqrestore(&mac->lock, flags);
466         mutex_lock(&mac->associnfo.mutex);
467
468         return err;
469 }
470 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
471
472 int
473 ieee80211softmac_wx_set_mlme(struct net_device *dev,
474                              struct iw_request_info *info,
475                              union iwreq_data *wrqu,
476                              char *extra)
477 {
478         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
479         struct iw_mlme *mlme = (struct iw_mlme *)extra;
480         u16 reason = cpu_to_le16(mlme->reason_code);
481         struct ieee80211softmac_network *net;
482         int err = -EINVAL;
483
484         mutex_lock(&mac->associnfo.mutex);
485
486         if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) {
487                 printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n");
488                 goto out;
489         }
490
491         switch (mlme->cmd) {
492         case IW_MLME_DEAUTH:
493                 net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data);
494                 if (!net) {
495                         printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n");
496                         goto out;
497                 }
498                 err =  ieee80211softmac_deauth_req(mac, net, reason);
499                 goto out;
500         case IW_MLME_DISASSOC:
501                 ieee80211softmac_send_disassoc_req(mac, reason);
502                 mac->associnfo.associated = 0;
503                 mac->associnfo.associating = 0;
504                 err = 0;
505                 goto out;
506         default:
507                 err = -EOPNOTSUPP;
508         }
509
510 out:
511         mutex_unlock(&mac->associnfo.mutex);
512
513         return err;
514 }
515 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);