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