blob: 17d30f4e7fe74a22bfe04f0ed0ec6d7d65123015 [file] [log] [blame]
/*
* This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them
*
* Copyright (c) 2005 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>
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);
int
ieee80211softmac_wx_get_scan_results(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 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);
int length = 0;
unsigned long flags;
spin_lock_irqsave(&sm->lock, flags);
sm->associnfo.static_essid = 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;
}
}
sm->associnfo.scan_retry = IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT;
/* 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) {
/* automatic detect */
if (ieee->modulation & IEEE80211_OFDM_MODULATION)
in_rate = 54000000;
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.default_rate = rate;
mac->txrates.default_fallback = lower_rate(mac, rate);
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);
static const unsigned char any[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static const unsigned char off[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned long flags;
/* sanity check */
if (data->ap_addr.sa_family != ARPHRD_ETHER) {
return -EINVAL;
}
spin_lock_irqsave(&mac->lock, flags);
if (!memcmp(any, data->ap_addr.sa_data, ETH_ALEN) ||
!memcmp(off, data->ap_addr.sa_data, ETH_ALEN)) {
schedule_work(&mac->associnfo.work);
goto out;
} 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);
}
/* 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", 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);