Merge master.kernel.org:/home/rmk/linux-2.6-arm
[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         int length = 0;
74         unsigned long flags;
75         
76         spin_lock_irqsave(&sm->lock, flags);
77         
78         sm->associnfo.static_essid = 0;
79
80         if (data->essid.flags && data->essid.length && extra /*required?*/) {
81                 length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE);
82                 if (length) {
83                         memcpy(sm->associnfo.req_essid.data, extra, length);
84                         sm->associnfo.static_essid = 1;
85                 }
86         }
87
88         /* set our requested ESSID length.
89          * If applicable, we have already copied the data in */
90         sm->associnfo.req_essid.len = length;
91
92         /* queue lower level code to do work (if necessary) */
93         schedule_work(&sm->associnfo.work);
94
95         spin_unlock_irqrestore(&sm->lock, flags);
96         return 0;
97 }
98 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
99
100 int
101 ieee80211softmac_wx_get_essid(struct net_device *net_dev,
102                               struct iw_request_info *info,
103                               union iwreq_data *data,
104                               char *extra)
105 {
106         struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
107         unsigned long flags;
108
109         /* avoid getting inconsistent information */
110         spin_lock_irqsave(&sm->lock, flags);
111         /* If all fails, return ANY (empty) */
112         data->essid.length = 0;
113         data->essid.flags = 0;  /* active */
114         
115         /* If we have a statically configured ESSID then return it */
116         if (sm->associnfo.static_essid) {
117                 data->essid.length = sm->associnfo.req_essid.len;
118                 data->essid.flags = 1;  /* active */
119                 memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
120         }
121         
122         /* If we're associating/associated, return that */
123         if (sm->associated || sm->associnfo.associating) {
124                 data->essid.length = sm->associnfo.associate_essid.len;
125                 data->essid.flags = 1;  /* active */
126                 memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
127         }
128         spin_unlock_irqrestore(&sm->lock, flags);
129         return 0;
130 }
131 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
132
133 int
134 ieee80211softmac_wx_set_rate(struct net_device *net_dev,
135                              struct iw_request_info *info,
136                              union iwreq_data *data,
137                              char *extra)
138 {
139         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
140         struct ieee80211_device *ieee = mac->ieee;
141         unsigned long flags;
142         s32 in_rate = data->bitrate.value;
143         u8 rate;
144         int is_ofdm = 0;
145         int err = -EINVAL;
146
147         if (in_rate == -1) {
148                 /* FIXME: We don't correctly handle backing down to lower
149                    rates, so 801.11g devices start off at 11M for now. People
150                    can manually change it if they really need to, but 11M is
151                    more reliable. Note similar logic in
152                    ieee80211softmac_wx_set_rate() */     
153                 if (ieee->modulation & IEEE80211_CCK_MODULATION)
154                         in_rate = 11000000;
155                 else
156                         in_rate = 54000000;
157         }
158
159         switch (in_rate) {
160         case 1000000:
161                 rate = IEEE80211_CCK_RATE_1MB;
162                 break;
163         case 2000000:
164                 rate = IEEE80211_CCK_RATE_2MB;
165                 break;
166         case 5500000:
167                 rate = IEEE80211_CCK_RATE_5MB;
168                 break;
169         case 11000000:
170                 rate = IEEE80211_CCK_RATE_11MB;
171                 break;
172         case 6000000:
173                 rate = IEEE80211_OFDM_RATE_6MB;
174                 is_ofdm = 1;
175                 break;
176         case 9000000:
177                 rate = IEEE80211_OFDM_RATE_9MB;
178                 is_ofdm = 1;
179                 break;
180         case 12000000:
181                 rate = IEEE80211_OFDM_RATE_12MB;
182                 is_ofdm = 1;
183                 break;
184         case 18000000:
185                 rate = IEEE80211_OFDM_RATE_18MB;
186                 is_ofdm = 1;
187                 break;
188         case 24000000:
189                 rate = IEEE80211_OFDM_RATE_24MB;
190                 is_ofdm = 1;
191                 break;
192         case 36000000:
193                 rate = IEEE80211_OFDM_RATE_36MB;
194                 is_ofdm = 1;
195                 break;
196         case 48000000:
197                 rate = IEEE80211_OFDM_RATE_48MB;
198                 is_ofdm = 1;
199                 break;
200         case 54000000:
201                 rate = IEEE80211_OFDM_RATE_54MB;
202                 is_ofdm = 1;
203                 break;
204         default:
205                 goto out;
206         }
207
208         spin_lock_irqsave(&mac->lock, flags);
209
210         /* Check if correct modulation for this PHY. */
211         if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
212                 goto out_unlock;
213
214         mac->txrates.default_rate = rate;
215         mac->txrates.default_fallback = lower_rate(mac, rate);
216         err = 0;
217
218 out_unlock:     
219         spin_unlock_irqrestore(&mac->lock, flags);
220 out:
221         return err;
222 }
223 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
224
225 int
226 ieee80211softmac_wx_get_rate(struct net_device *net_dev,
227                              struct iw_request_info *info,
228                              union iwreq_data *data,
229                              char *extra)
230 {
231         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
232         unsigned long flags;
233         int err = -EINVAL;
234
235         spin_lock_irqsave(&mac->lock, flags);
236         switch (mac->txrates.default_rate) {
237         case IEEE80211_CCK_RATE_1MB:
238                 data->bitrate.value = 1000000;
239                 break;
240         case IEEE80211_CCK_RATE_2MB:
241                 data->bitrate.value = 2000000;
242                 break;
243         case IEEE80211_CCK_RATE_5MB:
244                 data->bitrate.value = 5500000;
245                 break;
246         case IEEE80211_CCK_RATE_11MB:
247                 data->bitrate.value = 11000000;
248                 break;
249         case IEEE80211_OFDM_RATE_6MB:
250                 data->bitrate.value = 6000000;
251                 break;
252         case IEEE80211_OFDM_RATE_9MB:
253                 data->bitrate.value = 9000000;
254                 break;
255         case IEEE80211_OFDM_RATE_12MB:
256                 data->bitrate.value = 12000000;
257                 break;
258         case IEEE80211_OFDM_RATE_18MB:
259                 data->bitrate.value = 18000000;
260                 break;
261         case IEEE80211_OFDM_RATE_24MB:
262                 data->bitrate.value = 24000000;
263                 break;
264         case IEEE80211_OFDM_RATE_36MB:
265                 data->bitrate.value = 36000000;
266                 break;
267         case IEEE80211_OFDM_RATE_48MB:
268                 data->bitrate.value = 48000000;
269                 break;
270         case IEEE80211_OFDM_RATE_54MB:
271                 data->bitrate.value = 54000000;
272                 break;
273         default:
274                 assert(0);
275                 goto out_unlock;
276         }
277         err = 0;
278 out_unlock:
279         spin_unlock_irqrestore(&mac->lock, flags);
280
281         return err;
282 }
283 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
284
285 int
286 ieee80211softmac_wx_get_wap(struct net_device *net_dev,
287                             struct iw_request_info *info,
288                             union iwreq_data *data,
289                             char *extra)
290 {
291         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
292         int err = 0;
293         unsigned long flags;
294
295         spin_lock_irqsave(&mac->lock, flags);
296         if (mac->associnfo.bssvalid)
297                 memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
298         else
299                 memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
300         data->ap_addr.sa_family = ARPHRD_ETHER;
301         spin_unlock_irqrestore(&mac->lock, flags);
302         return err;
303 }
304 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
305
306 int
307 ieee80211softmac_wx_set_wap(struct net_device *net_dev,
308                             struct iw_request_info *info,
309                             union iwreq_data *data,
310                             char *extra)
311 {
312         struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
313         unsigned long flags;
314
315         /* sanity check */
316         if (data->ap_addr.sa_family != ARPHRD_ETHER) {
317                 return -EINVAL;
318         }
319
320         spin_lock_irqsave(&mac->lock, flags);
321         if (is_broadcast_ether_addr(data->ap_addr.sa_data)) {
322                 /* the bssid we have is not to be fixed any longer,
323                  * and we should reassociate to the best AP. */
324                 mac->associnfo.bssfixed = 0;
325                 /* force reassociation */
326                 mac->associnfo.bssvalid = 0;
327                 if (mac->associated)
328                         schedule_work(&mac->associnfo.work);
329         } else if (is_zero_ether_addr(data->ap_addr.sa_data)) {
330                 /* the bssid we have is no longer fixed */
331                 mac->associnfo.bssfixed = 0;
332         } else {
333                 if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
334                         if (mac->associnfo.associating || mac->associated) {
335                         /* bssid unchanged and associated or associating - just return */
336                                 goto out;
337                         }
338                 } else {
339                         /* copy new value in data->ap_addr.sa_data to bssid */
340                         memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
341                 }
342                 /* tell the other code that this bssid should be used no matter what */
343                 mac->associnfo.bssfixed = 1;
344                 /* queue associate if new bssid or (old one again and not associated) */
345                 schedule_work(&mac->associnfo.work);
346         }
347
348  out:
349         spin_unlock_irqrestore(&mac->lock, flags);
350         return 0;
351 }
352 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
353
354 int
355 ieee80211softmac_wx_set_genie(struct net_device *dev,
356                               struct iw_request_info *info,
357                               union iwreq_data *wrqu,
358                               char *extra)
359 {
360         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
361         unsigned long flags;
362         int err = 0;
363         char *buf;
364         int i;
365         
366         spin_lock_irqsave(&mac->lock, flags);
367         /* bleh. shouldn't be locked for that kmalloc... */
368
369         if (wrqu->data.length) {
370                 if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
371                         /* this is an IE, so the length must be
372                          * correct. Is it possible though that
373                          * more than one IE is passed in?
374                          */
375                         err = -EINVAL;
376                         goto out;
377                 }
378                 if (mac->wpa.IEbuflen <= wrqu->data.length) {
379                         buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
380                         if (!buf) {
381                                 err = -ENOMEM;
382                                 goto out;
383                         }
384                         kfree(mac->wpa.IE);
385                         mac->wpa.IE = buf;
386                         mac->wpa.IEbuflen = wrqu->data.length;
387                 }
388                 memcpy(mac->wpa.IE, extra, wrqu->data.length);
389                 dprintk(KERN_INFO PFX "generic IE set to ");
390                 for (i=0;i<wrqu->data.length;i++)
391                         dprintk("%.2x", mac->wpa.IE[i]);
392                 dprintk("\n");
393                 mac->wpa.IElen = wrqu->data.length;
394         } else {
395                 kfree(mac->wpa.IE);
396                 mac->wpa.IE = NULL;
397                 mac->wpa.IElen = 0;
398                 mac->wpa.IEbuflen = 0;
399         }
400
401  out:   
402         spin_unlock_irqrestore(&mac->lock, flags);
403         return err;
404 }
405 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
406
407 int
408 ieee80211softmac_wx_get_genie(struct net_device *dev,
409                               struct iw_request_info *info,
410                               union iwreq_data *wrqu,
411                               char *extra)
412 {
413         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
414         unsigned long flags;
415         int err = 0;
416         int space = wrqu->data.length;
417         
418         spin_lock_irqsave(&mac->lock, flags);
419         
420         wrqu->data.length = 0;
421         
422         if (mac->wpa.IE && mac->wpa.IElen) {
423                 wrqu->data.length = mac->wpa.IElen;
424                 if (mac->wpa.IElen <= space)
425                         memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
426                 else
427                         err = -E2BIG;
428         }
429         spin_unlock_irqrestore(&mac->lock, flags);
430         return err;
431 }
432 EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
433