|  | /* | 
|  | * 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_network *n; | 
|  | struct ieee80211softmac_auth_queue_item *authptr; | 
|  | int length = 0; | 
|  |  | 
|  | mutex_lock(&sm->associnfo.mutex); | 
|  |  | 
|  | /* Check if we're already associating to this or another network | 
|  | * If it's another network, cancel and start over with our new network | 
|  | * If it's our network, ignore the change, we're already doing it! | 
|  | */ | 
|  | if((sm->associnfo.associating || sm->associnfo.associated) && | 
|  | (data->essid.flags && data->essid.length)) { | 
|  | /* Get the associating network */ | 
|  | n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid); | 
|  | if(n && n->essid.len == data->essid.length && | 
|  | !memcmp(n->essid.data, extra, n->essid.len)) { | 
|  | dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n", | 
|  | MAC_ARG(sm->associnfo.bssid)); | 
|  | goto out; | 
|  | } else { | 
|  | 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; | 
|  | flush_scheduled_work(); | 
|  | sm->associnfo.associating = 0; | 
|  | sm->associnfo.associated = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | 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) */ | 
|  | schedule_delayed_work(&sm->associnfo.work, 0); | 
|  | out: | 
|  | 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); | 
|  | } | 
|  |  | 
|  | /* If we're associating/associated, return that */ | 
|  | if (sm->associnfo.associated || sm->associnfo.associating) { | 
|  | 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); | 
|  | } | 
|  | 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) { | 
|  | /* FIXME: We don't correctly handle backing down to lower | 
|  | rates, so 801.11g devices start off at 11M for now. People | 
|  | can manually change it if they really need to, but 11M is | 
|  | more reliable. Note similar logic in | 
|  | ieee80211softmac_wx_set_rate() */ | 
|  | if (ieee->modulation & IEEE80211_CCK_MODULATION) | 
|  | in_rate = 11000000; | 
|  | else | 
|  | in_rate = 54000000; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | schedule_delayed_work(&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) */ | 
|  | schedule_delayed_work(&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); |