fc8fd3400b
The current version of bcm43xx-softmac uses local routines to check if a channel is valid. As noted in the comments, these routines do not take any regulatory information into account. This patch converts the code to use the equivalent routine in ieee80211, which is being converted to know about regulatory information. Signed-Off-By: Larry Finger <Larry.Finger@lwfinger.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
1003 lines
26 KiB
C
1003 lines
26 KiB
C
/*
|
|
|
|
Broadcom BCM43xx wireless driver
|
|
|
|
Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
|
|
Stefano Brivio <st3@riseup.net>
|
|
Michael Buesch <mbuesch@freenet.de>
|
|
Danny van Dyk <kugelfang@gentoo.org>
|
|
Andreas Jaggi <andreas.jaggi@waterwave.ch>
|
|
|
|
Some parts of the code in this file are derived from the ipw2200
|
|
driver Copyright(c) 2003 - 2004 Intel Corporation.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
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; see the file COPYING. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#include <linux/wireless.h>
|
|
#include <net/iw_handler.h>
|
|
#include <net/ieee80211softmac.h>
|
|
#include <net/ieee80211softmac_wx.h>
|
|
#include <linux/capability.h>
|
|
#include <linux/sched.h> /* for capable() */
|
|
#include <linux/delay.h>
|
|
|
|
#include "bcm43xx.h"
|
|
#include "bcm43xx_wx.h"
|
|
#include "bcm43xx_main.h"
|
|
#include "bcm43xx_radio.h"
|
|
#include "bcm43xx_phy.h"
|
|
|
|
|
|
/* The WIRELESS_EXT version, which is implemented by this driver. */
|
|
#define BCM43xx_WX_VERSION 18
|
|
|
|
#define MAX_WX_STRING 80
|
|
|
|
|
|
static int bcm43xx_wx_get_name(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
int i;
|
|
unsigned long flags;
|
|
struct bcm43xx_phyinfo *phy;
|
|
char suffix[7] = { 0 };
|
|
int have_a = 0, have_b = 0, have_g = 0;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
for (i = 0; i < bcm->nr_80211_available; i++) {
|
|
phy = &(bcm->core_80211_ext[i].phy);
|
|
switch (phy->type) {
|
|
case BCM43xx_PHYTYPE_A:
|
|
have_a = 1;
|
|
break;
|
|
case BCM43xx_PHYTYPE_G:
|
|
have_g = 1;
|
|
case BCM43xx_PHYTYPE_B:
|
|
have_b = 1;
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
i = 0;
|
|
if (have_a) {
|
|
suffix[i++] = 'a';
|
|
suffix[i++] = '/';
|
|
}
|
|
if (have_b) {
|
|
suffix[i++] = 'b';
|
|
suffix[i++] = '/';
|
|
}
|
|
if (have_g) {
|
|
suffix[i++] = 'g';
|
|
suffix[i++] = '/';
|
|
}
|
|
if (i != 0)
|
|
suffix[i - 1] = '\0';
|
|
|
|
snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
u8 channel;
|
|
int freq;
|
|
int err = -EINVAL;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
if ((data->freq.m >= 0) && (data->freq.m <= 1000)) {
|
|
channel = data->freq.m;
|
|
freq = bcm43xx_channel_to_freq(bcm, channel);
|
|
} else {
|
|
channel = bcm43xx_freq_to_channel(bcm, data->freq.m);
|
|
freq = data->freq.m;
|
|
}
|
|
if (!ieee80211_is_valid_channel(bcm->ieee, channel))
|
|
goto out_unlock;
|
|
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
|
|
//ieee80211softmac_disassoc(softmac, $REASON);
|
|
bcm43xx_mac_suspend(bcm);
|
|
err = bcm43xx_radio_selectchannel(bcm, channel, 0);
|
|
bcm43xx_mac_enable(bcm);
|
|
} else {
|
|
bcm43xx_current_radio(bcm)->initial_channel = channel;
|
|
err = 0;
|
|
}
|
|
out_unlock:
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
struct bcm43xx_radioinfo *radio;
|
|
unsigned long flags;
|
|
int err = -ENODEV;
|
|
u16 channel;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
radio = bcm43xx_current_radio(bcm);
|
|
channel = radio->channel;
|
|
if (channel == 0xFF) {
|
|
channel = radio->initial_channel;
|
|
if (channel == 0xFF)
|
|
goto out_unlock;
|
|
}
|
|
assert(channel > 0 && channel <= 1000);
|
|
data->freq.e = 1;
|
|
data->freq.m = bcm43xx_channel_to_freq(bcm, channel) * 100000;
|
|
data->freq.flags = 1;
|
|
|
|
err = 0;
|
|
out_unlock:
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_mode(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int mode;
|
|
|
|
mode = data->mode;
|
|
if (mode == IW_MODE_AUTO)
|
|
mode = BCM43xx_INITIAL_IWMODE;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
|
|
if (bcm->ieee->iw_mode != mode)
|
|
bcm43xx_set_iwmode(bcm, mode);
|
|
} else
|
|
bcm->ieee->iw_mode = mode;
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_mode(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
data->mode = bcm->ieee->iw_mode;
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
struct iw_range *range = (struct iw_range *)extra;
|
|
const struct ieee80211_geo *geo;
|
|
unsigned long flags;
|
|
int i, j;
|
|
struct bcm43xx_phyinfo *phy;
|
|
|
|
data->data.length = sizeof(*range);
|
|
memset(range, 0, sizeof(*range));
|
|
|
|
//TODO: What about 802.11b?
|
|
/* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
|
|
range->throughput = 27 * 1000 * 1000;
|
|
|
|
range->max_qual.qual = 100;
|
|
/* TODO: Real max RSSI */
|
|
range->max_qual.level = 3;
|
|
range->max_qual.noise = 100;
|
|
range->max_qual.updated = 7;
|
|
|
|
range->avg_qual.qual = 70;
|
|
range->avg_qual.level = 2;
|
|
range->avg_qual.noise = 40;
|
|
range->avg_qual.updated = 7;
|
|
|
|
range->min_rts = BCM43xx_MIN_RTS_THRESHOLD;
|
|
range->max_rts = BCM43xx_MAX_RTS_THRESHOLD;
|
|
range->min_frag = MIN_FRAG_THRESHOLD;
|
|
range->max_frag = MAX_FRAG_THRESHOLD;
|
|
|
|
range->encoding_size[0] = 5;
|
|
range->encoding_size[1] = 13;
|
|
range->num_encoding_sizes = 2;
|
|
range->max_encoding_tokens = WEP_KEYS;
|
|
|
|
range->we_version_compiled = WIRELESS_EXT;
|
|
range->we_version_source = BCM43xx_WX_VERSION;
|
|
|
|
range->enc_capa = IW_ENC_CAPA_WPA |
|
|
IW_ENC_CAPA_WPA2 |
|
|
IW_ENC_CAPA_CIPHER_TKIP |
|
|
IW_ENC_CAPA_CIPHER_CCMP;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
phy = bcm43xx_current_phy(bcm);
|
|
|
|
range->num_bitrates = 0;
|
|
i = 0;
|
|
if (phy->type == BCM43xx_PHYTYPE_A ||
|
|
phy->type == BCM43xx_PHYTYPE_G) {
|
|
range->num_bitrates = 8;
|
|
range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB;
|
|
range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB;
|
|
range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB;
|
|
range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB;
|
|
range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB;
|
|
range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB;
|
|
range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB;
|
|
range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB;
|
|
}
|
|
if (phy->type == BCM43xx_PHYTYPE_B ||
|
|
phy->type == BCM43xx_PHYTYPE_G) {
|
|
range->num_bitrates += 4;
|
|
range->bitrate[i++] = IEEE80211_CCK_RATE_1MB;
|
|
range->bitrate[i++] = IEEE80211_CCK_RATE_2MB;
|
|
range->bitrate[i++] = IEEE80211_CCK_RATE_5MB;
|
|
range->bitrate[i++] = IEEE80211_CCK_RATE_11MB;
|
|
}
|
|
|
|
geo = ieee80211_get_geo(bcm->ieee);
|
|
range->num_channels = geo->a_channels + geo->bg_channels;
|
|
j = 0;
|
|
for (i = 0; i < geo->a_channels; i++) {
|
|
if (j == IW_MAX_FREQUENCIES)
|
|
break;
|
|
range->freq[j].i = j + 1;
|
|
range->freq[j].m = geo->a[i].freq;//FIXME?
|
|
range->freq[j].e = 1;
|
|
j++;
|
|
}
|
|
for (i = 0; i < geo->bg_channels; i++) {
|
|
if (j == IW_MAX_FREQUENCIES)
|
|
break;
|
|
range->freq[j].i = j + 1;
|
|
range->freq[j].m = geo->bg[i].freq;//FIXME?
|
|
range->freq[j].e = 1;
|
|
j++;
|
|
}
|
|
range->num_frequency = j;
|
|
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_nick(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
size_t len;
|
|
|
|
bcm43xx_lock_noirq(bcm);
|
|
len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
|
|
memcpy(bcm->nick, extra, len);
|
|
bcm->nick[len] = '\0';
|
|
bcm43xx_unlock_noirq(bcm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_nick(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
size_t len;
|
|
|
|
bcm43xx_lock_noirq(bcm);
|
|
len = strlen(bcm->nick) + 1;
|
|
memcpy(extra, bcm->nick, len);
|
|
data->data.length = (__u16)len;
|
|
data->data.flags = 1;
|
|
bcm43xx_unlock_noirq(bcm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_rts(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int err = -EINVAL;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
if (data->rts.disabled) {
|
|
bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD;
|
|
err = 0;
|
|
} else {
|
|
if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD &&
|
|
data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) {
|
|
bcm->rts_threshold = data->rts.value;
|
|
err = 0;
|
|
}
|
|
}
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_rts(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
data->rts.value = bcm->rts_threshold;
|
|
data->rts.fixed = 0;
|
|
data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD);
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_frag(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int err = -EINVAL;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
if (data->frag.disabled) {
|
|
bcm->ieee->fts = MAX_FRAG_THRESHOLD;
|
|
err = 0;
|
|
} else {
|
|
if (data->frag.value >= MIN_FRAG_THRESHOLD &&
|
|
data->frag.value <= MAX_FRAG_THRESHOLD) {
|
|
bcm->ieee->fts = data->frag.value & ~0x1;
|
|
err = 0;
|
|
}
|
|
}
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_frag(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
data->frag.value = bcm->ieee->fts;
|
|
data->frag.fixed = 0;
|
|
data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD);
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
struct bcm43xx_radioinfo *radio;
|
|
struct bcm43xx_phyinfo *phy;
|
|
unsigned long flags;
|
|
int err = -ENODEV;
|
|
u16 maxpower;
|
|
|
|
if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
|
|
printk(PFX KERN_ERR "TX power not in dBm.\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)
|
|
goto out_unlock;
|
|
radio = bcm43xx_current_radio(bcm);
|
|
phy = bcm43xx_current_phy(bcm);
|
|
if (data->txpower.disabled != (!(radio->enabled))) {
|
|
if (data->txpower.disabled)
|
|
bcm43xx_radio_turn_off(bcm);
|
|
else
|
|
bcm43xx_radio_turn_on(bcm);
|
|
}
|
|
if (data->txpower.value > 0) {
|
|
/* desired and maxpower dBm values are in Q5.2 */
|
|
if (phy->type == BCM43xx_PHYTYPE_A)
|
|
maxpower = bcm->sprom.maxpower_aphy;
|
|
else
|
|
maxpower = bcm->sprom.maxpower_bgphy;
|
|
radio->txpower_desired = limit_value(data->txpower.value << 2,
|
|
0, maxpower);
|
|
bcm43xx_phy_xmitpower(bcm);
|
|
}
|
|
err = 0;
|
|
|
|
out_unlock:
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
struct bcm43xx_radioinfo *radio;
|
|
unsigned long flags;
|
|
int err = -ENODEV;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)
|
|
goto out_unlock;
|
|
radio = bcm43xx_current_radio(bcm);
|
|
/* desired dBm value is in Q5.2 */
|
|
data->txpower.value = radio->txpower_desired >> 2;
|
|
data->txpower.fixed = 1;
|
|
data->txpower.flags = IW_TXPOW_DBM;
|
|
data->txpower.disabled = !(radio->enabled);
|
|
|
|
err = 0;
|
|
out_unlock:
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_encoding(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
int err;
|
|
|
|
err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_encodingext(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
int err;
|
|
|
|
err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_encoding(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
int err;
|
|
|
|
err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_encodingext(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
int err;
|
|
|
|
err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_interfmode(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int mode, err = 0;
|
|
|
|
mode = *((int *)extra);
|
|
switch (mode) {
|
|
case 0:
|
|
mode = BCM43xx_RADIO_INTERFMODE_NONE;
|
|
break;
|
|
case 1:
|
|
mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
|
|
break;
|
|
case 2:
|
|
mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
|
|
break;
|
|
case 3:
|
|
mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
|
|
break;
|
|
default:
|
|
printk(KERN_ERR PFX "set_interfmode allowed parameters are: "
|
|
"0 => None, 1 => Non-WLAN, 2 => WLAN, "
|
|
"3 => Auto-WLAN\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
|
|
err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
|
|
if (err) {
|
|
printk(KERN_ERR PFX "Interference Mitigation not "
|
|
"supported by device\n");
|
|
}
|
|
} else {
|
|
if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) {
|
|
printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN "
|
|
"not supported while the interface is down.\n");
|
|
err = -ENODEV;
|
|
} else
|
|
bcm43xx_current_radio(bcm)->interfmode = mode;
|
|
}
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_interfmode(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int mode;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
mode = bcm43xx_current_radio(bcm)->interfmode;
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
switch (mode) {
|
|
case BCM43xx_RADIO_INTERFMODE_NONE:
|
|
strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING);
|
|
break;
|
|
case BCM43xx_RADIO_INTERFMODE_NONWLAN:
|
|
strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING);
|
|
break;
|
|
case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
|
|
strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
data->data.length = strlen(extra) + 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int on;
|
|
|
|
on = *((int *)extra);
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
bcm->short_preamble = !!on;
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int on;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
on = bcm->short_preamble;
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
if (on)
|
|
strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING);
|
|
else
|
|
strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING);
|
|
data->data.length = strlen(extra) + 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_set_swencryption(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int on;
|
|
|
|
on = *((int *)extra);
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
bcm->ieee->host_encrypt = !!on;
|
|
bcm->ieee->host_decrypt = !!on;
|
|
bcm->ieee->host_build_iv = !on;
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_get_swencryption(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
unsigned long flags;
|
|
int on;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
on = bcm->ieee->host_encrypt;
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
|
|
if (on)
|
|
strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
|
|
else
|
|
strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
|
|
data->data.length = strlen(extra + 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Enough buffer to hold a hexdump of the sprom data. */
|
|
#define SPROM_BUFFERSIZE 512
|
|
|
|
static int sprom2hex(const u16 *sprom, char *dump)
|
|
{
|
|
int i, pos = 0;
|
|
|
|
for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
|
|
pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1,
|
|
"%04X", swab16(sprom[i]) & 0xFFFF);
|
|
}
|
|
|
|
return pos + 1;
|
|
}
|
|
|
|
static int hex2sprom(u16 *sprom, const char *dump, unsigned int len)
|
|
{
|
|
char tmp[5] = { 0 };
|
|
int cnt = 0;
|
|
unsigned long parsed;
|
|
|
|
if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
|
|
return -EINVAL;
|
|
while (cnt < BCM43xx_SPROM_SIZE) {
|
|
memcpy(tmp, dump, 4);
|
|
dump += 4;
|
|
parsed = simple_strtoul(tmp, NULL, 16);
|
|
sprom[cnt++] = swab16((u16)parsed);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bcm43xx_wx_sprom_read(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
int err = -EPERM;
|
|
u16 *sprom;
|
|
unsigned long flags;
|
|
|
|
if (!capable(CAP_SYS_RAWIO))
|
|
goto out;
|
|
|
|
err = -ENOMEM;
|
|
sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
|
|
GFP_KERNEL);
|
|
if (!sprom)
|
|
goto out;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
err = -ENODEV;
|
|
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
|
|
err = bcm43xx_sprom_read(bcm, sprom);
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
if (!err)
|
|
data->data.length = sprom2hex(sprom, extra);
|
|
kfree(sprom);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static int bcm43xx_wx_sprom_write(struct net_device *net_dev,
|
|
struct iw_request_info *info,
|
|
union iwreq_data *data,
|
|
char *extra)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
int err = -EPERM;
|
|
u16 *sprom;
|
|
unsigned long flags;
|
|
char *input;
|
|
unsigned int len;
|
|
|
|
if (!capable(CAP_SYS_RAWIO))
|
|
goto out;
|
|
|
|
err = -ENOMEM;
|
|
sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
|
|
GFP_KERNEL);
|
|
if (!sprom)
|
|
goto out;
|
|
|
|
len = data->data.length;
|
|
extra[len - 1] = '\0';
|
|
input = strchr(extra, ':');
|
|
if (input) {
|
|
input++;
|
|
len -= input - extra;
|
|
} else
|
|
input = extra;
|
|
err = hex2sprom(sprom, input, len);
|
|
if (err)
|
|
goto out_kfree;
|
|
|
|
bcm43xx_lock_irqsafe(bcm, flags);
|
|
err = -ENODEV;
|
|
if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
|
|
err = bcm43xx_sprom_write(bcm, sprom);
|
|
bcm43xx_unlock_irqsafe(bcm, flags);
|
|
out_kfree:
|
|
kfree(sprom);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */
|
|
|
|
static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev)
|
|
{
|
|
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
|
|
struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
|
|
struct iw_statistics *wstats;
|
|
|
|
wstats = &bcm->stats.wstats;
|
|
if (!mac->associated) {
|
|
wstats->miss.beacon = 0;
|
|
// bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here?
|
|
wstats->discard.retries = 0;
|
|
// bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question
|
|
wstats->discard.nwid = 0;
|
|
// bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto
|
|
wstats->discard.code = 0;
|
|
// bcm->ieee->ieee_stats.rx_fragments = 0; // FIXME: same here
|
|
wstats->discard.fragment = 0;
|
|
wstats->discard.misc = 0;
|
|
wstats->qual.qual = 0;
|
|
wstats->qual.level = 0;
|
|
wstats->qual.noise = 0;
|
|
wstats->qual.updated = 7;
|
|
wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
|
|
IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
|
|
return wstats;
|
|
}
|
|
/* fill in the real statistics when iface associated */
|
|
wstats->qual.qual = 100; // TODO: get the real signal quality
|
|
wstats->qual.level = 3 - bcm->stats.link_quality;
|
|
wstats->qual.noise = bcm->stats.noise;
|
|
wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
|
|
IW_QUAL_NOISE_UPDATED;
|
|
wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable;
|
|
wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded;
|
|
wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa;
|
|
wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments;
|
|
wstats->discard.misc = 0; // FIXME
|
|
wstats->miss.beacon = 0; // FIXME
|
|
return wstats;
|
|
}
|
|
|
|
|
|
#ifdef WX
|
|
# undef WX
|
|
#endif
|
|
#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT]
|
|
static const iw_handler bcm43xx_wx_handlers[] = {
|
|
/* Wireless Identification */
|
|
WX(SIOCGIWNAME) = bcm43xx_wx_get_name,
|
|
/* Basic operations */
|
|
WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq,
|
|
WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq,
|
|
WX(SIOCSIWMODE) = bcm43xx_wx_set_mode,
|
|
WX(SIOCGIWMODE) = bcm43xx_wx_get_mode,
|
|
/* Informative stuff */
|
|
WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams,
|
|
/* Access Point manipulation */
|
|
WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,
|
|
WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,
|
|
WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,
|
|
WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,
|
|
/* 802.11 specific support */
|
|
WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,
|
|
WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,
|
|
WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick,
|
|
WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick,
|
|
/* Other parameters */
|
|
WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate,
|
|
WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,
|
|
WX(SIOCSIWRTS) = bcm43xx_wx_set_rts,
|
|
WX(SIOCGIWRTS) = bcm43xx_wx_get_rts,
|
|
WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag,
|
|
WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag,
|
|
WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower,
|
|
WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower,
|
|
//TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry,
|
|
//TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry,
|
|
/* Encoding */
|
|
WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding,
|
|
WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding,
|
|
WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext,
|
|
WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext,
|
|
/* Power saving */
|
|
//TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power,
|
|
//TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power,
|
|
WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,
|
|
WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,
|
|
WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,
|
|
WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,
|
|
};
|
|
#undef WX
|
|
|
|
static const iw_handler bcm43xx_priv_wx_handlers[] = {
|
|
/* Set Interference Mitigation Mode. */
|
|
bcm43xx_wx_set_interfmode,
|
|
/* Get Interference Mitigation Mode. */
|
|
bcm43xx_wx_get_interfmode,
|
|
/* Enable/Disable Short Preamble mode. */
|
|
bcm43xx_wx_set_shortpreamble,
|
|
/* Get Short Preamble mode. */
|
|
bcm43xx_wx_get_shortpreamble,
|
|
/* Enable/Disable Software Encryption mode */
|
|
bcm43xx_wx_set_swencryption,
|
|
/* Get Software Encryption mode */
|
|
bcm43xx_wx_get_swencryption,
|
|
/* Write SRPROM data. */
|
|
bcm43xx_wx_sprom_write,
|
|
/* Read SPROM data. */
|
|
bcm43xx_wx_sprom_read,
|
|
};
|
|
|
|
#define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0)
|
|
#define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1)
|
|
#define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2)
|
|
#define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3)
|
|
#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4)
|
|
#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5)
|
|
#define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6)
|
|
#define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7)
|
|
|
|
#define PRIV_WX_DUMMY(ioctl) \
|
|
{ \
|
|
.cmd = (ioctl), \
|
|
.name = "__unused" \
|
|
}
|
|
|
|
static const struct iw_priv_args bcm43xx_priv_wx_args[] = {
|
|
{
|
|
.cmd = PRIV_WX_SET_INTERFMODE,
|
|
.set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
|
|
.name = "set_interfmode",
|
|
},
|
|
{
|
|
.cmd = PRIV_WX_GET_INTERFMODE,
|
|
.get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
|
|
.name = "get_interfmode",
|
|
},
|
|
{
|
|
.cmd = PRIV_WX_SET_SHORTPREAMBLE,
|
|
.set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
|
|
.name = "set_shortpreamb",
|
|
},
|
|
{
|
|
.cmd = PRIV_WX_GET_SHORTPREAMBLE,
|
|
.get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
|
|
.name = "get_shortpreamb",
|
|
},
|
|
{
|
|
.cmd = PRIV_WX_SET_SWENCRYPTION,
|
|
.set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
|
|
.name = "set_swencrypt",
|
|
},
|
|
{
|
|
.cmd = PRIV_WX_GET_SWENCRYPTION,
|
|
.get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
|
|
.name = "get_swencrypt",
|
|
},
|
|
{
|
|
.cmd = PRIV_WX_SPROM_WRITE,
|
|
.set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE,
|
|
.name = "write_sprom",
|
|
},
|
|
{
|
|
.cmd = PRIV_WX_SPROM_READ,
|
|
.get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE,
|
|
.name = "read_sprom",
|
|
},
|
|
};
|
|
|
|
const struct iw_handler_def bcm43xx_wx_handlers_def = {
|
|
.standard = bcm43xx_wx_handlers,
|
|
.num_standard = ARRAY_SIZE(bcm43xx_wx_handlers),
|
|
.num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers),
|
|
.num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args),
|
|
.private = bcm43xx_priv_wx_handlers,
|
|
.private_args = bcm43xx_priv_wx_args,
|
|
.get_wireless_stats = bcm43xx_get_wireless_stats,
|
|
};
|