| /* | 
 |  * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them | 
 |  * | 
 |  * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net> | 
 |  *                          Joseph Jezak <josejx@gentoo.org> | 
 |  *                          Larry Finger <Larry.Finger@lwfinger.net> | 
 |  *                          Danny van Dyk <kugelfang@gentoo.org> | 
 |  *                          Michael Buesch <mbuesch@freenet.de> | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify it | 
 |  * under the terms of version 2 of the GNU General Public License as | 
 |  * published by the Free Software Foundation. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, but WITHOUT | 
 |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
 |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
 |  * more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA | 
 |  * | 
 |  * The full GNU General Public License is included in this distribution in the | 
 |  * file called COPYING. | 
 |  */ | 
 |  | 
 | #include "ieee80211softmac_priv.h" | 
 |  | 
 | #include <net/iw_handler.h> | 
 | /* for is_broadcast_ether_addr and is_zero_ether_addr */ | 
 | #include <linux/etherdevice.h> | 
 |  | 
 | int | 
 | ieee80211softmac_wx_trigger_scan(struct net_device *net_dev, | 
 | 				 struct iw_request_info *info, | 
 | 				 union iwreq_data *data, | 
 | 				 char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); | 
 | 	return ieee80211softmac_start_scan(sm); | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan); | 
 |  | 
 |  | 
 | /* if we're still scanning, return -EAGAIN so that userspace tools | 
 |  * can get the complete scan results, otherwise return 0. */ | 
 | int | 
 | ieee80211softmac_wx_get_scan_results(struct net_device *net_dev, | 
 | 				     struct iw_request_info *info, | 
 | 				     union iwreq_data *data, | 
 | 				     char *extra) | 
 | { | 
 | 	unsigned long flags; | 
 | 	struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); | 
 |  | 
 | 	spin_lock_irqsave(&sm->lock, flags); | 
 | 	if (sm->scanning) { | 
 | 		spin_unlock_irqrestore(&sm->lock, flags); | 
 | 		return -EAGAIN; | 
 | 	} | 
 | 	spin_unlock_irqrestore(&sm->lock, flags); | 
 | 	return ieee80211_wx_get_scan(sm->ieee, info, data, extra); | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_set_essid(struct net_device *net_dev, | 
 | 			      struct iw_request_info *info, | 
 | 			      union iwreq_data *data, | 
 | 			      char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); | 
 | 	struct ieee80211softmac_auth_queue_item *authptr; | 
 | 	int length = 0; | 
 | 	DECLARE_MAC_BUF(mac); | 
 |  | 
 | check_assoc_again: | 
 | 	mutex_lock(&sm->associnfo.mutex); | 
 | 	if((sm->associnfo.associating || sm->associnfo.associated) && | 
 | 	   (data->essid.flags && data->essid.length)) { | 
 | 		dprintk(KERN_INFO PFX "Canceling existing associate request!\n"); | 
 | 		/* Cancel assoc work */ | 
 | 		cancel_delayed_work(&sm->associnfo.work); | 
 | 		/* We don't have to do this, but it's a little cleaner */ | 
 | 		list_for_each_entry(authptr, &sm->auth_queue, list) | 
 | 			cancel_delayed_work(&authptr->work); | 
 | 		sm->associnfo.bssvalid = 0; | 
 | 		sm->associnfo.bssfixed = 0; | 
 | 		sm->associnfo.associating = 0; | 
 | 		sm->associnfo.associated = 0; | 
 | 		/* We must unlock to avoid deadlocks with the assoc workqueue | 
 | 		 * on the associnfo.mutex */ | 
 | 		mutex_unlock(&sm->associnfo.mutex); | 
 | 		flush_workqueue(sm->wq); | 
 | 		/* Avoid race! Check assoc status again. Maybe someone started an | 
 | 		 * association while we flushed. */ | 
 | 		goto check_assoc_again; | 
 | 	} | 
 |  | 
 | 	sm->associnfo.static_essid = 0; | 
 | 	sm->associnfo.assoc_wait = 0; | 
 |  | 
 | 	if (data->essid.flags && data->essid.length) { | 
 | 		length = min((int)data->essid.length, IW_ESSID_MAX_SIZE); | 
 | 		if (length) { | 
 | 			memcpy(sm->associnfo.req_essid.data, extra, length); | 
 | 			sm->associnfo.static_essid = 1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* set our requested ESSID length. | 
 | 	 * If applicable, we have already copied the data in */ | 
 | 	sm->associnfo.req_essid.len = length; | 
 |  | 
 | 	sm->associnfo.associating = 1; | 
 | 	/* queue lower level code to do work (if necessary) */ | 
 | 	queue_delayed_work(sm->wq, &sm->associnfo.work, 0); | 
 |  | 
 | 	mutex_unlock(&sm->associnfo.mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_get_essid(struct net_device *net_dev, | 
 | 			      struct iw_request_info *info, | 
 | 			      union iwreq_data *data, | 
 | 			      char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); | 
 |  | 
 | 	mutex_lock(&sm->associnfo.mutex); | 
 | 	/* If all fails, return ANY (empty) */ | 
 | 	data->essid.length = 0; | 
 | 	data->essid.flags = 0;  /* active */ | 
 |  | 
 | 	/* If we have a statically configured ESSID then return it */ | 
 | 	if (sm->associnfo.static_essid) { | 
 | 		data->essid.length = sm->associnfo.req_essid.len; | 
 | 		data->essid.flags = 1;  /* active */ | 
 | 		memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len); | 
 | 		dprintk(KERN_INFO PFX "Getting essid from req_essid\n"); | 
 | 	} else if (sm->associnfo.associated || sm->associnfo.associating) { | 
 | 	/* If we're associating/associated, return that */ | 
 | 		data->essid.length = sm->associnfo.associate_essid.len; | 
 | 		data->essid.flags = 1;  /* active */ | 
 | 		memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len); | 
 | 		dprintk(KERN_INFO PFX "Getting essid from associate_essid\n"); | 
 | 	} | 
 | 	mutex_unlock(&sm->associnfo.mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_set_rate(struct net_device *net_dev, | 
 | 			     struct iw_request_info *info, | 
 | 			     union iwreq_data *data, | 
 | 			     char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | 
 | 	struct ieee80211_device *ieee = mac->ieee; | 
 | 	unsigned long flags; | 
 | 	s32 in_rate = data->bitrate.value; | 
 | 	u8 rate; | 
 | 	int is_ofdm = 0; | 
 | 	int err = -EINVAL; | 
 |  | 
 | 	if (in_rate == -1) { | 
 | 		if (ieee->modulation & IEEE80211_OFDM_MODULATION) | 
 | 			in_rate = 24000000; | 
 | 		else | 
 | 			in_rate = 11000000; | 
 | 	} | 
 |  | 
 | 	switch (in_rate) { | 
 | 	case 1000000: | 
 | 		rate = IEEE80211_CCK_RATE_1MB; | 
 | 		break; | 
 | 	case 2000000: | 
 | 		rate = IEEE80211_CCK_RATE_2MB; | 
 | 		break; | 
 | 	case 5500000: | 
 | 		rate = IEEE80211_CCK_RATE_5MB; | 
 | 		break; | 
 | 	case 11000000: | 
 | 		rate = IEEE80211_CCK_RATE_11MB; | 
 | 		break; | 
 | 	case 6000000: | 
 | 		rate = IEEE80211_OFDM_RATE_6MB; | 
 | 		is_ofdm = 1; | 
 | 		break; | 
 | 	case 9000000: | 
 | 		rate = IEEE80211_OFDM_RATE_9MB; | 
 | 		is_ofdm = 1; | 
 | 		break; | 
 | 	case 12000000: | 
 | 		rate = IEEE80211_OFDM_RATE_12MB; | 
 | 		is_ofdm = 1; | 
 | 		break; | 
 | 	case 18000000: | 
 | 		rate = IEEE80211_OFDM_RATE_18MB; | 
 | 		is_ofdm = 1; | 
 | 		break; | 
 | 	case 24000000: | 
 | 		rate = IEEE80211_OFDM_RATE_24MB; | 
 | 		is_ofdm = 1; | 
 | 		break; | 
 | 	case 36000000: | 
 | 		rate = IEEE80211_OFDM_RATE_36MB; | 
 | 		is_ofdm = 1; | 
 | 		break; | 
 | 	case 48000000: | 
 | 		rate = IEEE80211_OFDM_RATE_48MB; | 
 | 		is_ofdm = 1; | 
 | 		break; | 
 | 	case 54000000: | 
 | 		rate = IEEE80211_OFDM_RATE_54MB; | 
 | 		is_ofdm = 1; | 
 | 		break; | 
 | 	default: | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	spin_lock_irqsave(&mac->lock, flags); | 
 |  | 
 | 	/* Check if correct modulation for this PHY. */ | 
 | 	if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION)) | 
 | 		goto out_unlock; | 
 |  | 
 | 	mac->txrates.user_rate = rate; | 
 | 	ieee80211softmac_recalc_txrates(mac); | 
 | 	err = 0; | 
 |  | 
 | out_unlock: | 
 | 	spin_unlock_irqrestore(&mac->lock, flags); | 
 | out: | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_get_rate(struct net_device *net_dev, | 
 | 			     struct iw_request_info *info, | 
 | 			     union iwreq_data *data, | 
 | 			     char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | 
 | 	unsigned long flags; | 
 | 	int err = -EINVAL; | 
 |  | 
 | 	spin_lock_irqsave(&mac->lock, flags); | 
 |  | 
 | 	if (unlikely(!mac->running)) { | 
 | 		err = -ENODEV; | 
 | 		goto out_unlock; | 
 | 	} | 
 |  | 
 | 	switch (mac->txrates.default_rate) { | 
 | 	case IEEE80211_CCK_RATE_1MB: | 
 | 		data->bitrate.value = 1000000; | 
 | 		break; | 
 | 	case IEEE80211_CCK_RATE_2MB: | 
 | 		data->bitrate.value = 2000000; | 
 | 		break; | 
 | 	case IEEE80211_CCK_RATE_5MB: | 
 | 		data->bitrate.value = 5500000; | 
 | 		break; | 
 | 	case IEEE80211_CCK_RATE_11MB: | 
 | 		data->bitrate.value = 11000000; | 
 | 		break; | 
 | 	case IEEE80211_OFDM_RATE_6MB: | 
 | 		data->bitrate.value = 6000000; | 
 | 		break; | 
 | 	case IEEE80211_OFDM_RATE_9MB: | 
 | 		data->bitrate.value = 9000000; | 
 | 		break; | 
 | 	case IEEE80211_OFDM_RATE_12MB: | 
 | 		data->bitrate.value = 12000000; | 
 | 		break; | 
 | 	case IEEE80211_OFDM_RATE_18MB: | 
 | 		data->bitrate.value = 18000000; | 
 | 		break; | 
 | 	case IEEE80211_OFDM_RATE_24MB: | 
 | 		data->bitrate.value = 24000000; | 
 | 		break; | 
 | 	case IEEE80211_OFDM_RATE_36MB: | 
 | 		data->bitrate.value = 36000000; | 
 | 		break; | 
 | 	case IEEE80211_OFDM_RATE_48MB: | 
 | 		data->bitrate.value = 48000000; | 
 | 		break; | 
 | 	case IEEE80211_OFDM_RATE_54MB: | 
 | 		data->bitrate.value = 54000000; | 
 | 		break; | 
 | 	default: | 
 | 		assert(0); | 
 | 		goto out_unlock; | 
 | 	} | 
 | 	err = 0; | 
 | out_unlock: | 
 | 	spin_unlock_irqrestore(&mac->lock, flags); | 
 |  | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_get_wap(struct net_device *net_dev, | 
 | 			    struct iw_request_info *info, | 
 | 			    union iwreq_data *data, | 
 | 			    char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | 
 | 	int err = 0; | 
 |  | 
 | 	mutex_lock(&mac->associnfo.mutex); | 
 | 	if (mac->associnfo.bssvalid) | 
 | 		memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN); | 
 | 	else | 
 | 		memset(data->ap_addr.sa_data, 0xff, ETH_ALEN); | 
 | 	data->ap_addr.sa_family = ARPHRD_ETHER; | 
 | 	mutex_unlock(&mac->associnfo.mutex); | 
 |  | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_set_wap(struct net_device *net_dev, | 
 | 			    struct iw_request_info *info, | 
 | 			    union iwreq_data *data, | 
 | 			    char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); | 
 |  | 
 | 	/* sanity check */ | 
 | 	if (data->ap_addr.sa_family != ARPHRD_ETHER) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	mutex_lock(&mac->associnfo.mutex); | 
 | 	if (is_broadcast_ether_addr(data->ap_addr.sa_data)) { | 
 | 		/* the bssid we have is not to be fixed any longer, | 
 | 		 * and we should reassociate to the best AP. */ | 
 | 		mac->associnfo.bssfixed = 0; | 
 | 		/* force reassociation */ | 
 | 		mac->associnfo.bssvalid = 0; | 
 | 		if (mac->associnfo.associated) | 
 | 			queue_delayed_work(mac->wq, &mac->associnfo.work, 0); | 
 | 	} else if (is_zero_ether_addr(data->ap_addr.sa_data)) { | 
 | 		/* the bssid we have is no longer fixed */ | 
 | 		mac->associnfo.bssfixed = 0; | 
 | 	} else { | 
 | 		if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) { | 
 | 			if (mac->associnfo.associating || mac->associnfo.associated) { | 
 | 			/* bssid unchanged and associated or associating - just return */ | 
 | 				goto out; | 
 | 			} | 
 | 		} else { | 
 | 			/* copy new value in data->ap_addr.sa_data to bssid */ | 
 | 			memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN); | 
 | 		} | 
 | 		/* tell the other code that this bssid should be used no matter what */ | 
 | 		mac->associnfo.bssfixed = 1; | 
 | 		/* queue associate if new bssid or (old one again and not associated) */ | 
 | 		queue_delayed_work(mac->wq, &mac->associnfo.work, 0); | 
 | 	} | 
 |  | 
 |  out: | 
 | 	mutex_unlock(&mac->associnfo.mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_set_genie(struct net_device *dev, | 
 | 			      struct iw_request_info *info, | 
 | 			      union iwreq_data *wrqu, | 
 | 			      char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *mac = ieee80211_priv(dev); | 
 | 	unsigned long flags; | 
 | 	int err = 0; | 
 | 	char *buf; | 
 | 	int i; | 
 |  | 
 | 	mutex_lock(&mac->associnfo.mutex); | 
 | 	spin_lock_irqsave(&mac->lock, flags); | 
 | 	/* bleh. shouldn't be locked for that kmalloc... */ | 
 |  | 
 | 	if (wrqu->data.length) { | 
 | 		if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) { | 
 | 			/* this is an IE, so the length must be | 
 | 			 * correct. Is it possible though that | 
 | 			 * more than one IE is passed in? | 
 | 			 */ | 
 | 			err = -EINVAL; | 
 | 			goto out; | 
 | 		} | 
 | 		if (mac->wpa.IEbuflen <= wrqu->data.length) { | 
 | 			buf = kmalloc(wrqu->data.length, GFP_ATOMIC); | 
 | 			if (!buf) { | 
 | 				err = -ENOMEM; | 
 | 				goto out; | 
 | 			} | 
 | 			kfree(mac->wpa.IE); | 
 | 			mac->wpa.IE = buf; | 
 | 			mac->wpa.IEbuflen = wrqu->data.length; | 
 | 		} | 
 | 		memcpy(mac->wpa.IE, extra, wrqu->data.length); | 
 | 		dprintk(KERN_INFO PFX "generic IE set to "); | 
 | 		for (i=0;i<wrqu->data.length;i++) | 
 | 			dprintk("%.2x", (u8)mac->wpa.IE[i]); | 
 | 		dprintk("\n"); | 
 | 		mac->wpa.IElen = wrqu->data.length; | 
 | 	} else { | 
 | 		kfree(mac->wpa.IE); | 
 | 		mac->wpa.IE = NULL; | 
 | 		mac->wpa.IElen = 0; | 
 | 		mac->wpa.IEbuflen = 0; | 
 | 	} | 
 |  | 
 |  out: | 
 | 	spin_unlock_irqrestore(&mac->lock, flags); | 
 | 	mutex_unlock(&mac->associnfo.mutex); | 
 |  | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_get_genie(struct net_device *dev, | 
 | 			      struct iw_request_info *info, | 
 | 			      union iwreq_data *wrqu, | 
 | 			      char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *mac = ieee80211_priv(dev); | 
 | 	unsigned long flags; | 
 | 	int err = 0; | 
 | 	int space = wrqu->data.length; | 
 |  | 
 | 	mutex_lock(&mac->associnfo.mutex); | 
 | 	spin_lock_irqsave(&mac->lock, flags); | 
 |  | 
 | 	wrqu->data.length = 0; | 
 |  | 
 | 	if (mac->wpa.IE && mac->wpa.IElen) { | 
 | 		wrqu->data.length = mac->wpa.IElen; | 
 | 		if (mac->wpa.IElen <= space) | 
 | 			memcpy(extra, mac->wpa.IE, mac->wpa.IElen); | 
 | 		else | 
 | 			err = -E2BIG; | 
 | 	} | 
 | 	spin_unlock_irqrestore(&mac->lock, flags); | 
 | 	mutex_unlock(&mac->associnfo.mutex); | 
 |  | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie); | 
 |  | 
 | int | 
 | ieee80211softmac_wx_set_mlme(struct net_device *dev, | 
 | 			     struct iw_request_info *info, | 
 | 			     union iwreq_data *wrqu, | 
 | 			     char *extra) | 
 | { | 
 | 	struct ieee80211softmac_device *mac = ieee80211_priv(dev); | 
 | 	struct iw_mlme *mlme = (struct iw_mlme *)extra; | 
 | 	u16 reason = cpu_to_le16(mlme->reason_code); | 
 | 	struct ieee80211softmac_network *net; | 
 | 	int err = -EINVAL; | 
 |  | 
 | 	mutex_lock(&mac->associnfo.mutex); | 
 |  | 
 | 	if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) { | 
 | 		printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n"); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	switch (mlme->cmd) { | 
 | 	case IW_MLME_DEAUTH: | 
 | 		net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data); | 
 | 		if (!net) { | 
 | 			printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n"); | 
 | 			goto out; | 
 | 		} | 
 | 		err =  ieee80211softmac_deauth_req(mac, net, reason); | 
 | 		goto out; | 
 | 	case IW_MLME_DISASSOC: | 
 | 		ieee80211softmac_send_disassoc_req(mac, reason); | 
 | 		mac->associnfo.associated = 0; | 
 | 		mac->associnfo.associating = 0; | 
 | 		err = 0; | 
 | 		goto out; | 
 | 	default: | 
 | 		err = -EOPNOTSUPP; | 
 | 	} | 
 |  | 
 | out: | 
 | 	mutex_unlock(&mac->associnfo.mutex); | 
 |  | 
 | 	return err; | 
 | } | 
 | EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme); |