blob: b559aa9b5507712acea32ca7b6e7c614e5e37773 [file] [log] [blame]
Johannes Berg370121e2006-01-04 16:32:16 +01001/*
2 * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them
Johannes Berg4855d252006-01-12 21:12:59 +01003 *
Johannes Berg79859052006-01-31 19:31:41 +01004 * 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>
Johannes Berg4855d252006-01-12 21:12:59 +01009 *
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.
Johannes Berg370121e2006-01-04 16:32:16 +010025 */
26
27#include "ieee80211softmac_priv.h"
28
29#include <net/iw_handler.h>
30
31
32int
33ieee80211softmac_wx_trigger_scan(struct net_device *net_dev,
34 struct iw_request_info *info,
35 union iwreq_data *data,
36 char *extra)
37{
38 struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
39 return ieee80211softmac_start_scan(sm);
40}
41EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan);
42
43
44int
45ieee80211softmac_wx_get_scan_results(struct net_device *net_dev,
46 struct iw_request_info *info,
47 union iwreq_data *data,
48 char *extra)
49{
50 struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
51 return ieee80211_wx_get_scan(sm->ieee, info, data, extra);
52}
53EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results);
54
55int
56ieee80211softmac_wx_set_essid(struct net_device *net_dev,
57 struct iw_request_info *info,
58 union iwreq_data *data,
59 char *extra)
60{
61 struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
62 int length = 0;
63 unsigned long flags;
64
65 spin_lock_irqsave(&sm->lock, flags);
66
67 sm->associnfo.static_essid = 0;
68
69 if (data->essid.flags && data->essid.length && extra /*required?*/) {
70 length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE);
71 if (length) {
72 memcpy(sm->associnfo.req_essid.data, extra, length);
73 sm->associnfo.static_essid = 1;
74 }
75 }
76 sm->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
77
78 /* set our requested ESSID length.
79 * If applicable, we have already copied the data in */
80 sm->associnfo.req_essid.len = length;
81
82 /* queue lower level code to do work (if necessary) */
Johannes Berg5c4df6d2006-01-06 01:43:45 +010083 schedule_work(&sm->associnfo.work);
Johannes Berg370121e2006-01-04 16:32:16 +010084
85 spin_unlock_irqrestore(&sm->lock, flags);
86 return 0;
87}
88EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid);
89
90int
91ieee80211softmac_wx_get_essid(struct net_device *net_dev,
92 struct iw_request_info *info,
93 union iwreq_data *data,
94 char *extra)
95{
96 struct ieee80211softmac_device *sm = ieee80211_priv(net_dev);
97 unsigned long flags;
98
99 /* avoid getting inconsistent information */
100 spin_lock_irqsave(&sm->lock, flags);
101 /* If all fails, return ANY (empty) */
102 data->essid.length = 0;
103 data->essid.flags = 0; /* active */
104
105 /* If we have a statically configured ESSID then return it */
106 if (sm->associnfo.static_essid) {
107 data->essid.length = sm->associnfo.req_essid.len;
108 data->essid.flags = 1; /* active */
109 memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len);
110 }
111
112 /* If we're associating/associated, return that */
113 if (sm->associated || sm->associnfo.associating) {
114 data->essid.length = sm->associnfo.associate_essid.len;
115 data->essid.flags = 1; /* active */
116 memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len);
117 }
118 spin_unlock_irqrestore(&sm->lock, flags);
119 return 0;
120}
121EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid);
122
123int
124ieee80211softmac_wx_set_rate(struct net_device *net_dev,
125 struct iw_request_info *info,
126 union iwreq_data *data,
127 char *extra)
128{
129 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
130 struct ieee80211_device *ieee = mac->ieee;
131 unsigned long flags;
132 s32 in_rate = data->bitrate.value;
133 u8 rate;
134 int is_ofdm = 0;
135 int err = -EINVAL;
136
137 if (in_rate == -1) {
David Woodhouse2638fed2006-03-23 22:43:38 +0000138 /* FIXME: We don't correctly handle backing down to lower
139 rates, so 801.11g devices start off at 11M for now. People
140 can manually change it if they really need to, but 11M is
141 more reliable. Note similar logic in
142 ieee80211softmac_wx_set_rate() */
143 if (ieee->modulation & IEEE80211_CCK_MODULATION)
Johannes Berg370121e2006-01-04 16:32:16 +0100144 in_rate = 11000000;
David Woodhouse2638fed2006-03-23 22:43:38 +0000145 else
146 in_rate = 54000000;
Johannes Berg370121e2006-01-04 16:32:16 +0100147 }
148
149 switch (in_rate) {
150 case 1000000:
151 rate = IEEE80211_CCK_RATE_1MB;
152 break;
153 case 2000000:
154 rate = IEEE80211_CCK_RATE_2MB;
155 break;
156 case 5500000:
157 rate = IEEE80211_CCK_RATE_5MB;
158 break;
159 case 11000000:
160 rate = IEEE80211_CCK_RATE_11MB;
161 break;
162 case 6000000:
163 rate = IEEE80211_OFDM_RATE_6MB;
164 is_ofdm = 1;
165 break;
166 case 9000000:
167 rate = IEEE80211_OFDM_RATE_9MB;
168 is_ofdm = 1;
169 break;
170 case 12000000:
171 rate = IEEE80211_OFDM_RATE_12MB;
172 is_ofdm = 1;
173 break;
174 case 18000000:
175 rate = IEEE80211_OFDM_RATE_18MB;
176 is_ofdm = 1;
177 break;
178 case 24000000:
179 rate = IEEE80211_OFDM_RATE_24MB;
180 is_ofdm = 1;
181 break;
182 case 36000000:
183 rate = IEEE80211_OFDM_RATE_36MB;
184 is_ofdm = 1;
185 break;
186 case 48000000:
187 rate = IEEE80211_OFDM_RATE_48MB;
188 is_ofdm = 1;
189 break;
190 case 54000000:
191 rate = IEEE80211_OFDM_RATE_54MB;
192 is_ofdm = 1;
193 break;
194 default:
195 goto out;
196 }
197
198 spin_lock_irqsave(&mac->lock, flags);
199
200 /* Check if correct modulation for this PHY. */
201 if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION))
202 goto out_unlock;
203
204 mac->txrates.default_rate = rate;
205 mac->txrates.default_fallback = lower_rate(mac, rate);
206 err = 0;
207
208out_unlock:
209 spin_unlock_irqrestore(&mac->lock, flags);
210out:
211 return err;
212}
213EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate);
214
215int
216ieee80211softmac_wx_get_rate(struct net_device *net_dev,
217 struct iw_request_info *info,
218 union iwreq_data *data,
219 char *extra)
220{
221 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
222 unsigned long flags;
223 int err = -EINVAL;
224
225 spin_lock_irqsave(&mac->lock, flags);
226 switch (mac->txrates.default_rate) {
227 case IEEE80211_CCK_RATE_1MB:
228 data->bitrate.value = 1000000;
229 break;
230 case IEEE80211_CCK_RATE_2MB:
231 data->bitrate.value = 2000000;
232 break;
233 case IEEE80211_CCK_RATE_5MB:
234 data->bitrate.value = 5500000;
235 break;
236 case IEEE80211_CCK_RATE_11MB:
237 data->bitrate.value = 11000000;
238 break;
239 case IEEE80211_OFDM_RATE_6MB:
240 data->bitrate.value = 6000000;
241 break;
242 case IEEE80211_OFDM_RATE_9MB:
243 data->bitrate.value = 9000000;
244 break;
245 case IEEE80211_OFDM_RATE_12MB:
246 data->bitrate.value = 12000000;
247 break;
248 case IEEE80211_OFDM_RATE_18MB:
249 data->bitrate.value = 18000000;
250 break;
251 case IEEE80211_OFDM_RATE_24MB:
252 data->bitrate.value = 24000000;
253 break;
254 case IEEE80211_OFDM_RATE_36MB:
255 data->bitrate.value = 36000000;
256 break;
257 case IEEE80211_OFDM_RATE_48MB:
258 data->bitrate.value = 48000000;
259 break;
260 case IEEE80211_OFDM_RATE_54MB:
261 data->bitrate.value = 54000000;
262 break;
263 default:
264 assert(0);
265 goto out_unlock;
266 }
267 err = 0;
268out_unlock:
269 spin_unlock_irqrestore(&mac->lock, flags);
270
271 return err;
272}
273EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate);
274
275int
276ieee80211softmac_wx_get_wap(struct net_device *net_dev,
277 struct iw_request_info *info,
278 union iwreq_data *data,
279 char *extra)
280{
281 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
282 int err = 0;
283 unsigned long flags;
284
285 spin_lock_irqsave(&mac->lock, flags);
286 if (mac->associnfo.bssvalid)
287 memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN);
288 else
289 memset(data->ap_addr.sa_data, 0xff, ETH_ALEN);
290 data->ap_addr.sa_family = ARPHRD_ETHER;
291 spin_unlock_irqrestore(&mac->lock, flags);
292 return err;
293}
294EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap);
295
296int
297ieee80211softmac_wx_set_wap(struct net_device *net_dev,
298 struct iw_request_info *info,
299 union iwreq_data *data,
300 char *extra)
301{
302 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
303 static const unsigned char any[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
304 static const unsigned char off[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
305 unsigned long flags;
306
307 /* sanity check */
308 if (data->ap_addr.sa_family != ARPHRD_ETHER) {
309 return -EINVAL;
310 }
311
312 spin_lock_irqsave(&mac->lock, flags);
313 if (!memcmp(any, data->ap_addr.sa_data, ETH_ALEN) ||
314 !memcmp(off, data->ap_addr.sa_data, ETH_ALEN)) {
Johannes Berg5c4df6d2006-01-06 01:43:45 +0100315 schedule_work(&mac->associnfo.work);
Johannes Berg370121e2006-01-04 16:32:16 +0100316 goto out;
317 } else {
318 if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) {
319 if (mac->associnfo.associating || mac->associated) {
320 /* bssid unchanged and associated or associating - just return */
321 goto out;
322 }
323 } else {
324 /* copy new value in data->ap_addr.sa_data to bssid */
325 memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN);
326 }
327 /* queue associate if new bssid or (old one again and not associated) */
Johannes Berg5c4df6d2006-01-06 01:43:45 +0100328 schedule_work(&mac->associnfo.work);
Johannes Berg370121e2006-01-04 16:32:16 +0100329 }
330
331out:
332 spin_unlock_irqrestore(&mac->lock, flags);
333 return 0;
334}
335EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap);
336
337int
338ieee80211softmac_wx_set_genie(struct net_device *dev,
339 struct iw_request_info *info,
340 union iwreq_data *wrqu,
341 char *extra)
342{
343 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
344 unsigned long flags;
345 int err = 0;
346 char *buf;
347 int i;
348
349 spin_lock_irqsave(&mac->lock, flags);
350 /* bleh. shouldn't be locked for that kmalloc... */
351
352 if (wrqu->data.length) {
353 if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) {
354 /* this is an IE, so the length must be
355 * correct. Is it possible though that
356 * more than one IE is passed in?
357 */
358 err = -EINVAL;
359 goto out;
360 }
361 if (mac->wpa.IEbuflen <= wrqu->data.length) {
362 buf = kmalloc(wrqu->data.length, GFP_ATOMIC);
363 if (!buf) {
364 err = -ENOMEM;
365 goto out;
366 }
367 kfree(mac->wpa.IE);
368 mac->wpa.IE = buf;
369 mac->wpa.IEbuflen = wrqu->data.length;
370 }
371 memcpy(mac->wpa.IE, extra, wrqu->data.length);
372 dprintk(KERN_INFO PFX "generic IE set to ");
373 for (i=0;i<wrqu->data.length;i++)
374 dprintk("%.2x", mac->wpa.IE[i]);
375 dprintk("\n");
376 mac->wpa.IElen = wrqu->data.length;
377 } else {
378 kfree(mac->wpa.IE);
379 mac->wpa.IE = NULL;
380 mac->wpa.IElen = 0;
381 mac->wpa.IEbuflen = 0;
382 }
383
384 out:
385 spin_unlock_irqrestore(&mac->lock, flags);
386 return err;
387}
388EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie);
389
390int
391ieee80211softmac_wx_get_genie(struct net_device *dev,
392 struct iw_request_info *info,
393 union iwreq_data *wrqu,
394 char *extra)
395{
396 struct ieee80211softmac_device *mac = ieee80211_priv(dev);
397 unsigned long flags;
398 int err = 0;
399 int space = wrqu->data.length;
400
401 spin_lock_irqsave(&mac->lock, flags);
402
403 wrqu->data.length = 0;
404
405 if (mac->wpa.IE && mac->wpa.IElen) {
406 wrqu->data.length = mac->wpa.IElen;
407 if (mac->wpa.IElen <= space)
408 memcpy(extra, mac->wpa.IE, mac->wpa.IElen);
409 else
410 err = -E2BIG;
411 }
412 spin_unlock_irqrestore(&mac->lock, flags);
413 return err;
414}
415EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie);
416