498 lines
14 KiB
C
498 lines
14 KiB
C
/*
|
|
* 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;
|
|
unsigned long flags;
|
|
|
|
/* 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->associated) &&
|
|
(data->essid.flags && data->essid.length && extra)) {
|
|
/* Get the associating network */
|
|
n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid);
|
|
if(n && n->essid.len == (data->essid.length - 1) &&
|
|
!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));
|
|
return 0;
|
|
} else {
|
|
dprintk(KERN_INFO PFX "Canceling existing associate request!\n");
|
|
spin_lock_irqsave(&sm->lock,flags);
|
|
/* 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;
|
|
spin_unlock_irqrestore(&sm->lock,flags);
|
|
flush_scheduled_work();
|
|
}
|
|
}
|
|
|
|
|
|
spin_lock_irqsave(&sm->lock, flags);
|
|
|
|
sm->associnfo.static_essid = 0;
|
|
sm->associnfo.assoc_wait = 0;
|
|
|
|
if (data->essid.flags && data->essid.length && extra /*required?*/) {
|
|
length = min(data->essid.length - 1, 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;
|
|
|
|
/* queue lower level code to do work (if necessary) */
|
|
schedule_work(&sm->associnfo.work);
|
|
|
|
spin_unlock_irqrestore(&sm->lock, flags);
|
|
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);
|
|
unsigned long flags;
|
|
|
|
/* avoid getting inconsistent information */
|
|
spin_lock_irqsave(&sm->lock, flags);
|
|
/* 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->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);
|
|
}
|
|
spin_unlock_irqrestore(&sm->lock, flags);
|
|
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);
|
|
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;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
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;
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
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);
|
|
unsigned long flags;
|
|
|
|
/* sanity check */
|
|
if (data->ap_addr.sa_family != ARPHRD_ETHER) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_lock_irqsave(&mac->lock, flags);
|
|
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->associated)
|
|
schedule_work(&mac->associnfo.work);
|
|
} 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->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_work(&mac->associnfo.work);
|
|
}
|
|
|
|
out:
|
|
spin_unlock_irqrestore(&mac->lock, flags);
|
|
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;
|
|
|
|
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);
|
|
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;
|
|
|
|
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);
|
|
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;
|
|
|
|
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");
|
|
return -EINVAL;
|
|
}
|
|
|
|
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");
|
|
return -EINVAL;
|
|
}
|
|
return ieee80211softmac_deauth_req(mac, net, reason);
|
|
case IW_MLME_DISASSOC:
|
|
ieee80211softmac_send_disassoc_req(mac, reason);
|
|
return 0;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme);
|