summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-02-01 21:06:29 +1100
committerLinus Torvalds <torvalds@linux-foundation.org>2008-02-01 21:06:29 +1100
commitcec03afcb62fbbb0eaf943f6349ade61b89d7d40 (patch)
treecc80c13e373337d1c1dee9dd7269173da1f7c079 /drivers/net/wireless
parent2da53b0134ad41b91556d2d2a322cc03487a1ab7 (diff)
parent4814bdbd590e835ecec2d5e505165ec1c19796b2 (diff)
downloadblackbird-op-linux-cec03afcb62fbbb0eaf943f6349ade61b89d7d40.tar.gz
blackbird-op-linux-cec03afcb62fbbb0eaf943f6349ade61b89d7d40.zip
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (173 commits) [NETNS]: Lookup in FIB semantic hashes taking into account the namespace. [NETNS]: Add a namespace mark to fib_info. [IPV4]: fib_sync_down rework. [NETNS]: Process interface address manipulation routines in the namespace. [IPV4]: Small style cleanup of the error path in rtm_to_ifaddr. [IPV4]: Fix memory leak on error path during FIB initialization. [NETFILTER]: Ipv6-related xt_hashlimit compilation fix. [NET_SCHED]: Add flow classifier [NET_SCHED]: sch_sfq: make internal queues visible as classes [NET_SCHED]: sch_sfq: add support for external classifiers [NET_SCHED]: Constify struct tcf_ext_map [BLUETOOTH]: Fix bugs in previous conn add/del workqueue changes. [TCP]: Unexport sysctl_tcp_tso_win_divisor [IPV4]: Make struct ipv4_devconf static. [TR] net/802/tr.c: sysctl_tr_rif_timeout static [XFRM]: Fix statistics. [XFRM]: Remove unused exports. [PKT_SCHED] sch_teql.c: Duplicate IFF_BROADCAST in FMASK, remove 2nd. [BNX2]: Fix ASYM PAUSE advertisement for remote PHY. [IPV4] route cache: Introduce rt_genid for smooth cache invalidation ...
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/Kconfig28
-rw-r--r--drivers/net/wireless/Makefile2
-rw-r--r--drivers/net/wireless/ath5k/base.c10
-rw-r--r--drivers/net/wireless/ath5k/debug.c124
-rw-r--r--drivers/net/wireless/ath5k/debug.h18
-rw-r--r--drivers/net/wireless/b43/dma.c30
-rw-r--r--drivers/net/wireless/b43/main.c19
-rw-r--r--drivers/net/wireless/b43/xmit.c23
-rw-r--r--drivers/net/wireless/b43/xmit.h10
-rw-r--r--drivers/net/wireless/b43legacy/b43legacy.h31
-rw-r--r--drivers/net/wireless/b43legacy/main.c133
-rw-r--r--drivers/net/wireless/b43legacy/phy.c14
-rw-r--r--drivers/net/wireless/b43legacy/pio.c6
-rw-r--r--drivers/net/wireless/b43legacy/radio.c16
-rw-r--r--drivers/net/wireless/hostap/hostap_80211.h5
-rw-r--r--drivers/net/wireless/hostap/hostap_cs.c3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-hw.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.c14
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965-hw.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.c7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965.h1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-helpers.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c86
-rw-r--r--drivers/net/wireless/iwlwifi/iwl4965-base.c84
-rw-r--r--drivers/net/wireless/libertas/assoc.c6
-rw-r--r--drivers/net/wireless/libertas/dev.h2
-rw-r--r--drivers/net/wireless/libertas/if_cs.c6
-rw-r--r--drivers/net/wireless/rndis_wlan.c2757
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c3
-rw-r--r--drivers/net/wireless/rtl8180_dev.c1
31 files changed, 3227 insertions, 222 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f372960904b2..714a6ca30ad2 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -545,6 +545,34 @@ config USB_ZD1201
To compile this driver as a module, choose M here: the
module will be called zd1201.
+config USB_NET_RNDIS_WLAN
+ tristate "Wireless RNDIS USB support"
+ depends on USB && WLAN_80211 && EXPERIMENTAL
+ select USB_USBNET
+ select USB_NET_CDCETHER
+ select USB_NET_RNDIS_HOST
+ select WIRELESS_EXT
+ ---help---
+ This is a driver for wireless RNDIS devices.
+ These are USB based adapters found in devices such as:
+
+ Buffalo WLI-U2-KG125S
+ U.S. Robotics USR5421
+ Belkin F5D7051
+ Linksys WUSB54GSv2
+ Linksys WUSB54GSC
+ Asus WL169gE
+ Eminent EM4045
+ BT Voyager 1055
+ Linksys WUSB54GSv1
+ U.S. Robotics USR5420
+ BUFFALO WLI-USB-G54
+
+ All of these devices are based on Broadcom 4320 chip which is the
+ only wireless RNDIS chip known to date.
+
+ If you choose to build a module, it'll be called rndis_wlan.
+
config RTL8180
tristate "Realtek 8180/8185 PCI support"
depends on MAC80211 && PCI && WLAN_80211 && EXPERIMENTAL
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 6af7b158624e..091dfe2e574e 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -44,6 +44,8 @@ obj-$(CONFIG_ZD1211RW) += zd1211rw/
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
+obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o
+
obj-$(CONFIG_USB_ZD1201) += zd1201.o
obj-$(CONFIG_LIBERTAS) += libertas/
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c
index 72bcf321d1ce..d6599d219193 100644
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -1980,7 +1980,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
struct ath5k_buf *bf = sc->bbuf;
struct ath5k_hw *ah = sc->ah;
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC, "in beacon_send\n");
+ ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
if (unlikely(bf->skb == NULL || sc->opmode == IEEE80211_IF_TYPE_STA ||
sc->opmode == IEEE80211_IF_TYPE_MNTR)) {
@@ -1996,10 +1996,10 @@ ath5k_beacon_send(struct ath5k_softc *sc)
*/
if (unlikely(ath5k_hw_num_tx_pending(ah, sc->bhalq) != 0)) {
sc->bmisscount++;
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC,
+ ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
"missed %u consecutive beacons\n", sc->bmisscount);
if (sc->bmisscount > 3) { /* NB: 3 is a guess */
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC,
+ ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
"stuck beacon time (%u missed)\n",
sc->bmisscount);
tasklet_schedule(&sc->restq);
@@ -2007,7 +2007,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
return;
}
if (unlikely(sc->bmisscount != 0)) {
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC,
+ ATH5K_DBG(sc, ATH5K_DEBUG_BEACON,
"resume beacon xmit after %u misses\n",
sc->bmisscount);
sc->bmisscount = 0;
@@ -2027,7 +2027,7 @@ ath5k_beacon_send(struct ath5k_softc *sc)
ath5k_hw_put_tx_buf(ah, sc->bhalq, bf->daddr);
ath5k_hw_tx_start(ah, sc->bhalq);
- ATH5K_DBG(sc, ATH5K_DEBUG_BEACON_PROC, "TXDP[%u] = %llx (%p)\n",
+ ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
sc->bhalq, (unsigned long long)bf->daddr, bf->desc);
sc->bsent++;
diff --git a/drivers/net/wireless/ath5k/debug.c b/drivers/net/wireless/ath5k/debug.c
index 4ba649e20269..bb581ef6d1ef 100644
--- a/drivers/net/wireless/ath5k/debug.c
+++ b/drivers/net/wireless/ath5k/debug.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007 Bruno Randolf <bruno@thinktube.com>
+ * Copyright (c) 2007-2008 Bruno Randolf <bruno@thinktube.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -200,7 +200,7 @@ static ssize_t read_file_tsf(struct file *file, char __user *user_buf,
{
struct ath5k_softc *sc = file->private_data;
char buf[100];
- snprintf(buf, 100, "0x%016llx\n", ath5k_hw_get_tsf64(sc->ah));
+ snprintf(buf, sizeof(buf), "0x%016llx\n", ath5k_hw_get_tsf64(sc->ah));
return simple_read_from_buffer(user_buf, count, ppos, buf, 19);
}
@@ -209,7 +209,12 @@ static ssize_t write_file_tsf(struct file *file,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
- if (strncmp(userbuf, "reset", 5) == 0) {
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ if (strncmp(buf, "reset", 5) == 0) {
ath5k_hw_reset_tsf(sc->ah);
printk(KERN_INFO "debugfs reset TSF\n");
}
@@ -231,8 +236,8 @@ static ssize_t read_file_beacon(struct file *file, char __user *user_buf,
{
struct ath5k_softc *sc = file->private_data;
struct ath5k_hw *ah = sc->ah;
- char buf[1000];
- int len = 0;
+ char buf[500];
+ unsigned int len = 0;
unsigned int v;
u64 tsf;
@@ -277,11 +282,15 @@ static ssize_t write_file_beacon(struct file *file,
{
struct ath5k_softc *sc = file->private_data;
struct ath5k_hw *ah = sc->ah;
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
- if (strncmp(userbuf, "disable", 7) == 0) {
+ if (strncmp(buf, "disable", 7) == 0) {
AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
printk(KERN_INFO "debugfs disable beacons\n");
- } else if (strncmp(userbuf, "enable", 6) == 0) {
+ } else if (strncmp(buf, "enable", 6) == 0) {
AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
printk(KERN_INFO "debugfs enable beacons\n");
}
@@ -314,6 +323,82 @@ static const struct file_operations fops_reset = {
};
+/* debugfs: debug level */
+
+static struct {
+ enum ath5k_debug_level level;
+ const char *name;
+ const char *desc;
+} dbg_info[] = {
+ { ATH5K_DEBUG_RESET, "reset", "reset and initialization" },
+ { ATH5K_DEBUG_INTR, "intr", "interrupt handling" },
+ { ATH5K_DEBUG_MODE, "mode", "mode init/setup" },
+ { ATH5K_DEBUG_XMIT, "xmit", "basic xmit operation" },
+ { ATH5K_DEBUG_BEACON, "beacon", "beacon handling" },
+ { ATH5K_DEBUG_CALIBRATE, "calib", "periodic calibration" },
+ { ATH5K_DEBUG_TXPOWER, "txpower", "transmit power setting" },
+ { ATH5K_DEBUG_LED, "led", "LED mamagement" },
+ { ATH5K_DEBUG_DUMP_RX, "dumprx", "print received skb content" },
+ { ATH5K_DEBUG_DUMP_TX, "dumptx", "print transmit skb content" },
+ { ATH5K_DEBUG_DUMPMODES, "dumpmodes", "dump modes" },
+ { ATH5K_DEBUG_TRACE, "trace", "trace function calls" },
+ { ATH5K_DEBUG_ANY, "all", "show all debug levels" },
+};
+
+static ssize_t read_file_debug(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_softc *sc = file->private_data;
+ char buf[700];
+ unsigned int len = 0;
+ unsigned int i;
+
+ len += snprintf(buf+len, sizeof(buf)-len,
+ "DEBUG LEVEL: 0x%08x\n\n", sc->debug.level);
+
+ for (i = 0; i < ARRAY_SIZE(dbg_info) - 1; i++) {
+ len += snprintf(buf+len, sizeof(buf)-len,
+ "%10s %c 0x%08x - %s\n", dbg_info[i].name,
+ sc->debug.level & dbg_info[i].level ? '+' : ' ',
+ dbg_info[i].level, dbg_info[i].desc);
+ }
+ len += snprintf(buf+len, sizeof(buf)-len,
+ "%10s %c 0x%08x - %s\n", dbg_info[i].name,
+ sc->debug.level == dbg_info[i].level ? '+' : ' ',
+ dbg_info[i].level, dbg_info[i].desc);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_debug(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath5k_softc *sc = file->private_data;
+ unsigned int i;
+ char buf[20];
+
+ if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ return -EFAULT;
+
+ for (i = 0; i < ARRAY_SIZE(dbg_info); i++) {
+ if (strncmp(buf, dbg_info[i].name,
+ strlen(dbg_info[i].name)) == 0) {
+ sc->debug.level ^= dbg_info[i].level; /* toggle bit */
+ break;
+ }
+ }
+ return count;
+}
+
+static const struct file_operations fops_debug = {
+ .read = read_file_debug,
+ .write = write_file_debug,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+};
+
+
/* init */
void
@@ -326,26 +411,24 @@ void
ath5k_debug_init_device(struct ath5k_softc *sc)
{
sc->debug.level = ath5k_debug;
+
sc->debug.debugfs_phydir = debugfs_create_dir(wiphy_name(sc->hw->wiphy),
- ath5k_global_debugfs);
- sc->debug.debugfs_debug = debugfs_create_u32("debug",
- 0666, sc->debug.debugfs_phydir, &sc->debug.level);
+ ath5k_global_debugfs);
+
+ sc->debug.debugfs_debug = debugfs_create_file("debug", 0666,
+ sc->debug.debugfs_phydir, sc, &fops_debug);
sc->debug.debugfs_registers = debugfs_create_file("registers", 0444,
- sc->debug.debugfs_phydir,
- sc, &fops_registers);
+ sc->debug.debugfs_phydir, sc, &fops_registers);
sc->debug.debugfs_tsf = debugfs_create_file("tsf", 0666,
- sc->debug.debugfs_phydir,
- sc, &fops_tsf);
+ sc->debug.debugfs_phydir, sc, &fops_tsf);
sc->debug.debugfs_beacon = debugfs_create_file("beacon", 0666,
- sc->debug.debugfs_phydir,
- sc, &fops_beacon);
+ sc->debug.debugfs_phydir, sc, &fops_beacon);
sc->debug.debugfs_reset = debugfs_create_file("reset", 0222,
- sc->debug.debugfs_phydir,
- sc, &fops_reset);
+ sc->debug.debugfs_phydir, sc, &fops_reset);
}
void
@@ -415,8 +498,7 @@ ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
struct ath5k_buf *bf;
int status;
- if (likely(!(sc->debug.level &
- (ATH5K_DEBUG_RESET | ATH5K_DEBUG_FATAL))))
+ if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
return;
printk(KERN_DEBUG "rx queue %x, link %p\n",
@@ -426,7 +508,7 @@ ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
list_for_each_entry(bf, &sc->rxbuf, list) {
ds = bf->desc;
status = ah->ah_proc_rx_desc(ah, ds);
- if (!status || (sc->debug.level & ATH5K_DEBUG_FATAL))
+ if (!status)
ath5k_debug_printrxbuf(bf, status == 0);
}
spin_unlock_bh(&sc->rxbuflock);
diff --git a/drivers/net/wireless/ath5k/debug.h b/drivers/net/wireless/ath5k/debug.h
index 2b491cbc8c80..c4fd8c43df0c 100644
--- a/drivers/net/wireless/ath5k/debug.h
+++ b/drivers/net/wireless/ath5k/debug.h
@@ -91,7 +91,6 @@ struct ath5k_dbg_info {
* @ATH5K_DEBUG_MODE: mode init/setup
* @ATH5K_DEBUG_XMIT: basic xmit operation
* @ATH5K_DEBUG_BEACON: beacon handling
- * @ATH5K_DEBUG_BEACON_PROC: beacon ISR proc
* @ATH5K_DEBUG_CALIBRATE: periodic calibration
* @ATH5K_DEBUG_TXPOWER: transmit power setting
* @ATH5K_DEBUG_LED: led management
@@ -99,7 +98,6 @@ struct ath5k_dbg_info {
* @ATH5K_DEBUG_DUMP_TX: print transmit skb content
* @ATH5K_DEBUG_DUMPMODES: dump modes
* @ATH5K_DEBUG_TRACE: trace function calls
- * @ATH5K_DEBUG_FATAL: fatal errors
* @ATH5K_DEBUG_ANY: show at any debug level
*
* The debug level is used to control the amount and type of debugging output
@@ -115,15 +113,13 @@ enum ath5k_debug_level {
ATH5K_DEBUG_MODE = 0x00000004,
ATH5K_DEBUG_XMIT = 0x00000008,
ATH5K_DEBUG_BEACON = 0x00000010,
- ATH5K_DEBUG_BEACON_PROC = 0x00000020,
- ATH5K_DEBUG_CALIBRATE = 0x00000100,
- ATH5K_DEBUG_TXPOWER = 0x00000200,
- ATH5K_DEBUG_LED = 0x00000400,
- ATH5K_DEBUG_DUMP_RX = 0x00001000,
- ATH5K_DEBUG_DUMP_TX = 0x00002000,
- ATH5K_DEBUG_DUMPMODES = 0x00004000,
- ATH5K_DEBUG_TRACE = 0x00010000,
- ATH5K_DEBUG_FATAL = 0x80000000,
+ ATH5K_DEBUG_CALIBRATE = 0x00000020,
+ ATH5K_DEBUG_TXPOWER = 0x00000040,
+ ATH5K_DEBUG_LED = 0x00000080,
+ ATH5K_DEBUG_DUMP_RX = 0x00000100,
+ ATH5K_DEBUG_DUMP_TX = 0x00000200,
+ ATH5K_DEBUG_DUMPMODES = 0x00000400,
+ ATH5K_DEBUG_TRACE = 0x00001000,
ATH5K_DEBUG_ANY = 0xffffffff
};
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index 3e73d2a523aa..8a708b77925d 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -1114,7 +1114,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
{
const struct b43_dma_ops *ops = ring->ops;
u8 *header;
- int slot;
+ int slot, old_top_slot, old_used_slots;
int err;
struct b43_dmadesc_generic *desc;
struct b43_dmadesc_meta *meta;
@@ -1126,6 +1126,9 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
#define SLOTS_PER_PACKET 2
B43_WARN_ON(skb_shinfo(skb)->nr_frags);
+ old_top_slot = ring->current_slot;
+ old_used_slots = ring->used_slots;
+
/* Get a slot for the header. */
slot = request_slot(ring);
desc = ops->idx2desc(ring, slot, &meta_hdr);
@@ -1133,13 +1136,21 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
header = &(ring->txhdr_cache[slot * hdrsize]);
cookie = generate_cookie(ring, slot);
- b43_generate_txhdr(ring->dev, header,
- skb->data, skb->len, ctl, cookie);
+ err = b43_generate_txhdr(ring->dev, header,
+ skb->data, skb->len, ctl, cookie);
+ if (unlikely(err)) {
+ ring->current_slot = old_top_slot;
+ ring->used_slots = old_used_slots;
+ return err;
+ }
meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header,
hdrsize, 1);
- if (dma_mapping_error(meta_hdr->dmaaddr))
+ if (dma_mapping_error(meta_hdr->dmaaddr)) {
+ ring->current_slot = old_top_slot;
+ ring->used_slots = old_used_slots;
return -EIO;
+ }
ops->fill_descriptor(ring, desc, meta_hdr->dmaaddr,
hdrsize, 1, 0, 0);
@@ -1157,6 +1168,8 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
if (dma_mapping_error(meta->dmaaddr)) {
bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
if (!bounce_skb) {
+ ring->current_slot = old_top_slot;
+ ring->used_slots = old_used_slots;
err = -ENOMEM;
goto out_unmap_hdr;
}
@@ -1167,6 +1180,8 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
meta->skb = skb;
meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
if (dma_mapping_error(meta->dmaaddr)) {
+ ring->current_slot = old_top_slot;
+ ring->used_slots = old_used_slots;
err = -EIO;
goto out_free_bounce;
}
@@ -1252,6 +1267,13 @@ int b43_dma_tx(struct b43_wldev *dev,
B43_WARN_ON(ring->stopped);
err = dma_tx_fragment(ring, skb, ctl);
+ if (unlikely(err == -ENOKEY)) {
+ /* Drop this packet, as we don't have the encryption key
+ * anymore and must not transmit it unencrypted. */
+ dev_kfree_skb_any(skb);
+ err = 0;
+ goto out_unlock;
+ }
if (unlikely(err)) {
b43err(dev->wl, "DMA tx mapping failure\n");
goto out_unlock;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 88d2c15d3fbe..64c154d080d8 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -3532,8 +3532,6 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
b43_bluetooth_coext_enable(dev);
ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */
- memset(wl->bssid, 0, ETH_ALEN);
- memset(wl->mac_addr, 0, ETH_ALEN);
b43_upload_card_macaddress(dev);
b43_security_init(dev);
b43_rng_init(wl);
@@ -3630,6 +3628,15 @@ static int b43_op_start(struct ieee80211_hw *hw)
struct b43_wldev *dev = wl->current_dev;
int did_init = 0;
int err = 0;
+ bool do_rfkill_exit = 0;
+
+ /* Kill all old instance specific information to make sure
+ * the card won't use it in the short timeframe between start
+ * and mac80211 reconfiguring it. */
+ memset(wl->bssid, 0, ETH_ALEN);
+ memset(wl->mac_addr, 0, ETH_ALEN);
+ wl->filter_flags = 0;
+ wl->radiotap_enabled = 0;
/* First register RFkill.
* LEDs that are registered later depend on it. */
@@ -3639,8 +3646,10 @@ static int b43_op_start(struct ieee80211_hw *hw)
if (b43_status(dev) < B43_STAT_INITIALIZED) {
err = b43_wireless_core_init(dev);
- if (err)
+ if (err) {
+ do_rfkill_exit = 1;
goto out_mutex_unlock;
+ }
did_init = 1;
}
@@ -3649,6 +3658,7 @@ static int b43_op_start(struct ieee80211_hw *hw)
if (err) {
if (did_init)
b43_wireless_core_exit(dev);
+ do_rfkill_exit = 1;
goto out_mutex_unlock;
}
}
@@ -3656,6 +3666,9 @@ static int b43_op_start(struct ieee80211_hw *hw)
out_mutex_unlock:
mutex_unlock(&wl->mutex);
+ if (do_rfkill_exit)
+ b43_rfkill_exit(dev);
+
return err;
}
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index 3fc53e8b4416..7caa26eb4105 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -178,12 +178,12 @@ static u8 b43_calc_fallback_rate(u8 bitrate)
}
/* Generate a TX data header. */
-void b43_generate_txhdr(struct b43_wldev *dev,
- u8 *_txhdr,
- const unsigned char *fragment_data,
- unsigned int fragment_len,
- const struct ieee80211_tx_control *txctl,
- u16 cookie)
+int b43_generate_txhdr(struct b43_wldev *dev,
+ u8 *_txhdr,
+ const unsigned char *fragment_data,
+ unsigned int fragment_len,
+ const struct ieee80211_tx_control *txctl,
+ u16 cookie)
{
struct b43_txhdr *txhdr = (struct b43_txhdr *)_txhdr;
const struct b43_phy *phy = &dev->phy;
@@ -237,7 +237,15 @@ void b43_generate_txhdr(struct b43_wldev *dev,
B43_WARN_ON(key_idx >= dev->max_nr_keys);
key = &(dev->key[key_idx]);
- B43_WARN_ON(!key->keyconf);
+
+ if (unlikely(!key->keyconf)) {
+ /* This key is invalid. This might only happen
+ * in a short timeframe after machine resume before
+ * we were able to reconfigure keys.
+ * Drop this packet completely. Do not transmit it
+ * unencrypted to avoid leaking information. */
+ return -ENOKEY;
+ }
/* Hardware appends ICV. */
plcp_fragment_len += txctl->icv_len;
@@ -408,6 +416,7 @@ void b43_generate_txhdr(struct b43_wldev *dev,
txhdr->phy_ctl = cpu_to_le16(phy_ctl);
txhdr->extra_ft = extra_ft;
+ return 0;
}
static s8 b43_rssi_postprocess(struct b43_wldev *dev,
diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h
index ca2a2ab8654c..41765039552b 100644
--- a/drivers/net/wireless/b43/xmit.h
+++ b/drivers/net/wireless/b43/xmit.h
@@ -174,11 +174,11 @@ size_t b43_txhdr_size(struct b43_wldev *dev)
}
-void b43_generate_txhdr(struct b43_wldev *dev,
- u8 * txhdr,
- const unsigned char *fragment_data,
- unsigned int fragment_len,
- const struct ieee80211_tx_control *txctl, u16 cookie);
+int b43_generate_txhdr(struct b43_wldev *dev,
+ u8 * txhdr,
+ const unsigned char *fragment_data,
+ unsigned int fragment_len,
+ const struct ieee80211_tx_control *txctl, u16 cookie);
/* Transmit Status */
struct b43_txstatus {
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index 93419adb925e..c80edd2b9044 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -23,7 +23,7 @@
#include "phy.h"
-#define B43legacy_IRQWAIT_MAX_RETRIES 100
+#define B43legacy_IRQWAIT_MAX_RETRIES 20
#define B43legacy_RX_MAX_SSI 60 /* best guess at max ssi */
@@ -40,9 +40,8 @@
#define B43legacy_MMIO_DMA4_IRQ_MASK 0x44
#define B43legacy_MMIO_DMA5_REASON 0x48
#define B43legacy_MMIO_DMA5_IRQ_MASK 0x4C
-#define B43legacy_MMIO_MACCTL 0x120
-#define B43legacy_MMIO_STATUS_BITFIELD 0x120
-#define B43legacy_MMIO_STATUS2_BITFIELD 0x124
+#define B43legacy_MMIO_MACCTL 0x120 /* MAC control */
+#define B43legacy_MMIO_MACCMD 0x124 /* MAC command */
#define B43legacy_MMIO_GEN_IRQ_REASON 0x128
#define B43legacy_MMIO_GEN_IRQ_MASK 0x12C
#define B43legacy_MMIO_RAM_CONTROL 0x130
@@ -177,31 +176,25 @@
#define B43legacy_RADIOCTL_ID 0x01
/* MAC Control bitfield */
+#define B43legacy_MACCTL_ENABLED 0x00000001 /* MAC Enabled */
+#define B43legacy_MACCTL_PSM_RUN 0x00000002 /* Run Microcode */
+#define B43legacy_MACCTL_PSM_JMP0 0x00000004 /* Microcode jump to 0 */
+#define B43legacy_MACCTL_SHM_ENABLED 0x00000100 /* SHM Enabled */
#define B43legacy_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */
+#define B43legacy_MACCTL_BE 0x00010000 /* Big Endian mode */
#define B43legacy_MACCTL_INFRA 0x00020000 /* Infrastructure mode */
#define B43legacy_MACCTL_AP 0x00040000 /* AccessPoint mode */
+#define B43legacy_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */
#define B43legacy_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */
#define B43legacy_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep bad PLCP frames */
#define B43legacy_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */
#define B43legacy_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */
#define B43legacy_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */
+#define B43legacy_MACCTL_HWPS 0x02000000 /* Hardware Power Saving */
+#define B43legacy_MACCTL_AWAKE 0x04000000 /* Device is awake */
+#define B43legacy_MACCTL_TBTTHOLD 0x10000000 /* TBTT Hold */
#define B43legacy_MACCTL_GMODE 0x80000000 /* G Mode */
-/* StatusBitField */
-#define B43legacy_SBF_MAC_ENABLED 0x00000001
-#define B43legacy_SBF_CORE_READY 0x00000004
-#define B43legacy_SBF_400 0x00000400 /*FIXME: fix name*/
-#define B43legacy_SBF_XFER_REG_BYTESWAP 0x00010000
-#define B43legacy_SBF_MODE_NOTADHOC 0x00020000
-#define B43legacy_SBF_MODE_AP 0x00040000
-#define B43legacy_SBF_RADIOREG_LOCK 0x00080000
-#define B43legacy_SBF_MODE_MONITOR 0x00400000
-#define B43legacy_SBF_MODE_PROMISC 0x01000000
-#define B43legacy_SBF_PS1 0x02000000
-#define B43legacy_SBF_PS2 0x04000000
-#define B43legacy_SBF_NO_SSID_BCAST 0x08000000
-#define B43legacy_SBF_TIME_UPDATE 0x10000000
-
/* 802.11 core specific TM State Low flags */
#define B43legacy_TMSLOW_GMODE 0x20000000 /* G Mode Enable */
#define B43legacy_TMSLOW_PLLREFSEL 0x00200000 /* PLL Freq Ref Select */
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 4ed4243feeaa..aa20d5d56e2f 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -225,8 +225,8 @@ static void b43legacy_ram_write(struct b43legacy_wldev *dev, u16 offset,
B43legacy_WARN_ON(offset % 4 != 0);
- status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
- if (status & B43legacy_SBF_XFER_REG_BYTESWAP)
+ status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ if (status & B43legacy_MACCTL_BE)
val = swab32(val);
b43legacy_write32(dev, B43legacy_MMIO_RAM_CONTROL, offset);
@@ -434,9 +434,9 @@ static void b43legacy_time_lock(struct b43legacy_wldev *dev)
{
u32 status;
- status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
- status |= B43legacy_SBF_TIME_UPDATE;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
+ status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ status |= B43legacy_MACCTL_TBTTHOLD;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
mmiowb();
}
@@ -444,9 +444,9 @@ static void b43legacy_time_unlock(struct b43legacy_wldev *dev)
{
u32 status;
- status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
- status &= ~B43legacy_SBF_TIME_UPDATE;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
+ status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ status &= ~B43legacy_MACCTL_TBTTHOLD;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
}
static void b43legacy_tsf_write_locked(struct b43legacy_wldev *dev, u64 tsf)
@@ -647,7 +647,7 @@ void b43legacy_dummy_transmission(struct b43legacy_wldev *dev)
b43legacy_ram_write(dev, i * 4, buffer[i]);
/* dummy read follows */
- b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
+ b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
b43legacy_write16(dev, 0x0568, 0x0000);
b43legacy_write16(dev, 0x07C0, 0x0000);
@@ -794,9 +794,9 @@ static void b43legacy_jssi_write(struct b43legacy_wldev *dev, u32 jssi)
static void b43legacy_generate_noise_sample(struct b43legacy_wldev *dev)
{
b43legacy_jssi_write(dev, 0x7F7F7F7F);
- b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD,
+ b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
b43legacy_read32(dev,
- B43legacy_MMIO_STATUS2_BITFIELD)
+ B43legacy_MMIO_MACCMD)
| (1 << 4));
B43legacy_WARN_ON(dev->noisecalc.channel_at_start !=
dev->phy.channel);
@@ -895,8 +895,8 @@ static void handle_irq_atim_end(struct b43legacy_wldev *dev)
{
if (!dev->reg124_set_0x4) /*FIXME rename this variable*/
return;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD,
- b43legacy_read32(dev, B43legacy_MMIO_STATUS2_BITFIELD)
+ b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
+ b43legacy_read32(dev, B43legacy_MMIO_MACCMD)
| 0x4);
}
@@ -1106,9 +1106,9 @@ static void b43legacy_update_templates(struct b43legacy_wldev *dev)
b43legacy_write_probe_resp_template(dev, 0x268, 0x4A,
B43legacy_CCK_RATE_11MB);
- status = b43legacy_read32(dev, B43legacy_MMIO_STATUS2_BITFIELD);
+ status = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
status |= 0x03;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD, status);
+ b43legacy_write32(dev, B43legacy_MMIO_MACCMD, status);
}
static void b43legacy_refresh_templates(struct b43legacy_wldev *dev,
@@ -1166,7 +1166,7 @@ static void handle_irq_beacon(struct b43legacy_wldev *dev)
return;
dev->irq_savedstate &= ~B43legacy_IRQ_BEACON;
- status = b43legacy_read32(dev, B43legacy_MMIO_STATUS2_BITFIELD);
+ status = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
if (!dev->cached_beacon || ((status & 0x1) && (status & 0x2))) {
/* ACK beacon IRQ. */
@@ -1182,14 +1182,14 @@ static void handle_irq_beacon(struct b43legacy_wldev *dev)
b43legacy_write_beacon_template(dev, 0x68, 0x18,
B43legacy_CCK_RATE_1MB);
status |= 0x1;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD,
+ b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
status);
}
if (!(status & 0x2)) {
b43legacy_write_beacon_template(dev, 0x468, 0x1A,
B43legacy_CCK_RATE_1MB);
status |= 0x2;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD,
+ b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
status);
}
}
@@ -1548,9 +1548,20 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
u16 fwpatch;
u16 fwdate;
u16 fwtime;
- u32 tmp;
+ u32 tmp, macctl;
int err = 0;
+ /* Jump the microcode PSM to offset 0 */
+ macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ B43legacy_WARN_ON(macctl & B43legacy_MACCTL_PSM_RUN);
+ macctl |= B43legacy_MACCTL_PSM_JMP0;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
+ /* Zero out all microcode PSM registers and shared memory. */
+ for (i = 0; i < 64; i++)
+ b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, i, 0);
+ for (i = 0; i < 4096; i += 2)
+ b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, i, 0);
+
/* Upload Microcode. */
data = (__be32 *) (dev->fw.ucode->data + hdr_len);
len = (dev->fw.ucode->size - hdr_len) / sizeof(__be32);
@@ -1581,7 +1592,12 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON,
B43legacy_IRQ_ALL);
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, 0x00020402);
+
+ /* Start the microcode PSM */
+ macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ macctl &= ~B43legacy_MACCTL_PSM_JMP0;
+ macctl |= B43legacy_MACCTL_PSM_RUN;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
/* Wait for the microcode to load and respond */
i = 0;
@@ -1594,9 +1610,13 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
b43legacyerr(dev->wl, "Microcode not responding\n");
b43legacy_print_fw_helptext(dev->wl);
err = -ENODEV;
- goto out;
+ goto error;
+ }
+ msleep_interruptible(50);
+ if (signal_pending(current)) {
+ err = -EINTR;
+ goto error;
}
- udelay(10);
}
/* dummy read follows */
b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
@@ -1617,9 +1637,8 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
" is supported. You must change your firmware"
" files.\n");
b43legacy_print_fw_helptext(dev->wl);
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, 0);
err = -EOPNOTSUPP;
- goto out;
+ goto error;
}
b43legacydbg(dev->wl, "Loading firmware version 0x%X, patch level %u "
"(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", fwrev, fwpatch,
@@ -1629,7 +1648,14 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
dev->fw.rev = fwrev;
dev->fw.patch = fwpatch;
-out:
+ return 0;
+
+error:
+ macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ macctl &= ~B43legacy_MACCTL_PSM_RUN;
+ macctl |= B43legacy_MACCTL_PSM_JMP0;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
+
return err;
}
@@ -1736,9 +1762,9 @@ static int b43legacy_gpio_init(struct b43legacy_wldev *dev)
u32 mask;
u32 set;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL,
b43legacy_read32(dev,
- B43legacy_MMIO_STATUS_BITFIELD)
+ B43legacy_MMIO_MACCTL)
& 0xFFFF3FFF);
b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK,
@@ -1798,14 +1824,14 @@ void b43legacy_mac_enable(struct b43legacy_wldev *dev)
B43legacy_WARN_ON(dev->mac_suspended < 0);
B43legacy_WARN_ON(irqs_disabled());
if (dev->mac_suspended == 0) {
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL,
b43legacy_read32(dev,
- B43legacy_MMIO_STATUS_BITFIELD)
- | B43legacy_SBF_MAC_ENABLED);
+ B43legacy_MMIO_MACCTL)
+ | B43legacy_MACCTL_ENABLED);
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON,
B43legacy_IRQ_MAC_SUSPENDED);
/* the next two are dummy reads */
- b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
+ b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
b43legacy_power_saving_ctl_bits(dev, -1, -1);
@@ -1836,10 +1862,10 @@ void b43legacy_mac_suspend(struct b43legacy_wldev *dev)
dev->irq_savedstate = tmp;
b43legacy_power_saving_ctl_bits(dev, -1, 1);
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL,
b43legacy_read32(dev,
- B43legacy_MMIO_STATUS_BITFIELD)
- & ~B43legacy_SBF_MAC_ENABLED);
+ B43legacy_MMIO_MACCTL)
+ & ~B43legacy_MACCTL_ENABLED);
b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
for (i = 40; i; i--) {
tmp = b43legacy_read32(dev,
@@ -2007,12 +2033,15 @@ static int b43legacy_chip_init(struct b43legacy_wldev *dev)
struct b43legacy_phy *phy = &dev->phy;
int err;
int tmp;
- u32 value32;
+ u32 value32, macctl;
u16 value16;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
- B43legacy_SBF_CORE_READY
- | B43legacy_SBF_400);
+ /* Initialize the MAC control */
+ macctl = B43legacy_MACCTL_IHR_ENABLED | B43legacy_MACCTL_SHM_ENABLED;
+ if (dev->phy.gmode)
+ macctl |= B43legacy_MACCTL_GMODE;
+ macctl |= B43legacy_MACCTL_INFRA;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
err = b43legacy_request_firmware(dev);
if (err)
@@ -2052,12 +2081,12 @@ static int b43legacy_chip_init(struct b43legacy_wldev *dev)
if (dev->dev->id.revision < 5)
b43legacy_write32(dev, 0x010C, 0x01000000);
- value32 = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
- value32 &= ~B43legacy_SBF_MODE_NOTADHOC;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, value32);
- value32 = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
- value32 |= B43legacy_SBF_MODE_NOTADHOC;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, value32);
+ value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ value32 &= ~B43legacy_MACCTL_INFRA;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32);
+ value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ value32 |= B43legacy_MACCTL_INFRA;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32);
if (b43legacy_using_pio(dev)) {
b43legacy_write32(dev, 0x0210, 0x00000100);
@@ -2951,12 +2980,19 @@ static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev)
{
struct b43legacy_wl *wl = dev->wl;
struct b43legacy_phy *phy = &dev->phy;
+ u32 macctl;
B43legacy_WARN_ON(b43legacy_status(dev) > B43legacy_STAT_INITIALIZED);
if (b43legacy_status(dev) != B43legacy_STAT_INITIALIZED)
return;
b43legacy_set_status(dev, B43legacy_STAT_UNINIT);
+ /* Stop the microcode PSM. */
+ macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ macctl &= ~B43legacy_MACCTL_PSM_RUN;
+ macctl |= B43legacy_MACCTL_PSM_JMP0;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
+
mutex_unlock(&wl->mutex);
/* Must unlock as it would otherwise deadlock. No races here.
* Cancel possibly pending workqueues. */
@@ -3221,6 +3257,7 @@ static int b43legacy_op_start(struct ieee80211_hw *hw)
struct b43legacy_wldev *dev = wl->current_dev;
int did_init = 0;
int err = 0;
+ bool do_rfkill_exit = 0;
/* First register RFkill.
* LEDs that are registered later depend on it. */
@@ -3230,8 +3267,10 @@ static int b43legacy_op_start(struct ieee80211_hw *hw)
if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
err = b43legacy_wireless_core_init(dev);
- if (err)
+ if (err) {
+ do_rfkill_exit = 1;
goto out_mutex_unlock;
+ }
did_init = 1;
}
@@ -3240,6 +3279,7 @@ static int b43legacy_op_start(struct ieee80211_hw *hw)
if (err) {
if (did_init)
b43legacy_wireless_core_exit(dev);
+ do_rfkill_exit = 1;
goto out_mutex_unlock;
}
}
@@ -3247,6 +3287,9 @@ static int b43legacy_op_start(struct ieee80211_hw *hw)
out_mutex_unlock:
mutex_unlock(&wl->mutex);
+ if (do_rfkill_exit)
+ b43legacy_rfkill_exit(dev);
+
return err;
}
diff --git a/drivers/net/wireless/b43legacy/phy.c b/drivers/net/wireless/b43legacy/phy.c
index c16febbdbf5d..8e5c09b81871 100644
--- a/drivers/net/wireless/b43legacy/phy.c
+++ b/drivers/net/wireless/b43legacy/phy.c
@@ -140,7 +140,7 @@ void b43legacy_phy_calibrate(struct b43legacy_wldev *dev)
{
struct b43legacy_phy *phy = &dev->phy;
- b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD); /* Dummy read. */
+ b43legacy_read32(dev, B43legacy_MMIO_MACCTL); /* Dummy read. */
if (phy->calibrated)
return;
if (phy->type == B43legacy_PHYTYPE_G && phy->rev == 1) {
@@ -2231,16 +2231,16 @@ bit26 = 1;
* or the latest PS-Poll packet sent was successful,
* set bit26 */
}
- status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
+ status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
if (bit25)
- status |= B43legacy_SBF_PS1;
+ status |= B43legacy_MACCTL_HWPS;
else
- status &= ~B43legacy_SBF_PS1;
+ status &= ~B43legacy_MACCTL_HWPS;
if (bit26)
- status |= B43legacy_SBF_PS2;
+ status |= B43legacy_MACCTL_AWAKE;
else
- status &= ~B43legacy_SBF_PS2;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
+ status &= ~B43legacy_MACCTL_AWAKE;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
if (bit26 && dev->dev->id.revision >= 5) {
for (i = 0; i < 100; i++) {
if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
diff --git a/drivers/net/wireless/b43legacy/pio.c b/drivers/net/wireless/b43legacy/pio.c
index de843ac147ae..e4f4c5c39e33 100644
--- a/drivers/net/wireless/b43legacy/pio.c
+++ b/drivers/net/wireless/b43legacy/pio.c
@@ -334,9 +334,9 @@ struct b43legacy_pioqueue *b43legacy_setup_pioqueue(struct b43legacy_wldev *dev,
tasklet_init(&queue->txtask, tx_tasklet,
(unsigned long)queue);
- value = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
- value &= ~B43legacy_SBF_XFER_REG_BYTESWAP;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, value);
+ value = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ value &= ~B43legacy_MACCTL_BE;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value);
qsize = b43legacy_read16(dev, queue->mmio_base
+ B43legacy_PIO_TXQBUFSIZE);
diff --git a/drivers/net/wireless/b43legacy/radio.c b/drivers/net/wireless/b43legacy/radio.c
index 318a270cf9b4..955832e8654f 100644
--- a/drivers/net/wireless/b43legacy/radio.c
+++ b/drivers/net/wireless/b43legacy/radio.c
@@ -91,10 +91,10 @@ void b43legacy_radio_lock(struct b43legacy_wldev *dev)
{
u32 status;
- status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
- B43legacy_WARN_ON(status & B43legacy_SBF_RADIOREG_LOCK);
- status |= B43legacy_SBF_RADIOREG_LOCK;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
+ status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ B43legacy_WARN_ON(status & B43legacy_MACCTL_RADIOLOCK);
+ status |= B43legacy_MACCTL_RADIOLOCK;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
mmiowb();
udelay(10);
}
@@ -104,10 +104,10 @@ void b43legacy_radio_unlock(struct b43legacy_wldev *dev)
u32 status;
b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); /* dummy read */
- status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
- B43legacy_WARN_ON(!(status & B43legacy_SBF_RADIOREG_LOCK));
- status &= ~B43legacy_SBF_RADIOREG_LOCK;
- b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
+ status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+ B43legacy_WARN_ON(!(status & B43legacy_MACCTL_RADIOLOCK));
+ status &= ~B43legacy_MACCTL_RADIOLOCK;
+ b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
mmiowb();
}
diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h
index d6b9362a3d5c..3694b1eba521 100644
--- a/drivers/net/wireless/hostap/hostap_80211.h
+++ b/drivers/net/wireless/hostap/hostap_80211.h
@@ -71,11 +71,6 @@ struct hostap_80211_rx_status {
u16 rate; /* in 100 kbps */
};
-
-void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb,
- struct hostap_80211_rx_status *rx_stats);
-
-
/* prism2_rx_80211 'type' argument */
enum {
PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC,
diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c
index 07593803065a..437a9bcc9bd3 100644
--- a/drivers/net/wireless/hostap/hostap_cs.c
+++ b/drivers/net/wireless/hostap/hostap_cs.c
@@ -891,6 +891,9 @@ static struct pcmcia_device_id hostap_cs_ids[] = {
PCMCIA_DEVICE_PROD_ID123(
"The Linksys Group, Inc.", "Wireless Network CF Card", "ISL37300P",
0xa5f472c2, 0x9c05598d, 0xc9049a39),
+ PCMCIA_DEVICE_PROD_ID123(
+ "Wireless LAN" , "11Mbps PC Card", "Version 01.02",
+ 0x4b8870ff, 0x70e946d1, 0x4b74baa0),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids);
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
index 6e0187393af4..571815d7e8bf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h
@@ -373,7 +373,7 @@ struct iwl3945_eeprom {
#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */
#define CSR_INT_BIT_DNLD (1 << 28) /* uCode Download */
#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */
-#define CSR_INT_BIT_MAC_CLK_ACTV (1 << 26) /* NIC controller's clock toggled on/off */
+#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */
#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */
#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */
#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index 76c4ed1135f2..4fdeb5323248 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -2369,18 +2369,4 @@ struct pci_device_id iwl3945_hw_card_ids[] = {
{0}
};
-/*
- * Clear the OWNER_MSK, to establish driver (instead of uCode running on
- * embedded controller) as EEPROM reader; each read is a series of pulses
- * to/from the EEPROM chip, not a single event, so even reads could conflict
- * if they weren't arbitrated by some ownership mechanism. Here, the driver
- * simply claims ownership, which should be safe when this function is called
- * (i.e. before loading uCode!).
- */
-inline int iwl3945_eeprom_acquire_semaphore(struct iwl3945_priv *priv)
-{
- _iwl3945_clear_bit(priv, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK);
- return 0;
-}
-
MODULE_DEVICE_TABLE(pci, iwl3945_hw_card_ids);
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h
index 20b925f57e35..1da14f9bbe0f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.h
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.h
@@ -671,7 +671,6 @@ extern int iwl3945_hw_channel_switch(struct iwl3945_priv *priv, u16 channel);
/*
* Forward declare iwl-3945.c functions for iwl-base.c
*/
-extern int iwl3945_eeprom_acquire_semaphore(struct iwl3945_priv *priv);
extern __le32 iwl3945_get_antenna_flags(const struct iwl3945_priv *priv);
extern int iwl3945_init_hw_rate_table(struct iwl3945_priv *priv);
extern void iwl3945_reg_txpower_periodic(struct iwl3945_priv *priv);
@@ -791,7 +790,6 @@ struct iwl3945_priv {
u16 active_rate_basic;
u8 call_post_assoc_from_beacon;
- u8 assoc_station_added;
/* Rate scaling data */
s8 data_retry_limit;
u8 retry_rate;
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
index ff71c09ab1a7..ffe1e9dfdec7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h
@@ -465,7 +465,7 @@ struct iwl4965_eeprom {
#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */
#define CSR_INT_BIT_DNLD (1 << 28) /* uCode Download */
#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */
-#define CSR_INT_BIT_MAC_CLK_ACTV (1 << 26) /* NIC controller's clock toggled on/off */
+#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */
#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */
#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */
#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 04db34ba814b..569347ff377b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -4961,11 +4961,4 @@ int iwl4965_eeprom_acquire_semaphore(struct iwl4965_priv *priv)
return rc;
}
-inline void iwl4965_eeprom_release_semaphore(struct iwl4965_priv *priv)
-{
- iwl4965_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
- CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
-}
-
-
MODULE_DEVICE_TABLE(pci, iwl4965_hw_card_ids);
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.h b/drivers/net/wireless/iwlwifi/iwl-4965.h
index 78bc148c9f7f..9cb82be0ff80 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.h
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.h
@@ -750,7 +750,6 @@ struct iwl4965_priv;
* Forward declare iwl-4965.c functions for iwl-base.c
*/
extern int iwl4965_eeprom_acquire_semaphore(struct iwl4965_priv *priv);
-extern void iwl4965_eeprom_release_semaphore(struct iwl4965_priv *priv);
extern int iwl4965_tx_queue_update_wr_ptr(struct iwl4965_priv *priv,
struct iwl4965_tx_queue *txq,
diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h
index cd2eb1848310..cb009f4c401f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-helpers.h
+++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h
@@ -246,10 +246,10 @@ static inline int iwl_check_bits(unsigned long field, unsigned long mask)
static inline unsigned long elapsed_jiffies(unsigned long start,
unsigned long end)
{
- if (end > start)
+ if (end >= start)
return end - start;
- return end + (MAX_JIFFY_OFFSET - start);
+ return end + (MAX_JIFFY_OFFSET - start) + 1;
}
static inline u8 iwl_get_dma_hi_address(dma_addr_t addr)
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 748ac1222abb..33239f197984 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -1557,6 +1557,20 @@ static void get_eeprom_mac(struct iwl3945_priv *priv, u8 *mac)
memcpy(mac, priv->eeprom.mac_address, 6);
}
+/*
+ * Clear the OWNER_MSK, to establish driver (instead of uCode running on
+ * embedded controller) as EEPROM reader; each read is a series of pulses
+ * to/from the EEPROM chip, not a single event, so even reads could conflict
+ * if they weren't arbitrated by some ownership mechanism. Here, the driver
+ * simply claims ownership, which should be safe when this function is called
+ * (i.e. before loading uCode!).
+ */
+static inline int iwl3945_eeprom_acquire_semaphore(struct iwl3945_priv *priv)
+{
+ _iwl3945_clear_bit(priv, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK);
+ return 0;
+}
+
/**
* iwl3945_eeprom_init - read EEPROM contents
*
@@ -2792,7 +2806,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv,
#endif
/* drop all data frame if we are not associated */
- if (!iwl3945_is_associated(priv) && !priv->assoc_id &&
+ if ((!iwl3945_is_associated(priv) || !priv->assoc_id) &&
((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
IWL_DEBUG_DROP("Dropping - !iwl3945_is_associated\n");
goto drop_unlock;
@@ -4745,8 +4759,9 @@ static void iwl3945_irq_tasklet(struct iwl3945_priv *priv)
#ifdef CONFIG_IWL3945_DEBUG
if (iwl3945_debug_level & (IWL_DL_ISR)) {
/* NIC fires this, but we don't use it, redundant with WAKEUP */
- if (inta & CSR_INT_BIT_MAC_CLK_ACTV)
- IWL_DEBUG_ISR("Microcode started or stopped.\n");
+ if (inta & CSR_INT_BIT_SCD)
+ IWL_DEBUG_ISR("Scheduler finished to transmit "
+ "the frame/frames.\n");
/* Alive notification via Rx interrupt will do the real work */
if (inta & CSR_INT_BIT_ALIVE)
@@ -4754,7 +4769,7 @@ static void iwl3945_irq_tasklet(struct iwl3945_priv *priv)
}
#endif
/* Safely ignore these bits for debug checks below */
- inta &= ~(CSR_INT_BIT_MAC_CLK_ACTV | CSR_INT_BIT_ALIVE);
+ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
/* HW RF KILL switch toggled (4965 only) */
if (inta & CSR_INT_BIT_RF_KILL) {
@@ -4890,8 +4905,11 @@ static irqreturn_t iwl3945_isr(int irq, void *data)
IWL_DEBUG_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
inta, inta_mask, inta_fh);
+ inta &= ~CSR_INT_BIT_SCD;
+
/* iwl3945_irq_tasklet() will service interrupts and re-enable them */
- tasklet_schedule(&priv->irq_tasklet);
+ if (likely(inta || inta_fh))
+ tasklet_schedule(&priv->irq_tasklet);
unplugged:
spin_unlock(&priv->lock);
@@ -5146,6 +5164,15 @@ static int iwl3945_init_channel_map(struct iwl3945_priv *priv)
return 0;
}
+/*
+ * iwl3945_free_channel_map - undo allocations in iwl3945_init_channel_map
+ */
+static void iwl3945_free_channel_map(struct iwl3945_priv *priv)
+{
+ kfree(priv->channel_info);
+ priv->channel_count = 0;
+}
+
/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
* sending probe req. This should be set long enough to hear probe responses
* from more than one AP. */
@@ -5471,6 +5498,17 @@ static int iwl3945_init_geos(struct iwl3945_priv *priv)
return 0;
}
+/*
+ * iwl3945_free_geos - undo allocations in iwl3945_init_geos
+ */
+static void iwl3945_free_geos(struct iwl3945_priv *priv)
+{
+ kfree(priv->modes);
+ kfree(priv->ieee_channels);
+ kfree(priv->ieee_rates);
+ clear_bit(STATUS_GEO_CONFIGURED, &priv->status);
+}
+
/******************************************************************************
*
* uCode download functions
@@ -6130,15 +6168,6 @@ static void iwl3945_alive_start(struct iwl3945_priv *priv)
/* Clear out the uCode error bit if it is set */
clear_bit(STATUS_FW_ERROR, &priv->status);
- rc = iwl3945_init_channel_map(priv);
- if (rc) {
- IWL_ERROR("initializing regulatory failed: %d\n", rc);
- return;
- }
-
- iwl3945_init_geos(priv);
- iwl3945_reset_channel_flag(priv);
-
if (iwl3945_is_rfkill(priv))
return;
@@ -6599,7 +6628,7 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
* that based on the direct_mask added to each channel entry */
scan->tx_cmd.len = cpu_to_le16(
iwl3945_fill_probe_req(priv, (struct ieee80211_mgmt *)scan->data,
- IWL_MAX_SCAN_SIZE - sizeof(scan), 0));
+ IWL_MAX_SCAN_SIZE - sizeof(*scan), 0));
scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
scan->tx_cmd.sta_id = priv->hw_setting.bcast_sta_id;
scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
@@ -7120,7 +7149,7 @@ static void iwl3945_config_ap(struct iwl3945_priv *priv)
{
int rc = 0;
- if (priv->status & STATUS_EXIT_PENDING)
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
/* The following should be done only at AP bring up */
@@ -8614,11 +8643,24 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
IWL_DEBUG_INFO("MAC address: %s\n", print_mac(mac, priv->mac_addr));
SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
+ err = iwl3945_init_channel_map(priv);
+ if (err) {
+ IWL_ERROR("initializing regulatory failed: %d\n", err);
+ goto out_remove_sysfs;
+ }
+
+ err = iwl3945_init_geos(priv);
+ if (err) {
+ IWL_ERROR("initializing geos failed: %d\n", err);
+ goto out_free_channel_map;
+ }
+ iwl3945_reset_channel_flag(priv);
+
iwl3945_rate_control_register(priv->hw);
err = ieee80211_register_hw(priv->hw);
if (err) {
IWL_ERROR("Failed to register network device (error %d)\n", err);
- goto out_remove_sysfs;
+ goto out_free_geos;
}
priv->hw->conf.beacon_int = 100;
@@ -8628,6 +8670,10 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
return 0;
+ out_free_geos:
+ iwl3945_free_geos(priv);
+ out_free_channel_map:
+ iwl3945_free_channel_map(priv);
out_remove_sysfs:
sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group);
@@ -8702,10 +8748,8 @@ static void iwl3945_pci_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- kfree(priv->channel_info);
-
- kfree(priv->ieee_channels);
- kfree(priv->ieee_rates);
+ iwl3945_free_channel_map(priv);
+ iwl3945_free_geos(priv);
if (priv->ibss_beacon)
dev_kfree_skb(priv->ibss_beacon);
diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c
index c86da5cd1df1..bf3a60c037aa 100644
--- a/drivers/net/wireless/iwlwifi/iwl4965-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c
@@ -1639,6 +1639,12 @@ static void get_eeprom_mac(struct iwl4965_priv *priv, u8 *mac)
memcpy(mac, priv->eeprom.mac_address, 6);
}
+static inline void iwl4965_eeprom_release_semaphore(struct iwl4965_priv *priv)
+{
+ iwl4965_clear_bit(priv, CSR_HW_IF_CONFIG_REG,
+ CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
+}
+
/**
* iwl4965_eeprom_init - read EEPROM contents
*
@@ -2927,8 +2933,10 @@ static int iwl4965_tx_skb(struct iwl4965_priv *priv,
#endif
/* drop all data frame if we are not associated */
- if (!iwl4965_is_associated(priv) && !priv->assoc_id &&
- ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
+ if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
+ (!iwl4965_is_associated(priv) ||
+ !priv->assoc_id ||
+ !priv->assoc_station_added)) {
IWL_DEBUG_DROP("Dropping - !iwl4965_is_associated\n");
goto drop_unlock;
}
@@ -5131,8 +5139,9 @@ static void iwl4965_irq_tasklet(struct iwl4965_priv *priv)
#ifdef CONFIG_IWL4965_DEBUG
if (iwl4965_debug_level & (IWL_DL_ISR)) {
/* NIC fires this, but we don't use it, redundant with WAKEUP */
- if (inta & CSR_INT_BIT_MAC_CLK_ACTV)
- IWL_DEBUG_ISR("Microcode started or stopped.\n");
+ if (inta & CSR_INT_BIT_SCD)
+ IWL_DEBUG_ISR("Scheduler finished to transmit "
+ "the frame/frames.\n");
/* Alive notification via Rx interrupt will do the real work */
if (inta & CSR_INT_BIT_ALIVE)
@@ -5140,7 +5149,7 @@ static void iwl4965_irq_tasklet(struct iwl4965_priv *priv)
}
#endif
/* Safely ignore these bits for debug checks below */
- inta &= ~(CSR_INT_BIT_MAC_CLK_ACTV | CSR_INT_BIT_ALIVE);
+ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
/* HW RF KILL switch toggled */
if (inta & CSR_INT_BIT_RF_KILL) {
@@ -5269,8 +5278,11 @@ static irqreturn_t iwl4965_isr(int irq, void *data)
IWL_DEBUG_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
inta, inta_mask, inta_fh);
+ inta &= ~CSR_INT_BIT_SCD;
+
/* iwl4965_irq_tasklet() will service interrupts and re-enable them */
- tasklet_schedule(&priv->irq_tasklet);
+ if (likely(inta || inta_fh))
+ tasklet_schedule(&priv->irq_tasklet);
unplugged:
spin_unlock(&priv->lock);
@@ -5576,6 +5588,15 @@ static int iwl4965_init_channel_map(struct iwl4965_priv *priv)
return 0;
}
+/*
+ * iwl4965_free_channel_map - undo allocations in iwl4965_init_channel_map
+ */
+static void iwl4965_free_channel_map(struct iwl4965_priv *priv)
+{
+ kfree(priv->channel_info);
+ priv->channel_count = 0;
+}
+
/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after
* sending probe req. This should be set long enough to hear probe responses
* from more than one AP. */
@@ -5909,6 +5930,17 @@ static int iwl4965_init_geos(struct iwl4965_priv *priv)
return 0;
}
+/*
+ * iwl4965_free_geos - undo allocations in iwl4965_init_geos
+ */
+static void iwl4965_free_geos(struct iwl4965_priv *priv)
+{
+ kfree(priv->modes);
+ kfree(priv->ieee_channels);
+ kfree(priv->ieee_rates);
+ clear_bit(STATUS_GEO_CONFIGURED, &priv->status);
+}
+
/******************************************************************************
*
* uCode download functions
@@ -6560,15 +6592,6 @@ static void iwl4965_alive_start(struct iwl4965_priv *priv)
/* Clear out the uCode error bit if it is set */
clear_bit(STATUS_FW_ERROR, &priv->status);
- rc = iwl4965_init_channel_map(priv);
- if (rc) {
- IWL_ERROR("initializing regulatory failed: %d\n", rc);
- return;
- }
-
- iwl4965_init_geos(priv);
- iwl4965_reset_channel_flag(priv);
-
if (iwl4965_is_rfkill(priv))
return;
@@ -7023,7 +7046,7 @@ static void iwl4965_bg_request_scan(struct work_struct *data)
* that based on the direct_mask added to each channel entry */
scan->tx_cmd.len = cpu_to_le16(
iwl4965_fill_probe_req(priv, (struct ieee80211_mgmt *)scan->data,
- IWL_MAX_SCAN_SIZE - sizeof(scan), 0));
+ IWL_MAX_SCAN_SIZE - sizeof(*scan), 0));
scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
scan->tx_cmd.sta_id = priv->hw_setting.bcast_sta_id;
scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE;
@@ -7448,7 +7471,7 @@ static int iwl4965_mac_add_interface(struct ieee80211_hw *hw,
if (priv->vif) {
IWL_DEBUG_MAC80211("leave - vif != NULL\n");
- return 0;
+ return -EOPNOTSUPP;
}
spin_lock_irqsave(&priv->lock, flags);
@@ -7580,7 +7603,7 @@ static void iwl4965_config_ap(struct iwl4965_priv *priv)
{
int rc = 0;
- if (priv->status & STATUS_EXIT_PENDING)
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
/* The following should be done only at AP bring up */
@@ -9198,11 +9221,24 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
IWL_DEBUG_INFO("MAC address: %s\n", print_mac(mac, priv->mac_addr));
SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr);
+ err = iwl4965_init_channel_map(priv);
+ if (err) {
+ IWL_ERROR("initializing regulatory failed: %d\n", err);
+ goto out_remove_sysfs;
+ }
+
+ err = iwl4965_init_geos(priv);
+ if (err) {
+ IWL_ERROR("initializing geos failed: %d\n", err);
+ goto out_free_channel_map;
+ }
+ iwl4965_reset_channel_flag(priv);
+
iwl4965_rate_control_register(priv->hw);
err = ieee80211_register_hw(priv->hw);
if (err) {
IWL_ERROR("Failed to register network device (error %d)\n", err);
- goto out_remove_sysfs;
+ goto out_free_geos;
}
priv->hw->conf.beacon_int = 100;
@@ -9212,6 +9248,10 @@ static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
return 0;
+ out_free_geos:
+ iwl4965_free_geos(priv);
+ out_free_channel_map:
+ iwl4965_free_channel_map(priv);
out_remove_sysfs:
sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group);
@@ -9286,10 +9326,8 @@ static void iwl4965_pci_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
- kfree(priv->channel_info);
-
- kfree(priv->ieee_channels);
- kfree(priv->ieee_rates);
+ iwl4965_free_channel_map(priv);
+ iwl4965_free_geos(priv);
if (priv->ibss_beacon)
dev_kfree_skb(priv->ibss_beacon);
diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c
index c622e9b63cd1..87e145ffe8f1 100644
--- a/drivers/net/wireless/libertas/assoc.c
+++ b/drivers/net/wireless/libertas/assoc.c
@@ -12,8 +12,10 @@
#include "cmd.h"
-static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const u8 bssid_any[ETH_ALEN] __attribute__ ((aligned (2))) =
+ { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static int assoc_helper_essid(struct lbs_private *priv,
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 58d7ef6b5ff5..5a69f2b60865 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -349,7 +349,7 @@ struct assoc_request {
u8 channel;
u8 band;
u8 mode;
- u8 bssid[ETH_ALEN];
+ u8 bssid[ETH_ALEN] __attribute__ ((aligned (2)));
/** WEP keys */
struct enc_key wep_keys[4];
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
index 4b5ab9a6b97b..5a9cadb97503 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/libertas/if_cs.c
@@ -249,14 +249,14 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)
lbs_deb_enter(LBS_DEB_CS);
int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE);
- if(int_cause == 0x0) {
+ if (int_cause == 0x0) {
/* Not for us */
return IRQ_NONE;
} else if (int_cause == 0xffff) {
/* Read in junk, the card has probably been removed */
card->priv->surpriseremoved = 1;
-
+ return IRQ_HANDLED;
} else {
if (int_cause & IF_CS_H_IC_TX_OVER)
lbs_host_to_card_done(card->priv);
@@ -717,8 +717,8 @@ static void if_cs_release(struct pcmcia_device *p_dev)
lbs_deb_enter(LBS_DEB_CS);
- pcmcia_disable_device(p_dev);
free_irq(p_dev->irq.AssignedIRQ, card);
+ pcmcia_disable_device(p_dev);
if (card->iobase)
ioport_unmap(card->iobase);
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
new file mode 100644
index 000000000000..d3ecf89abd93
--- /dev/null
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -0,0 +1,2757 @@
+/*
+ * Driver for RNDIS based wireless USB devices.
+ *
+ * Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net>
+ * Copyright (C) 2008 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Portions of this file are based on NDISwrapper project,
+ * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ * http://ndiswrapper.sourceforge.net/
+ */
+
+// #define DEBUG // error path messages, extra info
+// #define VERBOSE // more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <linux/ctype.h>
+#include <linux/spinlock.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+#include <linux/usb/usbnet.h>
+#include <linux/usb/rndis_host.h>
+
+
+/* NOTE: All these are settings for Broadcom chipset */
+static char modparam_country[4] = "EU";
+module_param_string(country, modparam_country, 4, 0444);
+MODULE_PARM_DESC(country, "Country code (ISO 3166-1 alpha-2), default: EU");
+
+static int modparam_frameburst = 1;
+module_param_named(frameburst, modparam_frameburst, int, 0444);
+MODULE_PARM_DESC(frameburst, "enable frame bursting (default: on)");
+
+static int modparam_afterburner = 0;
+module_param_named(afterburner, modparam_afterburner, int, 0444);
+MODULE_PARM_DESC(afterburner,
+ "enable afterburner aka '125 High Speed Mode' (default: off)");
+
+static int modparam_power_save = 0;
+module_param_named(power_save, modparam_power_save, int, 0444);
+MODULE_PARM_DESC(power_save,
+ "set power save mode: 0=off, 1=on, 2=fast (default: off)");
+
+static int modparam_power_output = 3;
+module_param_named(power_output, modparam_power_output, int, 0444);
+MODULE_PARM_DESC(power_output,
+ "set power output: 0=25%, 1=50%, 2=75%, 3=100% (default: 100%)");
+
+static int modparam_roamtrigger = -70;
+module_param_named(roamtrigger, modparam_roamtrigger, int, 0444);
+MODULE_PARM_DESC(roamtrigger,
+ "set roaming dBm trigger: -80=optimize for distance, "
+ "-60=bandwidth (default: -70)");
+
+static int modparam_roamdelta = 1;
+module_param_named(roamdelta, modparam_roamdelta, int, 0444);
+MODULE_PARM_DESC(roamdelta,
+ "set roaming tendency: 0=aggressive, 1=moderate, "
+ "2=conservative (default: moderate)");
+
+static int modparam_workaround_interval = 500;
+module_param_named(workaround_interval, modparam_workaround_interval,
+ int, 0444);
+MODULE_PARM_DESC(workaround_interval,
+ "set stall workaround interval in msecs (default: 500)");
+
+
+/* various RNDIS OID defs */
+#define OID_GEN_LINK_SPEED ccpu2(0x00010107)
+#define OID_GEN_RNDIS_CONFIG_PARAMETER ccpu2(0x0001021b)
+
+#define OID_GEN_XMIT_OK ccpu2(0x00020101)
+#define OID_GEN_RCV_OK ccpu2(0x00020102)
+#define OID_GEN_XMIT_ERROR ccpu2(0x00020103)
+#define OID_GEN_RCV_ERROR ccpu2(0x00020104)
+#define OID_GEN_RCV_NO_BUFFER ccpu2(0x00020105)
+
+#define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101)
+#define OID_802_3_CURRENT_ADDRESS ccpu2(0x01010102)
+#define OID_802_3_MULTICAST_LIST ccpu2(0x01010103)
+#define OID_802_3_MAXIMUM_LIST_SIZE ccpu2(0x01010104)
+
+#define OID_802_11_BSSID ccpu2(0x0d010101)
+#define OID_802_11_SSID ccpu2(0x0d010102)
+#define OID_802_11_INFRASTRUCTURE_MODE ccpu2(0x0d010108)
+#define OID_802_11_ADD_WEP ccpu2(0x0d010113)
+#define OID_802_11_REMOVE_WEP ccpu2(0x0d010114)
+#define OID_802_11_DISASSOCIATE ccpu2(0x0d010115)
+#define OID_802_11_AUTHENTICATION_MODE ccpu2(0x0d010118)
+#define OID_802_11_PRIVACY_FILTER ccpu2(0x0d010119)
+#define OID_802_11_BSSID_LIST_SCAN ccpu2(0x0d01011a)
+#define OID_802_11_ENCRYPTION_STATUS ccpu2(0x0d01011b)
+#define OID_802_11_ADD_KEY ccpu2(0x0d01011d)
+#define OID_802_11_REMOVE_KEY ccpu2(0x0d01011e)
+#define OID_802_11_PMKID ccpu2(0x0d010123)
+#define OID_802_11_NETWORK_TYPES_SUPPORTED ccpu2(0x0d010203)
+#define OID_802_11_NETWORK_TYPE_IN_USE ccpu2(0x0d010204)
+#define OID_802_11_TX_POWER_LEVEL ccpu2(0x0d010205)
+#define OID_802_11_RSSI ccpu2(0x0d010206)
+#define OID_802_11_RSSI_TRIGGER ccpu2(0x0d010207)
+#define OID_802_11_FRAGMENTATION_THRESHOLD ccpu2(0x0d010209)
+#define OID_802_11_RTS_THRESHOLD ccpu2(0x0d01020a)
+#define OID_802_11_SUPPORTED_RATES ccpu2(0x0d01020e)
+#define OID_802_11_CONFIGURATION ccpu2(0x0d010211)
+#define OID_802_11_BSSID_LIST ccpu2(0x0d010217)
+
+
+/* Typical noise/maximum signal level values taken from ndiswrapper iw_ndis.h */
+#define WL_NOISE -96 /* typical noise level in dBm */
+#define WL_SIGMAX -32 /* typical maximum signal level in dBm */
+
+
+/* Assume that Broadcom 4320 (only chipset at time of writing known to be
+ * based on wireless rndis) has default txpower of 13dBm.
+ * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications.
+ * 13dBm == 19.9mW
+ */
+#define BCM4320_DEFAULT_TXPOWER 20
+
+
+/* codes for "status" field of completion messages */
+#define RNDIS_STATUS_ADAPTER_NOT_READY ccpu2(0xc0010011)
+#define RNDIS_STATUS_ADAPTER_NOT_OPEN ccpu2(0xc0010012)
+
+
+/* NDIS data structures. Taken from wpa_supplicant driver_ndis.c
+ * slightly modified for datatype endianess, etc
+ */
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+struct NDIS_802_11_SSID {
+ __le32 SsidLength;
+ u8 Ssid[NDIS_802_11_LENGTH_SSID];
+} __attribute__((packed));
+
+enum NDIS_802_11_NETWORK_TYPE {
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ Ndis802_11NetworkTypeMax
+};
+
+struct NDIS_802_11_CONFIGURATION_FH {
+ __le32 Length;
+ __le32 HopPattern;
+ __le32 HopSet;
+ __le32 DwellTime;
+} __attribute__((packed));
+
+struct NDIS_802_11_CONFIGURATION {
+ __le32 Length;
+ __le32 BeaconPeriod;
+ __le32 ATIMWindow;
+ __le32 DSConfig;
+ struct NDIS_802_11_CONFIGURATION_FH FHConfig;
+} __attribute__((packed));
+
+enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax
+};
+
+enum NDIS_802_11_AUTHENTICATION_MODE {
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeWPA2,
+ Ndis802_11AuthModeWPA2PSK,
+ Ndis802_11AuthModeMax
+};
+
+enum NDIS_802_11_ENCRYPTION_STATUS {
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent
+};
+
+enum NDIS_802_11_PRIVACY_FILTER {
+ Ndis802_11PrivFilterAcceptAll,
+ Ndis802_11PrivFilter8021xWEP
+};
+
+struct NDIS_WLAN_BSSID_EX {
+ __le32 Length;
+ u8 MacAddress[6];
+ u8 Padding[2];
+ struct NDIS_802_11_SSID Ssid;
+ __le32 Privacy;
+ __le32 Rssi;
+ enum NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ struct NDIS_802_11_CONFIGURATION Configuration;
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ u8 SupportedRates[NDIS_802_11_LENGTH_RATES_EX];
+ __le32 IELength;
+ u8 IEs[0];
+} __attribute__((packed));
+
+struct NDIS_802_11_BSSID_LIST_EX {
+ __le32 NumberOfItems;
+ struct NDIS_WLAN_BSSID_EX Bssid[0];
+} __attribute__((packed));
+
+struct NDIS_802_11_FIXED_IEs {
+ u8 Timestamp[8];
+ __le16 BeaconInterval;
+ __le16 Capabilities;
+} __attribute__((packed));
+
+struct NDIS_802_11_WEP {
+ __le32 Length;
+ __le32 KeyIndex;
+ __le32 KeyLength;
+ u8 KeyMaterial[32];
+} __attribute__((packed));
+
+struct NDIS_802_11_KEY {
+ __le32 Length;
+ __le32 KeyIndex;
+ __le32 KeyLength;
+ u8 Bssid[6];
+ u8 Padding[6];
+ __le64 KeyRSC;
+ u8 KeyMaterial[32];
+} __attribute__((packed));
+
+struct NDIS_802_11_REMOVE_KEY {
+ __le32 Length;
+ __le32 KeyIndex;
+ u8 Bssid[6];
+} __attribute__((packed));
+
+struct RNDIS_CONFIG_PARAMETER_INFOBUFFER {
+ __le32 ParameterNameOffset;
+ __le32 ParameterNameLength;
+ __le32 ParameterType;
+ __le32 ParameterValueOffset;
+ __le32 ParameterValueLength;
+} __attribute__((packed));
+
+/* these have to match what is in wpa_supplicant */
+enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg;
+enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, CIPHER_WEP104 }
+ wpa_cipher;
+enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, KEY_MGMT_802_1X_NO_WPA,
+ KEY_MGMT_WPA_NONE } wpa_key_mgmt;
+
+/*
+ * private data
+ */
+#define NET_TYPE_11FB 0
+
+#define CAP_MODE_80211A 1
+#define CAP_MODE_80211B 2
+#define CAP_MODE_80211G 4
+#define CAP_MODE_MASK 7
+#define CAP_SUPPORT_TXPOWER 8
+
+#define WORK_CONNECTION_EVENT (1<<0)
+#define WORK_SET_MULTICAST_LIST (1<<1)
+
+/* RNDIS device private data */
+struct rndis_wext_private {
+ char name[32];
+
+ struct usbnet *usbdev;
+
+ struct workqueue_struct *workqueue;
+ struct delayed_work stats_work;
+ struct work_struct work;
+ struct mutex command_lock;
+ spinlock_t stats_lock;
+ unsigned long work_pending;
+
+ struct iw_statistics iwstats;
+ struct iw_statistics privstats;
+
+ int nick_len;
+ char nick[32];
+
+ int caps;
+ int multicast_size;
+
+ /* module parameters */
+ char param_country[4];
+ int param_frameburst;
+ int param_afterburner;
+ int param_power_save;
+ int param_power_output;
+ int param_roamtrigger;
+ int param_roamdelta;
+ u32 param_workaround_interval;
+
+ /* hardware state */
+ int radio_on;
+ int infra_mode;
+ struct NDIS_802_11_SSID essid;
+
+ /* encryption stuff */
+ int encr_tx_key_index;
+ char encr_keys[4][32];
+ int encr_key_len[4];
+ int wpa_version;
+ int wpa_keymgmt;
+ int wpa_authalg;
+ int wpa_ie_len;
+ u8 *wpa_ie;
+ int wpa_cipher_pair;
+ int wpa_cipher_group;
+};
+
+
+static const int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+ 2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+
+static const int rates_80211g[8] = { 6, 9, 12, 18, 24, 36, 48, 54 };
+
+static const int bcm4320_power_output[4] = { 25, 50, 75, 100 };
+
+static const unsigned char zero_bssid[ETH_ALEN] = {0,};
+static const unsigned char ffff_bssid[ETH_ALEN] = { 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff };
+
+
+static struct rndis_wext_private *get_rndis_wext_priv(struct usbnet *dev)
+{
+ return (struct rndis_wext_private *)dev->driver_priv;
+}
+
+
+static u32 get_bcm4320_power(struct rndis_wext_private *priv)
+{
+ return BCM4320_DEFAULT_TXPOWER *
+ bcm4320_power_output[priv->param_power_output] / 100;
+}
+
+
+/* translate error code */
+static int rndis_error_status(__le32 rndis_status)
+{
+ int ret = -EINVAL;
+ switch (rndis_status) {
+ case RNDIS_STATUS_SUCCESS:
+ ret = 0;
+ break;
+ case RNDIS_STATUS_FAILURE:
+ case RNDIS_STATUS_INVALID_DATA:
+ ret = -EINVAL;
+ break;
+ case RNDIS_STATUS_NOT_SUPPORTED:
+ ret = -EOPNOTSUPP;
+ break;
+ case RNDIS_STATUS_ADAPTER_NOT_READY:
+ case RNDIS_STATUS_ADAPTER_NOT_OPEN:
+ ret = -EBUSY;
+ break;
+ }
+ return ret;
+}
+
+
+static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+ union {
+ void *buf;
+ struct rndis_msg_hdr *header;
+ struct rndis_query *get;
+ struct rndis_query_c *get_c;
+ } u;
+ int ret, buflen;
+
+ buflen = *len + sizeof(*u.get);
+ if (buflen < CONTROL_BUFFER_SIZE)
+ buflen = CONTROL_BUFFER_SIZE;
+ u.buf = kmalloc(buflen, GFP_KERNEL);
+ if (!u.buf)
+ return -ENOMEM;
+ memset(u.get, 0, sizeof *u.get);
+ u.get->msg_type = RNDIS_MSG_QUERY;
+ u.get->msg_len = ccpu2(sizeof *u.get);
+ u.get->oid = oid;
+
+ mutex_lock(&priv->command_lock);
+ ret = rndis_command(dev, u.header);
+ mutex_unlock(&priv->command_lock);
+
+ if (ret == 0) {
+ ret = le32_to_cpu(u.get_c->len);
+ *len = (*len > ret) ? ret : *len;
+ memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len);
+ ret = rndis_error_status(u.get_c->status);
+ }
+
+ kfree(u.buf);
+ return ret;
+}
+
+
+static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+ union {
+ void *buf;
+ struct rndis_msg_hdr *header;
+ struct rndis_set *set;
+ struct rndis_set_c *set_c;
+ } u;
+ int ret, buflen;
+
+ buflen = len + sizeof(*u.set);
+ if (buflen < CONTROL_BUFFER_SIZE)
+ buflen = CONTROL_BUFFER_SIZE;
+ u.buf = kmalloc(buflen, GFP_KERNEL);
+ if (!u.buf)
+ return -ENOMEM;
+
+ memset(u.set, 0, sizeof *u.set);
+ u.set->msg_type = RNDIS_MSG_SET;
+ u.set->msg_len = cpu_to_le32(sizeof(*u.set) + len);
+ u.set->oid = oid;
+ u.set->len = cpu_to_le32(len);
+ u.set->offset = ccpu2(sizeof(*u.set) - 8);
+ u.set->handle = ccpu2(0);
+ memcpy(u.buf + sizeof(*u.set), data, len);
+
+ mutex_lock(&priv->command_lock);
+ ret = rndis_command(dev, u.header);
+ mutex_unlock(&priv->command_lock);
+
+ if (ret == 0)
+ ret = rndis_error_status(u.set_c->status);
+
+ kfree(u.buf);
+ return ret;
+}
+
+
+/*
+ * Specs say that we can only set config parameters only soon after device
+ * initialization.
+ * value_type: 0 = u32, 2 = unicode string
+ */
+static int rndis_set_config_parameter(struct usbnet *dev, char *param,
+ int value_type, void *value)
+{
+ struct RNDIS_CONFIG_PARAMETER_INFOBUFFER *infobuf;
+ int value_len, info_len, param_len, ret, i;
+ __le16 *unibuf;
+ __le32 *dst_value;
+
+ if (value_type == 0)
+ value_len = sizeof(__le32);
+ else if (value_type == 2)
+ value_len = strlen(value) * sizeof(__le16);
+ else
+ return -EINVAL;
+
+ param_len = strlen(param) * sizeof(__le16);
+ info_len = sizeof(*infobuf) + param_len + value_len;
+
+#ifdef DEBUG
+ info_len += 12;
+#endif
+ infobuf = kmalloc(info_len, GFP_KERNEL);
+ if (!infobuf)
+ return -ENOMEM;
+
+#ifdef DEBUG
+ info_len -= 12;
+ /* extra 12 bytes are for padding (debug output) */
+ memset(infobuf, 0xCC, info_len + 12);
+#endif
+
+ if (value_type == 2)
+ devdbg(dev, "setting config parameter: %s, value: %s",
+ param, (u8 *)value);
+ else
+ devdbg(dev, "setting config parameter: %s, value: %d",
+ param, *(u32 *)value);
+
+ infobuf->ParameterNameOffset = cpu_to_le32(sizeof(*infobuf));
+ infobuf->ParameterNameLength = cpu_to_le32(param_len);
+ infobuf->ParameterType = cpu_to_le32(value_type);
+ infobuf->ParameterValueOffset = cpu_to_le32(sizeof(*infobuf) +
+ param_len);
+ infobuf->ParameterValueLength = cpu_to_le32(value_len);
+
+ /* simple string to unicode string conversion */
+ unibuf = (void *)infobuf + sizeof(*infobuf);
+ for (i = 0; i < param_len / sizeof(__le16); i++)
+ unibuf[i] = cpu_to_le16(param[i]);
+
+ if (value_type == 2) {
+ unibuf = (void *)infobuf + sizeof(*infobuf) + param_len;
+ for (i = 0; i < value_len / sizeof(__le16); i++)
+ unibuf[i] = cpu_to_le16(((u8 *)value)[i]);
+ } else {
+ dst_value = (void *)infobuf + sizeof(*infobuf) + param_len;
+ *dst_value = cpu_to_le32(*(u32 *)value);
+ }
+
+#ifdef DEBUG
+ devdbg(dev, "info buffer (len: %d):", info_len);
+ for (i = 0; i < info_len; i += 12) {
+ u32 *tmp = (u32 *)((u8 *)infobuf + i);
+ devdbg(dev, "%08X:%08X:%08X",
+ cpu_to_be32(tmp[0]),
+ cpu_to_be32(tmp[1]),
+ cpu_to_be32(tmp[2]));
+ }
+#endif
+
+ ret = rndis_set_oid(dev, OID_GEN_RNDIS_CONFIG_PARAMETER,
+ infobuf, info_len);
+ if (ret != 0)
+ devdbg(dev, "setting rndis config paramater failed, %d.", ret);
+
+ kfree(infobuf);
+ return ret;
+}
+
+static int rndis_set_config_parameter_str(struct usbnet *dev,
+ char *param, char *value)
+{
+ return(rndis_set_config_parameter(dev, param, 2, value));
+}
+
+/*static int rndis_set_config_parameter_u32(struct usbnet *dev,
+ char *param, u32 value)
+{
+ return(rndis_set_config_parameter(dev, param, 0, &value));
+}*/
+
+
+/*
+ * data conversion functions
+ */
+static int level_to_qual(int level)
+{
+ int qual = 100 * (level - WL_NOISE) / (WL_SIGMAX - WL_NOISE);
+ return qual >= 0 ? (qual <= 100 ? qual : 100) : 0;
+}
+
+
+static void dsconfig_to_freq(unsigned int dsconfig, struct iw_freq *freq)
+{
+ freq->e = 0;
+ freq->i = 0;
+ freq->flags = 0;
+
+ /* see comment in wireless.h above the "struct iw_freq"
+ * definition for an explanation of this if
+ * NOTE: 1000000 is due to the kHz
+ */
+ if (dsconfig > 1000000) {
+ freq->m = dsconfig / 10;
+ freq->e = 1;
+ } else
+ freq->m = dsconfig;
+
+ /* convert from kHz to Hz */
+ freq->e += 3;
+}
+
+
+static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)
+{
+ if (freq->m < 1000 && freq->e == 0) {
+ if (freq->m >= 1 &&
+ freq->m <= (sizeof(freq_chan) / sizeof(freq_chan[0])))
+ *dsconfig = freq_chan[freq->m - 1] * 1000;
+ else
+ return -1;
+ } else {
+ int i;
+ *dsconfig = freq->m;
+ for (i = freq->e; i > 0; i--)
+ *dsconfig *= 10;
+ *dsconfig /= 1000;
+ }
+
+ return 0;
+}
+
+
+/*
+ * common functions
+ */
+static int
+add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index);
+
+static int get_essid(struct usbnet *usbdev, struct NDIS_802_11_SSID *ssid)
+{
+ int ret, len;
+
+ len = sizeof(*ssid);
+ ret = rndis_query_oid(usbdev, OID_802_11_SSID, ssid, &len);
+
+ if (ret != 0)
+ ssid->SsidLength = 0;
+
+#ifdef DEBUG
+ {
+ unsigned char tmp[NDIS_802_11_LENGTH_SSID + 1];
+
+ memcpy(tmp, ssid->Ssid, le32_to_cpu(ssid->SsidLength));
+ tmp[le32_to_cpu(ssid->SsidLength)] = 0;
+ devdbg(usbdev, "get_essid: '%s', ret: %d", tmp, ret);
+ }
+#endif
+ return ret;
+}
+
+
+static int set_essid(struct usbnet *usbdev, struct NDIS_802_11_SSID *ssid)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ int ret;
+
+ ret = rndis_set_oid(usbdev, OID_802_11_SSID, ssid, sizeof(*ssid));
+ if (ret == 0) {
+ memcpy(&priv->essid, ssid, sizeof(priv->essid));
+ priv->radio_on = 1;
+ devdbg(usbdev, "set_essid: radio_on = 1");
+ }
+
+ return ret;
+}
+
+
+static int get_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN])
+{
+ int ret, len;
+
+ len = ETH_ALEN;
+ ret = rndis_query_oid(usbdev, OID_802_11_BSSID, bssid, &len);
+
+ if (ret != 0)
+ memset(bssid, 0, ETH_ALEN);
+
+ return ret;
+}
+
+
+static int is_associated(struct usbnet *usbdev)
+{
+ u8 bssid[ETH_ALEN];
+ int ret;
+
+ ret = get_bssid(usbdev, bssid);
+
+ return(ret == 0 && memcmp(bssid, zero_bssid, ETH_ALEN) != 0);
+}
+
+
+static int disassociate(struct usbnet *usbdev, int reset_ssid)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ struct NDIS_802_11_SSID ssid;
+ int i, ret = 0;
+
+ if (priv->radio_on) {
+ ret = rndis_set_oid(usbdev, OID_802_11_DISASSOCIATE, NULL, 0);
+ if (ret == 0) {
+ priv->radio_on = 0;
+ devdbg(usbdev, "disassociate: radio_on = 0");
+
+ if (reset_ssid)
+ msleep(100);
+ }
+ }
+
+ /* disassociate causes radio to be turned off; if reset_ssid
+ * is given, set random ssid to enable radio */
+ if (reset_ssid) {
+ ssid.SsidLength = cpu_to_le32(sizeof(ssid.Ssid));
+ get_random_bytes(&ssid.Ssid[2], sizeof(ssid.Ssid)-2);
+ ssid.Ssid[0] = 0x1;
+ ssid.Ssid[1] = 0xff;
+ for (i = 2; i < sizeof(ssid.Ssid); i++)
+ ssid.Ssid[i] = 0x1 + (ssid.Ssid[i] * 0xfe / 0xff);
+ ret = set_essid(usbdev, &ssid);
+ }
+ return ret;
+}
+
+
+static int set_auth_mode(struct usbnet *usbdev, int wpa_version, int authalg)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ __le32 tmp;
+ int auth_mode, ret;
+
+ devdbg(usbdev, "set_auth_mode: wpa_version=0x%x authalg=0x%x "
+ "keymgmt=0x%x", wpa_version, authalg, priv->wpa_keymgmt);
+
+ if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) {
+ if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X)
+ auth_mode = Ndis802_11AuthModeWPA2;
+ else
+ auth_mode = Ndis802_11AuthModeWPA2PSK;
+ } else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) {
+ if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X)
+ auth_mode = Ndis802_11AuthModeWPA;
+ else if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_PSK)
+ auth_mode = Ndis802_11AuthModeWPAPSK;
+ else
+ auth_mode = Ndis802_11AuthModeWPANone;
+ } else if (authalg & IW_AUTH_ALG_SHARED_KEY) {
+ if (authalg & IW_AUTH_ALG_OPEN_SYSTEM)
+ auth_mode = Ndis802_11AuthModeAutoSwitch;
+ else
+ auth_mode = Ndis802_11AuthModeShared;
+ } else
+ auth_mode = Ndis802_11AuthModeOpen;
+
+ tmp = cpu_to_le32(auth_mode);
+ ret = rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp,
+ sizeof(tmp));
+ if (ret != 0) {
+ devwarn(usbdev, "setting auth mode failed (%08X)", ret);
+ return ret;
+ }
+
+ priv->wpa_version = wpa_version;
+ priv->wpa_authalg = authalg;
+ return 0;
+}
+
+
+static int set_priv_filter(struct usbnet *usbdev)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ __le32 tmp;
+
+ devdbg(usbdev, "set_priv_filter: wpa_version=0x%x", priv->wpa_version);
+
+ if (priv->wpa_version & IW_AUTH_WPA_VERSION_WPA2 ||
+ priv->wpa_version & IW_AUTH_WPA_VERSION_WPA)
+ tmp = cpu_to_le32(Ndis802_11PrivFilter8021xWEP);
+ else
+ tmp = cpu_to_le32(Ndis802_11PrivFilterAcceptAll);
+
+ return rndis_set_oid(usbdev, OID_802_11_PRIVACY_FILTER, &tmp,
+ sizeof(tmp));
+}
+
+
+static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ __le32 tmp;
+ int encr_mode, ret;
+
+ devdbg(usbdev, "set_encr_mode: cipher_pair=0x%x cipher_group=0x%x",
+ pairwise,
+ groupwise);
+
+ if (pairwise & IW_AUTH_CIPHER_CCMP)
+ encr_mode = Ndis802_11Encryption3Enabled;
+ else if (pairwise & IW_AUTH_CIPHER_TKIP)
+ encr_mode = Ndis802_11Encryption2Enabled;
+ else if (pairwise &
+ (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
+ encr_mode = Ndis802_11Encryption1Enabled;
+ else if (groupwise & IW_AUTH_CIPHER_CCMP)
+ encr_mode = Ndis802_11Encryption3Enabled;
+ else if (groupwise & IW_AUTH_CIPHER_TKIP)
+ encr_mode = Ndis802_11Encryption2Enabled;
+ else
+ encr_mode = Ndis802_11EncryptionDisabled;
+
+ tmp = cpu_to_le32(encr_mode);
+ ret = rndis_set_oid(usbdev, OID_802_11_ENCRYPTION_STATUS, &tmp,
+ sizeof(tmp));
+ if (ret != 0) {
+ devwarn(usbdev, "setting encr mode failed (%08X)", ret);
+ return ret;
+ }
+
+ priv->wpa_cipher_pair = pairwise;
+ priv->wpa_cipher_group = groupwise;
+ return 0;
+}
+
+
+static int set_assoc_params(struct usbnet *usbdev)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ set_auth_mode(usbdev, priv->wpa_version, priv->wpa_authalg);
+ set_priv_filter(usbdev);
+ set_encr_mode(usbdev, priv->wpa_cipher_pair, priv->wpa_cipher_group);
+
+ return 0;
+}
+
+
+static int set_infra_mode(struct usbnet *usbdev, int mode)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ __le32 tmp;
+ int ret, i;
+
+ devdbg(usbdev, "set_infra_mode: infra_mode=0x%x", priv->infra_mode);
+
+ tmp = cpu_to_le32(mode);
+ ret = rndis_set_oid(usbdev, OID_802_11_INFRASTRUCTURE_MODE, &tmp,
+ sizeof(tmp));
+ if (ret != 0) {
+ devwarn(usbdev, "setting infra mode failed (%08X)", ret);
+ return ret;
+ }
+
+ /* NDIS drivers clear keys when infrastructure mode is
+ * changed. But Linux tools assume otherwise. So set the
+ * keys */
+ if (priv->wpa_keymgmt == 0 ||
+ priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) {
+ for (i = 0; i < 4; i++) {
+ if (priv->encr_key_len[i] > 0)
+ add_wep_key(usbdev, priv->encr_keys[i],
+ priv->encr_key_len[i], i);
+ }
+ }
+
+ priv->infra_mode = mode;
+ return 0;
+}
+
+
+static void set_default_iw_params(struct usbnet *usbdev)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ priv->wpa_keymgmt = 0;
+ priv->wpa_version = 0;
+
+ set_infra_mode(usbdev, Ndis802_11Infrastructure);
+ set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED,
+ IW_AUTH_ALG_OPEN_SYSTEM);
+ set_priv_filter(usbdev);
+ set_encr_mode(usbdev, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE);
+}
+
+
+static int deauthenticate(struct usbnet *usbdev)
+{
+ int ret;
+
+ ret = disassociate(usbdev, 1);
+ set_default_iw_params(usbdev);
+ return ret;
+}
+
+
+/* index must be 0 - N, as per NDIS */
+static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ struct NDIS_802_11_WEP ndis_key;
+ int ret;
+
+ if (key_len <= 0 || key_len > 32 || index < 0 || index >= 4)
+ return -EINVAL;
+
+ memset(&ndis_key, 0, sizeof(ndis_key));
+
+ ndis_key.Length = cpu_to_le32(sizeof(ndis_key));
+ ndis_key.KeyLength = cpu_to_le32(key_len);
+ ndis_key.KeyIndex = cpu_to_le32(index);
+ memcpy(&ndis_key.KeyMaterial, key, key_len);
+
+ if (index == priv->encr_tx_key_index) {
+ ndis_key.KeyIndex |= cpu_to_le32(1 << 31);
+ ret = set_encr_mode(usbdev, IW_AUTH_CIPHER_WEP104,
+ IW_AUTH_CIPHER_NONE);
+ if (ret)
+ devwarn(usbdev, "encryption couldn't be enabled (%08X)",
+ ret);
+ }
+
+ ret = rndis_set_oid(usbdev, OID_802_11_ADD_WEP, &ndis_key,
+ sizeof(ndis_key));
+ if (ret != 0) {
+ devwarn(usbdev, "adding encryption key %d failed (%08X)",
+ index+1, ret);
+ return ret;
+ }
+
+ priv->encr_key_len[index] = key_len;
+ memcpy(&priv->encr_keys[index], key, key_len);
+
+ return 0;
+}
+
+
+/* remove_key is for both wep and wpa */
+static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ struct NDIS_802_11_REMOVE_KEY remove_key;
+ __le32 keyindex;
+ int ret;
+
+ if (priv->encr_key_len[index] == 0)
+ return 0;
+
+ priv->encr_key_len[index] = 0;
+ memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index]));
+
+ if (priv->wpa_cipher_pair == IW_AUTH_CIPHER_TKIP ||
+ priv->wpa_cipher_pair == IW_AUTH_CIPHER_CCMP ||
+ priv->wpa_cipher_group == IW_AUTH_CIPHER_TKIP ||
+ priv->wpa_cipher_group == IW_AUTH_CIPHER_CCMP) {
+ remove_key.Length = cpu_to_le32(sizeof(remove_key));
+ remove_key.KeyIndex = cpu_to_le32(index);
+ if (bssid) {
+ /* pairwise key */
+ if (memcmp(bssid, ffff_bssid, ETH_ALEN) != 0)
+ remove_key.KeyIndex |= cpu_to_le32(1 << 30);
+ memcpy(remove_key.Bssid, bssid,
+ sizeof(remove_key.Bssid));
+ } else
+ memset(remove_key.Bssid, 0xff,
+ sizeof(remove_key.Bssid));
+
+ ret = rndis_set_oid(usbdev, OID_802_11_REMOVE_KEY, &remove_key,
+ sizeof(remove_key));
+ if (ret != 0)
+ return ret;
+ } else {
+ keyindex = cpu_to_le32(index);
+ ret = rndis_set_oid(usbdev, OID_802_11_REMOVE_WEP, &keyindex,
+ sizeof(keyindex));
+ if (ret != 0) {
+ devwarn(usbdev,
+ "removing encryption key %d failed (%08X)",
+ index, ret);
+ return ret;
+ }
+ }
+
+ /* if it is transmit key, disable encryption */
+ if (index == priv->encr_tx_key_index)
+ set_encr_mode(usbdev, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE);
+
+ return 0;
+}
+
+
+static void set_multicast_list(struct usbnet *usbdev)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ struct dev_mc_list *mclist;
+ __le32 filter;
+ int ret, i, size;
+ char *buf;
+
+ filter = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST;
+
+ if (usbdev->net->flags & IFF_PROMISC) {
+ filter |= RNDIS_PACKET_TYPE_PROMISCUOUS |
+ RNDIS_PACKET_TYPE_ALL_LOCAL;
+ } else if (usbdev->net->flags & IFF_ALLMULTI ||
+ usbdev->net->mc_count > priv->multicast_size) {
+ filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
+ } else if (usbdev->net->mc_count > 0) {
+ size = min(priv->multicast_size, usbdev->net->mc_count);
+ buf = kmalloc(size * ETH_ALEN, GFP_KERNEL);
+ if (!buf) {
+ devwarn(usbdev,
+ "couldn't alloc %d bytes of memory",
+ size * ETH_ALEN);
+ return;
+ }
+
+ mclist = usbdev->net->mc_list;
+ for (i = 0; i < size && mclist; mclist = mclist->next) {
+ if (mclist->dmi_addrlen != ETH_ALEN)
+ continue;
+
+ memcpy(buf + i * ETH_ALEN, mclist->dmi_addr, ETH_ALEN);
+ i++;
+ }
+
+ ret = rndis_set_oid(usbdev, OID_802_3_MULTICAST_LIST, buf,
+ i * ETH_ALEN);
+ if (ret == 0 && i > 0)
+ filter |= RNDIS_PACKET_TYPE_MULTICAST;
+ else
+ filter |= RNDIS_PACKET_TYPE_ALL_MULTICAST;
+
+ devdbg(usbdev, "OID_802_3_MULTICAST_LIST(%d, max: %d) -> %d",
+ i, priv->multicast_size, ret);
+
+ kfree(buf);
+ }
+
+ ret = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter,
+ sizeof(filter));
+ if (ret < 0) {
+ devwarn(usbdev, "couldn't set packet filter: %08x",
+ le32_to_cpu(filter));
+ }
+
+ devdbg(usbdev, "OID_GEN_CURRENT_PACKET_FILTER(%08x) -> %d",
+ le32_to_cpu(filter), ret);
+}
+
+
+/*
+ * wireless extension handlers
+ */
+
+static int rndis_iw_commit(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ /* dummy op */
+ return 0;
+}
+
+
+static int rndis_iw_get_range(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct iw_range *range = (struct iw_range *)extra;
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ int len, ret, i, j, num, has_80211g_rates;
+ u8 rates[8];
+ __le32 tx_power;
+
+ devdbg(usbdev, "SIOCGIWRANGE");
+
+ /* clear iw_range struct */
+ memset(range, 0, sizeof(*range));
+ wrqu->data.length = sizeof(*range);
+
+ range->txpower_capa = IW_TXPOW_MWATT;
+ range->num_txpower = 1;
+ if (priv->caps & CAP_SUPPORT_TXPOWER) {
+ len = sizeof(tx_power);
+ ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+ &tx_power, &len);
+ if (ret == 0 && le32_to_cpu(tx_power) != 0xFF)
+ range->txpower[0] = le32_to_cpu(tx_power);
+ else
+ range->txpower[0] = get_bcm4320_power(priv);
+ } else
+ range->txpower[0] = get_bcm4320_power(priv);
+
+ len = sizeof(rates);
+ ret = rndis_query_oid(usbdev, OID_802_11_SUPPORTED_RATES, &rates,
+ &len);
+ has_80211g_rates = 0;
+ if (ret == 0) {
+ j = 0;
+ for (i = 0; i < len; i++) {
+ if (rates[i] == 0)
+ break;
+ range->bitrate[j] = (rates[i] & 0x7f) * 500000;
+ /* check for non 802.11b rates */
+ if (range->bitrate[j] == 6000000 ||
+ range->bitrate[j] == 9000000 ||
+ (range->bitrate[j] >= 12000000 &&
+ range->bitrate[j] != 22000000))
+ has_80211g_rates = 1;
+ j++;
+ }
+ range->num_bitrates = j;
+ } else
+ range->num_bitrates = 0;
+
+ /* fill in 802.11g rates */
+ if (has_80211g_rates) {
+ num = range->num_bitrates;
+ for (i = 0; i < sizeof(rates_80211g); i++) {
+ for (j = 0; j < num; j++) {
+ if (range->bitrate[j] ==
+ rates_80211g[i] * 1000000)
+ break;
+ }
+ if (j == num)
+ range->bitrate[range->num_bitrates++] =
+ rates_80211g[i] * 1000000;
+ if (range->num_bitrates == IW_MAX_BITRATES)
+ break;
+ }
+
+ /* estimated max real througput in bps */
+ range->throughput = 54 * 1000 * 1000 / 2;
+
+ /* ~35% more with afterburner */
+ if (priv->param_afterburner)
+ range->throughput = range->throughput / 100 * 135;
+ } else {
+ /* estimated max real througput in bps */
+ range->throughput = 11 * 1000 * 1000 / 2;
+ }
+
+ range->num_channels = (sizeof(freq_chan)/sizeof(freq_chan[0]));
+
+ for (i = 0; i < (sizeof(freq_chan)/sizeof(freq_chan[0])) &&
+ i < IW_MAX_FREQUENCIES; i++) {
+ range->freq[i].i = i + 1;
+ range->freq[i].m = freq_chan[i] * 100000;
+ range->freq[i].e = 1;
+ }
+ range->num_frequency = i;
+
+ range->min_rts = 0;
+ range->max_rts = 2347;
+ range->min_frag = 256;
+ range->max_frag = 2346;
+
+ range->max_qual.qual = 100;
+ range->max_qual.level = 154;
+ range->max_qual.updated = IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_NOISE_INVALID;
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = WIRELESS_EXT;
+
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+ return 0;
+}
+
+
+static int rndis_iw_get_name(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ strcpy(wrqu->name, priv->name);
+ return 0;
+}
+
+
+static int rndis_iw_set_essid(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
+{
+ struct NDIS_802_11_SSID ssid;
+ int length = wrqu->essid.length;
+ struct usbnet *usbdev = dev->priv;
+
+ devdbg(usbdev, "SIOCSIWESSID: [flags:%d,len:%d] '%.32s'",
+ wrqu->essid.flags, wrqu->essid.length, essid);
+
+ if (length > NDIS_802_11_LENGTH_SSID)
+ length = NDIS_802_11_LENGTH_SSID;
+
+ ssid.SsidLength = cpu_to_le32(length);
+ if (length > 0)
+ memcpy(ssid.Ssid, essid, length);
+ else
+ memset(ssid.Ssid, 0, NDIS_802_11_LENGTH_SSID);
+
+ set_assoc_params(usbdev);
+
+ if (!wrqu->essid.flags || length == 0)
+ return disassociate(usbdev, 1);
+ else
+ return set_essid(usbdev, &ssid);
+}
+
+
+static int rndis_iw_get_essid(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *essid)
+{
+ struct NDIS_802_11_SSID ssid;
+ struct usbnet *usbdev = dev->priv;
+ int ret;
+
+ ret = get_essid(usbdev, &ssid);
+
+ if (ret == 0 && le32_to_cpu(ssid.SsidLength) > 0) {
+ wrqu->essid.flags = 1;
+ wrqu->essid.length = le32_to_cpu(ssid.SsidLength);
+ memcpy(essid, ssid.Ssid, wrqu->essid.length);
+ essid[wrqu->essid.length] = 0;
+ } else {
+ memset(essid, 0, sizeof(NDIS_802_11_LENGTH_SSID));
+ wrqu->essid.flags = 0;
+ wrqu->essid.length = 0;
+ }
+ devdbg(usbdev, "SIOCGIWESSID: %s", essid);
+ return ret;
+}
+
+
+static int rndis_iw_get_bssid(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ unsigned char bssid[ETH_ALEN];
+ int ret;
+ DECLARE_MAC_BUF(mac);
+
+ ret = get_bssid(usbdev, bssid);
+
+ if (ret == 0)
+ devdbg(usbdev, "SIOCGIWAP: %s", print_mac(mac, bssid));
+ else
+ devdbg(usbdev, "SIOCGIWAP: <not associated>");
+
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wrqu->ap_addr.sa_data, bssid, ETH_ALEN);
+
+ return ret;
+}
+
+
+static int rndis_iw_set_bssid(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ u8 *bssid = (u8 *)wrqu->ap_addr.sa_data;
+ DECLARE_MAC_BUF(mac);
+ int ret;
+
+ devdbg(usbdev, "SIOCSIWAP: %s", print_mac(mac, bssid));
+
+ ret = rndis_set_oid(usbdev, OID_802_11_BSSID, bssid, ETH_ALEN);
+
+ /* user apps may set ap's mac address, which is not required;
+ * they may fail to work if this function fails, so return
+ * success */
+ if (ret)
+ devwarn(usbdev, "setting AP mac address failed (%08X)", ret);
+
+ return 0;
+}
+
+
+static int rndis_iw_set_auth(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct iw_param *p = &wrqu->param;
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ int ret = -ENOTSUPP;
+
+ switch (p->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ devdbg(usbdev, "SIOCSIWAUTH: WPA_VERSION, %08x", p->value);
+ priv->wpa_version = p->value;
+ ret = 0;
+ break;
+
+ case IW_AUTH_CIPHER_PAIRWISE:
+ devdbg(usbdev, "SIOCSIWAUTH: CIPHER_PAIRWISE, %08x", p->value);
+ priv->wpa_cipher_pair = p->value;
+ ret = 0;
+ break;
+
+ case IW_AUTH_CIPHER_GROUP:
+ devdbg(usbdev, "SIOCSIWAUTH: CIPHER_GROUP, %08x", p->value);
+ priv->wpa_cipher_group = p->value;
+ ret = 0;
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+ devdbg(usbdev, "SIOCSIWAUTH: KEY_MGMT, %08x", p->value);
+ priv->wpa_keymgmt = p->value;
+ ret = 0;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ devdbg(usbdev, "SIOCSIWAUTH: TKIP_COUNTERMEASURES, %08x",
+ p->value);
+ ret = 0;
+ break;
+
+ case IW_AUTH_DROP_UNENCRYPTED:
+ devdbg(usbdev, "SIOCSIWAUTH: DROP_UNENCRYPTED, %08x", p->value);
+ ret = 0;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ devdbg(usbdev, "SIOCSIWAUTH: 80211_AUTH_ALG, %08x", p->value);
+ priv->wpa_authalg = p->value;
+ ret = 0;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ devdbg(usbdev, "SIOCSIWAUTH: WPA_ENABLED, %08x", p->value);
+ if (wrqu->param.value)
+ deauthenticate(usbdev);
+ ret = 0;
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ devdbg(usbdev, "SIOCSIWAUTH: RX_UNENCRYPTED_EAPOL, %08x",
+ p->value);
+ ret = 0;
+ break;
+
+ case IW_AUTH_ROAMING_CONTROL:
+ devdbg(usbdev, "SIOCSIWAUTH: ROAMING_CONTROL, %08x", p->value);
+ ret = 0;
+ break;
+
+ case IW_AUTH_PRIVACY_INVOKED:
+ devdbg(usbdev, "SIOCSIWAUTH: invalid cmd %d",
+ wrqu->param.flags & IW_AUTH_INDEX);
+ return -EOPNOTSUPP;
+
+ default:
+ devdbg(usbdev, "SIOCSIWAUTH: UNKNOWN %08x, %08x",
+ p->flags & IW_AUTH_INDEX, p->value);
+ }
+ return ret;
+}
+
+
+static int rndis_iw_get_auth(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct iw_param *p = &wrqu->param;
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ switch (p->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ p->value = priv->wpa_version;
+ break;
+ case IW_AUTH_CIPHER_PAIRWISE:
+ p->value = priv->wpa_cipher_pair;
+ break;
+ case IW_AUTH_CIPHER_GROUP:
+ p->value = priv->wpa_cipher_group;
+ break;
+ case IW_AUTH_KEY_MGMT:
+ p->value = priv->wpa_keymgmt;
+ break;
+ case IW_AUTH_80211_AUTH_ALG:
+ p->value = priv->wpa_authalg;
+ break;
+ default:
+ devdbg(usbdev, "SIOCGIWAUTH: invalid cmd %d",
+ wrqu->param.flags & IW_AUTH_INDEX);
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+static int rndis_iw_get_mode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ switch (priv->infra_mode) {
+ case Ndis802_11IBSS:
+ wrqu->mode = IW_MODE_ADHOC;
+ break;
+ case Ndis802_11Infrastructure:
+ wrqu->mode = IW_MODE_INFRA;
+ break;
+ /*case Ndis802_11AutoUnknown:*/
+ default:
+ wrqu->mode = IW_MODE_AUTO;
+ break;
+ }
+ devdbg(usbdev, "SIOCGIWMODE: %08x", wrqu->mode);
+ return 0;
+}
+
+
+static int rndis_iw_set_mode(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ int mode;
+
+ devdbg(usbdev, "SIOCSIWMODE: %08x", wrqu->mode);
+
+ switch (wrqu->mode) {
+ case IW_MODE_ADHOC:
+ mode = Ndis802_11IBSS;
+ break;
+ case IW_MODE_INFRA:
+ mode = Ndis802_11Infrastructure;
+ break;
+ /*case IW_MODE_AUTO:*/
+ default:
+ mode = Ndis802_11AutoUnknown;
+ break;
+ }
+
+ return set_infra_mode(usbdev, mode);
+}
+
+
+static int rndis_iw_set_encode(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ int ret, index, key_len;
+ u8 *key;
+
+ index = (wrqu->encoding.flags & IW_ENCODE_INDEX);
+
+ /* iwconfig gives index as 1 - N */
+ if (index > 0)
+ index--;
+ else
+ index = priv->encr_tx_key_index;
+
+ if (index < 0 || index >= 4) {
+ devwarn(usbdev, "encryption index out of range (%u)", index);
+ return -EINVAL;
+ }
+
+ /* remove key if disabled */
+ if (wrqu->data.flags & IW_ENCODE_DISABLED) {
+ if (remove_key(usbdev, index, NULL))
+ return -EINVAL;
+ else
+ return 0;
+ }
+
+ /* global encryption state (for all keys) */
+ if (wrqu->data.flags & IW_ENCODE_OPEN)
+ ret = set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED,
+ IW_AUTH_ALG_OPEN_SYSTEM);
+ else /*if (wrqu->data.flags & IW_ENCODE_RESTRICTED)*/
+ ret = set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED,
+ IW_AUTH_ALG_SHARED_KEY);
+ if (ret != 0)
+ return ret;
+
+ if (wrqu->data.length > 0) {
+ key_len = wrqu->data.length;
+ key = extra;
+ } else {
+ /* must be set as tx key */
+ if (priv->encr_key_len[index] == 0)
+ return -EINVAL;
+ key_len = priv->encr_key_len[index];
+ key = priv->encr_keys[index];
+ priv->encr_tx_key_index = index;
+ }
+
+ if (add_wep_key(usbdev, key, key_len, index) != 0)
+ return -EINVAL;
+
+ if (index == priv->encr_tx_key_index)
+ /* ndis drivers want essid to be set after setting encr */
+ set_essid(usbdev, &priv->essid);
+
+ return 0;
+}
+
+
+static int rndis_iw_set_encode_ext(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ struct NDIS_802_11_KEY ndis_key;
+ int i, keyidx, ret;
+ u8 *addr;
+
+ keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX;
+
+ /* iwconfig gives index as 1 - N */
+ if (keyidx)
+ keyidx--;
+ else
+ keyidx = priv->encr_tx_key_index;
+
+ if (keyidx < 0 || keyidx >= 4)
+ return -EINVAL;
+
+ if (ext->alg == WPA_ALG_WEP) {
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+ priv->encr_tx_key_index = keyidx;
+ return add_wep_key(usbdev, ext->key, ext->key_len, keyidx);
+ }
+
+ if ((wrqu->encoding.flags & IW_ENCODE_DISABLED) ||
+ ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0)
+ return remove_key(usbdev, keyidx, NULL);
+
+ if (ext->key_len > sizeof(ndis_key.KeyMaterial))
+ return -1;
+
+ memset(&ndis_key, 0, sizeof(ndis_key));
+
+ ndis_key.Length = cpu_to_le32(sizeof(ndis_key) -
+ sizeof(ndis_key.KeyMaterial) + ext->key_len);
+ ndis_key.KeyLength = cpu_to_le32(ext->key_len);
+ ndis_key.KeyIndex = cpu_to_le32(keyidx);
+
+ if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
+ for (i = 0; i < 6; i++)
+ ndis_key.KeyRSC |=
+ cpu_to_le64(ext->rx_seq[i] << (i * 8));
+ ndis_key.KeyIndex |= cpu_to_le32(1 << 29);
+ }
+
+ addr = ext->addr.sa_data;
+ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+ /* group key */
+ if (priv->infra_mode == Ndis802_11IBSS)
+ memset(ndis_key.Bssid, 0xff, ETH_ALEN);
+ else
+ get_bssid(usbdev, ndis_key.Bssid);
+ } else {
+ /* pairwise key */
+ ndis_key.KeyIndex |= cpu_to_le32(1 << 30);
+ memcpy(ndis_key.Bssid, addr, ETH_ALEN);
+ }
+
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+ ndis_key.KeyIndex |= cpu_to_le32(1 << 31);
+
+ if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) {
+ /* wpa_supplicant gives us the Michael MIC RX/TX keys in
+ * different order than NDIS spec, so swap the order here. */
+ memcpy(ndis_key.KeyMaterial, ext->key, 16);
+ memcpy(ndis_key.KeyMaterial + 16, ext->key + 24, 8);
+ memcpy(ndis_key.KeyMaterial + 24, ext->key + 16, 8);
+ } else
+ memcpy(ndis_key.KeyMaterial, ext->key, ext->key_len);
+
+ ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &ndis_key,
+ le32_to_cpu(ndis_key.Length));
+ devdbg(usbdev, "SIOCSIWENCODEEXT: OID_802_11_ADD_KEY -> %08X", ret);
+ if (ret != 0)
+ return ret;
+
+ priv->encr_key_len[keyidx] = ext->key_len;
+ memcpy(&priv->encr_keys[keyidx], ndis_key.KeyMaterial, ext->key_len);
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+ priv->encr_tx_key_index = keyidx;
+
+ return 0;
+}
+
+
+static int rndis_iw_set_scan(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct iw_param *param = &wrqu->param;
+ struct usbnet *usbdev = dev->priv;
+ union iwreq_data evt;
+ int ret = -EINVAL;
+ __le32 tmp;
+
+ devdbg(usbdev, "SIOCSIWSCAN");
+
+ if (param->flags == 0) {
+ tmp = ccpu2(1);
+ ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
+ sizeof(tmp));
+ evt.data.flags = 0;
+ evt.data.length = 0;
+ wireless_send_event(dev, SIOCGIWSCAN, &evt, NULL);
+ }
+ return ret;
+}
+
+
+static char *rndis_translate_scan(struct net_device *dev,
+ char *cev, char *end_buf, struct NDIS_WLAN_BSSID_EX *bssid)
+{
+#ifdef DEBUG
+ struct usbnet *usbdev = dev->priv;
+#endif
+ struct ieee80211_info_element *ie;
+ char *current_val;
+ int bssid_len, ie_len, i;
+ u32 beacon, atim;
+ struct iw_event iwe;
+ unsigned char sbuf[32];
+ DECLARE_MAC_BUF(mac);
+
+ bssid_len = le32_to_cpu(bssid->Length);
+
+ devdbg(usbdev, "BSSID %s", print_mac(mac, bssid->MacAddress));
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bssid->MacAddress, ETH_ALEN);
+ cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+ devdbg(usbdev, "SSID(%d) %s",
+ le32_to_cpu(bssid->Ssid.SsidLength),
+ bssid->Ssid.Ssid);
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.essid.length = le32_to_cpu(bssid->Ssid.SsidLength);
+ iwe.u.essid.flags = 1;
+ cev = iwe_stream_add_point(cev, end_buf, &iwe,
+ bssid->Ssid.Ssid);
+
+ devdbg(usbdev, "MODE %d",
+ le32_to_cpu(bssid->InfrastructureMode));
+ iwe.cmd = SIOCGIWMODE;
+ switch (le32_to_cpu(bssid->InfrastructureMode)) {
+ case Ndis802_11IBSS:
+ iwe.u.mode = IW_MODE_ADHOC;
+ break;
+ case Ndis802_11Infrastructure:
+ iwe.u.mode = IW_MODE_INFRA;
+ break;
+ /*case Ndis802_11AutoUnknown:*/
+ default:
+ iwe.u.mode = IW_MODE_AUTO;
+ break;
+ }
+ cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN);
+
+ devdbg(usbdev, "FREQ %d kHz",
+ le32_to_cpu(bssid->Configuration.DSConfig));
+ iwe.cmd = SIOCGIWFREQ;
+ dsconfig_to_freq(le32_to_cpu(bssid->Configuration.DSConfig),
+ &iwe.u.freq);
+ cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN);
+
+ devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->Rssi));
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.qual = level_to_qual(le32_to_cpu(bssid->Rssi));
+ iwe.u.qual.level = le32_to_cpu(bssid->Rssi);
+ iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_NOISE_INVALID;
+ cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN);
+
+ devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->Privacy));
+ iwe.cmd = SIOCGIWENCODE;
+ iwe.u.data.length = 0;
+ if (le32_to_cpu(bssid->Privacy) == Ndis802_11PrivFilterAcceptAll)
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ else
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+
+ cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL);
+
+ devdbg(usbdev, "RATES:");
+ current_val = cev + IW_EV_LCP_LEN;
+ iwe.cmd = SIOCGIWRATE;
+ for (i = 0; i < sizeof(bssid->SupportedRates); i++) {
+ if (bssid->SupportedRates[i] & 0x7f) {
+ iwe.u.bitrate.value =
+ ((bssid->SupportedRates[i] & 0x7f) *
+ 500000);
+ devdbg(usbdev, " %d", iwe.u.bitrate.value);
+ current_val = iwe_stream_add_value(cev,
+ current_val, end_buf, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ }
+
+ if ((current_val - cev) > IW_EV_LCP_LEN)
+ cev = current_val;
+
+ beacon = le32_to_cpu(bssid->Configuration.BeaconPeriod);
+ devdbg(usbdev, "BCN_INT %d", beacon);
+ iwe.cmd = IWEVCUSTOM;
+ snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon);
+ iwe.u.data.length = strlen(sbuf);
+ cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf);
+
+ atim = le32_to_cpu(bssid->Configuration.ATIMWindow);
+ devdbg(usbdev, "ATIM %d", atim);
+ iwe.cmd = IWEVCUSTOM;
+ snprintf(sbuf, sizeof(sbuf), "atim=%u", atim);
+ iwe.u.data.length = strlen(sbuf);
+ cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf);
+
+ ie = (void *)(bssid->IEs + sizeof(struct NDIS_802_11_FIXED_IEs));
+ ie_len = min(bssid_len - (int)sizeof(*bssid),
+ (int)le32_to_cpu(bssid->IELength));
+ ie_len -= sizeof(struct NDIS_802_11_FIXED_IEs);
+ while (ie_len >= sizeof(*ie) && sizeof(*ie) + ie->len <= ie_len) {
+ if ((ie->id == MFIE_TYPE_GENERIC && ie->len >= 4 &&
+ memcmp(ie->data, "\x00\x50\xf2\x01", 4) == 0) ||
+ ie->id == MFIE_TYPE_RSN) {
+ devdbg(usbdev, "IE: WPA%d",
+ (ie->id == MFIE_TYPE_RSN) ? 2 : 1);
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = min(ie->len + 2, MAX_WPA_IE_LEN);
+ cev = iwe_stream_add_point(cev, end_buf, &iwe,
+ (u8 *)ie);
+ }
+
+ ie_len -= sizeof(*ie) + ie->len;
+ ie = (struct ieee80211_info_element *)&ie->data[ie->len];
+ }
+
+ return cev;
+}
+
+
+static int rndis_iw_get_scan(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ void *buf = NULL;
+ char *cev = extra;
+ struct NDIS_802_11_BSSID_LIST_EX *bssid_list;
+ struct NDIS_WLAN_BSSID_EX *bssid;
+ int ret = -EINVAL, len, count, bssid_len;
+
+ devdbg(usbdev, "SIOCGIWSCAN");
+
+ len = CONTROL_BUFFER_SIZE;
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
+
+ if (ret != 0)
+ goto out;
+
+ bssid_list = buf;
+ bssid = bssid_list->Bssid;
+ bssid_len = le32_to_cpu(bssid->Length);
+ count = le32_to_cpu(bssid_list->NumberOfItems);
+ devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count);
+
+ while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
+ cev = rndis_translate_scan(dev, cev, extra + IW_SCAN_MAX_DATA,
+ bssid);
+ bssid = (void *)bssid + bssid_len;
+ bssid_len = le32_to_cpu(bssid->Length);
+ count--;
+ }
+
+out:
+ wrqu->data.length = cev - extra;
+ wrqu->data.flags = 0;
+ kfree(buf);
+ return ret;
+}
+
+
+static int rndis_iw_set_genie(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ int ret = 0;
+
+#ifdef DEBUG
+ int j;
+ u8 *gie = extra;
+ for (j = 0; j < wrqu->data.length; j += 8)
+ devdbg(usbdev,
+ "SIOCSIWGENIE %04x - "
+ "%02x %02x %02x %02x %02x %02x %02x %02x", j,
+ gie[j + 0], gie[j + 1], gie[j + 2], gie[j + 3],
+ gie[j + 4], gie[j + 5], gie[j + 6], gie[j + 7]);
+#endif
+ /* clear existing IEs */
+ if (priv->wpa_ie_len) {
+ kfree(priv->wpa_ie);
+ priv->wpa_ie_len = 0;
+ }
+
+ /* set new IEs */
+ priv->wpa_ie = kmalloc(wrqu->data.length, GFP_KERNEL);
+ if (priv->wpa_ie) {
+ priv->wpa_ie_len = wrqu->data.length;
+ memcpy(priv->wpa_ie, extra, priv->wpa_ie_len);
+ } else
+ ret = -ENOMEM;
+ return ret;
+}
+
+
+static int rndis_iw_get_genie(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ devdbg(usbdev, "SIOCGIWGENIE");
+
+ if (priv->wpa_ie_len == 0 || priv->wpa_ie == NULL) {
+ wrqu->data.length = 0;
+ return 0;
+ }
+
+ if (wrqu->data.length < priv->wpa_ie_len)
+ return -E2BIG;
+
+ wrqu->data.length = priv->wpa_ie_len;
+ memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
+
+ return 0;
+}
+
+
+static int rndis_iw_set_rts(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ __le32 tmp;
+ devdbg(usbdev, "SIOCSIWRTS");
+
+ tmp = cpu_to_le32(wrqu->rts.value);
+ return rndis_set_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp,
+ sizeof(tmp));
+}
+
+
+static int rndis_iw_get_rts(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ __le32 tmp;
+ int len, ret;
+
+ len = sizeof(tmp);
+ ret = rndis_query_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, &len);
+ if (ret == 0) {
+ wrqu->rts.value = le32_to_cpu(tmp);
+ wrqu->rts.flags = 1;
+ wrqu->rts.disabled = 0;
+ }
+
+ devdbg(usbdev, "SIOCGIWRTS: %d", wrqu->rts.value);
+
+ return ret;
+}
+
+
+static int rndis_iw_set_frag(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ __le32 tmp;
+
+ devdbg(usbdev, "SIOCSIWFRAG");
+
+ tmp = cpu_to_le32(wrqu->frag.value);
+ return rndis_set_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp,
+ sizeof(tmp));
+}
+
+
+static int rndis_iw_get_frag(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ __le32 tmp;
+ int len, ret;
+
+ len = sizeof(tmp);
+ ret = rndis_query_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp,
+ &len);
+ if (ret == 0) {
+ wrqu->frag.value = le32_to_cpu(tmp);
+ wrqu->frag.flags = 1;
+ wrqu->frag.disabled = 0;
+ }
+ devdbg(usbdev, "SIOCGIWFRAG: %d", wrqu->frag.value);
+ return ret;
+}
+
+
+static int rndis_iw_set_nick(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ devdbg(usbdev, "SIOCSIWNICK");
+
+ priv->nick_len = wrqu->data.length;
+ if (priv->nick_len > 32)
+ priv->nick_len = 32;
+
+ memcpy(priv->nick, extra, priv->nick_len);
+ return 0;
+}
+
+
+static int rndis_iw_get_nick(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ wrqu->data.flags = 1;
+ wrqu->data.length = priv->nick_len;
+ memcpy(extra, priv->nick, priv->nick_len);
+
+ devdbg(usbdev, "SIOCGIWNICK: '%s'", priv->nick);
+
+ return 0;
+}
+
+
+static int rndis_iw_set_freq(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct NDIS_802_11_CONFIGURATION config;
+ unsigned int dsconfig;
+ int len, ret;
+
+ /* this OID is valid only when not associated */
+ if (is_associated(usbdev))
+ return 0;
+
+ dsconfig = 0;
+ if (freq_to_dsconfig(&wrqu->freq, &dsconfig))
+ return -EINVAL;
+
+ len = sizeof(config);
+ ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
+ if (ret != 0) {
+ devdbg(usbdev, "SIOCSIWFREQ: querying configuration failed");
+ return 0;
+ }
+
+ config.DSConfig = cpu_to_le32(dsconfig);
+
+ devdbg(usbdev, "SIOCSIWFREQ: %d * 10^%d", wrqu->freq.m, wrqu->freq.e);
+ return rndis_set_oid(usbdev, OID_802_11_CONFIGURATION, &config,
+ sizeof(config));
+}
+
+
+static int rndis_iw_get_freq(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct NDIS_802_11_CONFIGURATION config;
+ int len, ret;
+
+ len = sizeof(config);
+ ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
+ if (ret == 0)
+ dsconfig_to_freq(le32_to_cpu(config.DSConfig), &wrqu->freq);
+
+ devdbg(usbdev, "SIOCGIWFREQ: %d", wrqu->freq.m);
+ return ret;
+}
+
+
+static int rndis_iw_get_txpower(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ __le32 tx_power;
+ int ret = 0, len;
+
+ if (priv->radio_on) {
+ if (priv->caps & CAP_SUPPORT_TXPOWER) {
+ len = sizeof(tx_power);
+ ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+ &tx_power, &len);
+ if (ret != 0)
+ return ret;
+ } else
+ /* fake incase not supported */
+ tx_power = cpu_to_le32(get_bcm4320_power(priv));
+
+ wrqu->txpower.flags = IW_TXPOW_MWATT;
+ wrqu->txpower.value = le32_to_cpu(tx_power);
+ wrqu->txpower.disabled = 0;
+ } else {
+ wrqu->txpower.flags = IW_TXPOW_MWATT;
+ wrqu->txpower.value = 0;
+ wrqu->txpower.disabled = 1;
+ }
+
+ devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value);
+
+ return ret;
+}
+
+
+static int rndis_iw_set_txpower(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ __le32 tx_power = 0;
+ int ret = 0;
+
+ if (!wrqu->txpower.disabled) {
+ if (wrqu->txpower.flags == IW_TXPOW_MWATT)
+ tx_power = cpu_to_le32(wrqu->txpower.value);
+ else { /* wrqu->txpower.flags == IW_TXPOW_DBM */
+ if (wrqu->txpower.value > 20)
+ tx_power = cpu_to_le32(128);
+ else if (wrqu->txpower.value < -43)
+ tx_power = cpu_to_le32(127);
+ else {
+ signed char tmp;
+ tmp = wrqu->txpower.value;
+ tmp = -12 - tmp;
+ tmp <<= 2;
+ tx_power = cpu_to_le32((unsigned char)tmp);
+ }
+ }
+ }
+
+ devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power));
+
+ if (le32_to_cpu(tx_power) != 0) {
+ if (priv->caps & CAP_SUPPORT_TXPOWER) {
+ /* turn radio on first */
+ if (!priv->radio_on)
+ disassociate(usbdev, 1);
+
+ ret = rndis_set_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
+ &tx_power, sizeof(tx_power));
+ if (ret != 0)
+ ret = -EOPNOTSUPP;
+ return ret;
+ } else {
+ /* txpower unsupported, just turn radio on */
+ if (!priv->radio_on)
+ return disassociate(usbdev, 1);
+ return 0; /* all ready on */
+ }
+ }
+
+ /* tx_power == 0, turn off radio */
+ return disassociate(usbdev, 0);
+}
+
+
+static int rndis_iw_get_rate(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ __le32 tmp;
+ int ret, len;
+
+ len = sizeof(tmp);
+ ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &tmp, &len);
+ if (ret == 0) {
+ wrqu->bitrate.value = le32_to_cpu(tmp) * 100;
+ wrqu->bitrate.disabled = 0;
+ wrqu->bitrate.flags = 1;
+ }
+ return ret;
+}
+
+
+static int rndis_iw_set_mlme(struct net_device *dev,
+ struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ unsigned char bssid[ETH_ALEN];
+
+ get_bssid(usbdev, bssid);
+
+ if (memcmp(bssid, mlme->addr.sa_data, ETH_ALEN))
+ return -EINVAL;
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ return deauthenticate(usbdev);
+ case IW_MLME_DISASSOC:
+ return disassociate(usbdev, priv->radio_on);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+
+static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->stats_lock, flags);
+ memcpy(&priv->iwstats, &priv->privstats, sizeof(priv->iwstats));
+ spin_unlock_irqrestore(&priv->stats_lock, flags);
+
+ return &priv->iwstats;
+}
+
+
+#define IW_IOCTL(x) [(x) - SIOCSIWCOMMIT]
+static const iw_handler rndis_iw_handler[] =
+{
+ IW_IOCTL(SIOCSIWCOMMIT) = rndis_iw_commit,
+ IW_IOCTL(SIOCGIWNAME) = rndis_iw_get_name,
+ IW_IOCTL(SIOCSIWFREQ) = rndis_iw_set_freq,
+ IW_IOCTL(SIOCGIWFREQ) = rndis_iw_get_freq,
+ IW_IOCTL(SIOCSIWMODE) = rndis_iw_set_mode,
+ IW_IOCTL(SIOCGIWMODE) = rndis_iw_get_mode,
+ IW_IOCTL(SIOCGIWRANGE) = rndis_iw_get_range,
+ IW_IOCTL(SIOCSIWAP) = rndis_iw_set_bssid,
+ IW_IOCTL(SIOCGIWAP) = rndis_iw_get_bssid,
+ IW_IOCTL(SIOCSIWSCAN) = rndis_iw_set_scan,
+ IW_IOCTL(SIOCGIWSCAN) = rndis_iw_get_scan,
+ IW_IOCTL(SIOCSIWESSID) = rndis_iw_set_essid,
+ IW_IOCTL(SIOCGIWESSID) = rndis_iw_get_essid,
+ IW_IOCTL(SIOCSIWNICKN) = rndis_iw_set_nick,
+ IW_IOCTL(SIOCGIWNICKN) = rndis_iw_get_nick,
+ IW_IOCTL(SIOCGIWRATE) = rndis_iw_get_rate,
+ IW_IOCTL(SIOCSIWRTS) = rndis_iw_set_rts,
+ IW_IOCTL(SIOCGIWRTS) = rndis_iw_get_rts,
+ IW_IOCTL(SIOCSIWFRAG) = rndis_iw_set_frag,
+ IW_IOCTL(SIOCGIWFRAG) = rndis_iw_get_frag,
+ IW_IOCTL(SIOCSIWTXPOW) = rndis_iw_set_txpower,
+ IW_IOCTL(SIOCGIWTXPOW) = rndis_iw_get_txpower,
+ IW_IOCTL(SIOCSIWENCODE) = rndis_iw_set_encode,
+ IW_IOCTL(SIOCSIWENCODEEXT) = rndis_iw_set_encode_ext,
+ IW_IOCTL(SIOCSIWAUTH) = rndis_iw_set_auth,
+ IW_IOCTL(SIOCGIWAUTH) = rndis_iw_get_auth,
+ IW_IOCTL(SIOCSIWGENIE) = rndis_iw_set_genie,
+ IW_IOCTL(SIOCGIWGENIE) = rndis_iw_get_genie,
+ IW_IOCTL(SIOCSIWMLME) = rndis_iw_set_mlme,
+};
+
+static const iw_handler rndis_wext_private_handler[] = {
+};
+
+static const struct iw_priv_args rndis_wext_private_args[] = {
+};
+
+
+static const struct iw_handler_def rndis_iw_handlers = {
+ .num_standard = ARRAY_SIZE(rndis_iw_handler),
+ .num_private = ARRAY_SIZE(rndis_wext_private_handler),
+ .num_private_args = ARRAY_SIZE(rndis_wext_private_args),
+ .standard = (iw_handler *)rndis_iw_handler,
+ .private = (iw_handler *)rndis_wext_private_handler,
+ .private_args = (struct iw_priv_args *)rndis_wext_private_args,
+ .get_wireless_stats = rndis_get_wireless_stats,
+};
+
+
+static void rndis_wext_worker(struct work_struct *work)
+{
+ struct rndis_wext_private *priv =
+ container_of(work, struct rndis_wext_private, work);
+ struct usbnet *usbdev = priv->usbdev;
+ union iwreq_data evt;
+ unsigned char bssid[ETH_ALEN];
+ int ret;
+
+ if (test_and_clear_bit(WORK_CONNECTION_EVENT, &priv->work_pending)) {
+ ret = get_bssid(usbdev, bssid);
+
+ if (!ret) {
+ evt.data.flags = 0;
+ evt.data.length = 0;
+ memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN);
+ wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL);
+ }
+ }
+
+ if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending))
+ set_multicast_list(usbdev);
+}
+
+static void rndis_wext_set_multicast_list(struct net_device *dev)
+{
+ struct usbnet *usbdev = dev->priv;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
+
+ set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending);
+ queue_work(priv->workqueue, &priv->work);
+}
+
+static void rndis_wext_link_change(struct usbnet *dev, int state)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+ union iwreq_data evt;
+
+ if (state) {
+ /* queue work to avoid recursive calls into rndis_command */
+ set_bit(WORK_CONNECTION_EVENT, &priv->work_pending);
+ queue_work(priv->workqueue, &priv->work);
+ } else {
+ evt.data.flags = 0;
+ evt.data.length = 0;
+ memset(evt.ap_addr.sa_data, 0, ETH_ALEN);
+ wireless_send_event(dev->net, SIOCGIWAP, &evt, NULL);
+ }
+}
+
+
+static int rndis_wext_get_caps(struct usbnet *dev)
+{
+ struct {
+ __le32 num_items;
+ __le32 items[8];
+ } networks_supported;
+ int len, retval, i, n;
+ __le32 tx_power;
+ struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+
+ /* determine if supports setting txpower */
+ len = sizeof(tx_power);
+ retval = rndis_query_oid(dev, OID_802_11_TX_POWER_LEVEL, &tx_power,
+ &len);
+ if (retval == 0 && le32_to_cpu(tx_power) != 0xFF)
+ priv->caps |= CAP_SUPPORT_TXPOWER;
+
+ /* determine supported modes */
+ len = sizeof(networks_supported);
+ retval = rndis_query_oid(dev, OID_802_11_NETWORK_TYPES_SUPPORTED,
+ &networks_supported, &len);
+ if (retval >= 0) {
+ n = le32_to_cpu(networks_supported.num_items);
+ if (n > 8)
+ n = 8;
+ for (i = 0; i < n; i++) {
+ switch (le32_to_cpu(networks_supported.items[i])) {
+ case Ndis802_11FH:
+ case Ndis802_11DS:
+ priv->caps |= CAP_MODE_80211B;
+ break;
+ case Ndis802_11OFDM5:
+ priv->caps |= CAP_MODE_80211A;
+ break;
+ case Ndis802_11OFDM24:
+ priv->caps |= CAP_MODE_80211G;
+ break;
+ }
+ }
+ if (priv->caps & CAP_MODE_80211A)
+ strcat(priv->name, "a");
+ if (priv->caps & CAP_MODE_80211B)
+ strcat(priv->name, "b");
+ if (priv->caps & CAP_MODE_80211G)
+ strcat(priv->name, "g");
+ }
+
+ return retval;
+}
+
+
+#define STATS_UPDATE_JIFFIES (HZ)
+static void rndis_update_wireless_stats(struct work_struct *work)
+{
+ struct rndis_wext_private *priv =
+ container_of(work, struct rndis_wext_private, stats_work.work);
+ struct usbnet *usbdev = priv->usbdev;
+ struct iw_statistics iwstats;
+ __le32 rssi, tmp;
+ int len, ret, bitrate, j;
+ unsigned long flags;
+ int update_jiffies = STATS_UPDATE_JIFFIES;
+ void *buf;
+
+ spin_lock_irqsave(&priv->stats_lock, flags);
+ memcpy(&iwstats, &priv->privstats, sizeof(iwstats));
+ spin_unlock_irqrestore(&priv->stats_lock, flags);
+
+ /* only update stats when connected */
+ if (!is_associated(usbdev)) {
+ iwstats.qual.qual = 0;
+ iwstats.qual.level = 0;
+ iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_NOISE_INVALID
+ | IW_QUAL_QUAL_INVALID
+ | IW_QUAL_LEVEL_INVALID;
+ goto end;
+ }
+
+ len = sizeof(rssi);
+ ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len);
+
+ devdbg(usbdev, "stats: OID_802_11_RSSI -> %d, rssi:%d", ret,
+ le32_to_cpu(rssi));
+ if (ret == 0) {
+ memset(&iwstats.qual, 0, sizeof(iwstats.qual));
+ iwstats.qual.qual = level_to_qual(le32_to_cpu(rssi));
+ iwstats.qual.level = le32_to_cpu(rssi);
+ iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_NOISE_INVALID;
+ }
+
+ memset(&iwstats.discard, 0, sizeof(iwstats.discard));
+
+ len = sizeof(tmp);
+ ret = rndis_query_oid(usbdev, OID_GEN_XMIT_ERROR, &tmp, &len);
+ if (ret == 0)
+ iwstats.discard.misc += le32_to_cpu(tmp);
+
+ len = sizeof(tmp);
+ ret = rndis_query_oid(usbdev, OID_GEN_RCV_ERROR, &tmp, &len);
+ if (ret == 0)
+ iwstats.discard.misc += le32_to_cpu(tmp);
+
+ len = sizeof(tmp);
+ ret = rndis_query_oid(usbdev, OID_GEN_RCV_NO_BUFFER, &tmp, &len);
+ if (ret == 0)
+ iwstats.discard.misc += le32_to_cpu(tmp);
+
+ /* Workaround transfer stalls on poor quality links. */
+ len = sizeof(tmp);
+ ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &tmp, &len);
+ if (ret == 0) {
+ bitrate = le32_to_cpu(tmp) * 100;
+ if (bitrate > 11000000)
+ goto end;
+
+ /* Decrease stats worker interval to catch stalls.
+ * faster. Faster than 400-500ms causes packet loss,
+ * Slower doesn't catch stalls fast enough.
+ */
+ j = msecs_to_jiffies(priv->param_workaround_interval);
+ if (j > STATS_UPDATE_JIFFIES)
+ j = STATS_UPDATE_JIFFIES;
+ else if (j <= 0)
+ j = 1;
+ update_jiffies = j;
+
+ /* Send scan OID. Use of both OIDs is required to get device
+ * working.
+ */
+ tmp = ccpu2(1);
+ rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
+ sizeof(tmp));
+
+ len = CONTROL_BUFFER_SIZE;
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ goto end;
+
+ rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
+ kfree(buf);
+ }
+end:
+ spin_lock_irqsave(&priv->stats_lock, flags);
+ memcpy(&priv->privstats, &iwstats, sizeof(iwstats));
+ spin_unlock_irqrestore(&priv->stats_lock, flags);
+
+ if (update_jiffies >= HZ)
+ update_jiffies = round_jiffies_relative(update_jiffies);
+ else {
+ j = round_jiffies_relative(update_jiffies);
+ if (abs(j - update_jiffies) <= 10)
+ update_jiffies = j;
+ }
+
+ queue_delayed_work(priv->workqueue, &priv->stats_work, update_jiffies);
+}
+
+
+static int bcm4320_early_init(struct usbnet *dev)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+ char buf[8];
+
+ /* Early initialization settings, setting these won't have effect
+ * if called after generic_rndis_bind().
+ */
+
+ priv->param_country[0] = modparam_country[0];
+ priv->param_country[1] = modparam_country[1];
+ priv->param_country[2] = 0;
+ priv->param_frameburst = modparam_frameburst;
+ priv->param_afterburner = modparam_afterburner;
+ priv->param_power_save = modparam_power_save;
+ priv->param_power_output = modparam_power_output;
+ priv->param_roamtrigger = modparam_roamtrigger;
+ priv->param_roamdelta = modparam_roamdelta;
+ priv->param_workaround_interval = modparam_workaround_interval;
+
+ priv->param_country[0] = toupper(priv->param_country[0]);
+ priv->param_country[1] = toupper(priv->param_country[1]);
+ /* doesn't support EU as country code, use FI instead */
+ if (!strcmp(priv->param_country, "EU"))
+ strcpy(priv->param_country, "FI");
+
+ if (priv->param_power_save < 0)
+ priv->param_power_save = 0;
+ else if (priv->param_power_save > 2)
+ priv->param_power_save = 2;
+
+ if (priv->param_roamtrigger < -80)
+ priv->param_roamtrigger = -80;
+ else if (priv->param_roamtrigger > -60)
+ priv->param_roamtrigger = -60;
+
+ if (priv->param_roamdelta < 0)
+ priv->param_roamdelta = 0;
+ else if (priv->param_roamdelta > 2)
+ priv->param_roamdelta = 2;
+
+ if (priv->param_workaround_interval < 0)
+ priv->param_workaround_interval = 500;
+
+ rndis_set_config_parameter_str(dev, "Country", priv->param_country);
+ rndis_set_config_parameter_str(dev, "FrameBursting",
+ priv->param_frameburst ? "1" : "0");
+ rndis_set_config_parameter_str(dev, "Afterburner",
+ priv->param_afterburner ? "1" : "0");
+ sprintf(buf, "%d", priv->param_power_save);
+ rndis_set_config_parameter_str(dev, "PowerSaveMode", buf);
+ sprintf(buf, "%d", priv->param_power_output);
+ rndis_set_config_parameter_str(dev, "PwrOut", buf);
+ sprintf(buf, "%d", priv->param_roamtrigger);
+ rndis_set_config_parameter_str(dev, "RoamTrigger", buf);
+ sprintf(buf, "%d", priv->param_roamdelta);
+ rndis_set_config_parameter_str(dev, "RoamDelta", buf);
+
+ return 0;
+}
+
+
+static int rndis_wext_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct net_device *net = dev->net;
+ struct rndis_wext_private *priv;
+ int retval, len;
+ __le32 tmp;
+
+ /* allocate rndis private data */
+ priv = kmalloc(sizeof(struct rndis_wext_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* These have to be initialized before calling generic_rndis_bind().
+ * Otherwise we'll be in big trouble in rndis_wext_early_init().
+ */
+ dev->driver_priv = priv;
+ memset(priv, 0, sizeof(*priv));
+ memset(priv->name, 0, sizeof(priv->name));
+ strcpy(priv->name, "IEEE802.11");
+ net->wireless_handlers = &rndis_iw_handlers;
+ priv->usbdev = dev;
+
+ mutex_init(&priv->command_lock);
+ spin_lock_init(&priv->stats_lock);
+
+ /* try bind rndis_host */
+ retval = generic_rndis_bind(dev, intf, FLAG_RNDIS_PHYM_WIRELESS);
+ if (retval < 0)
+ goto fail;
+
+ /* generic_rndis_bind set packet filter to multicast_all+
+ * promisc mode which doesn't work well for our devices (device
+ * picks up rssi to closest station instead of to access point).
+ *
+ * rndis_host wants to avoid all OID as much as possible
+ * so do promisc/multicast handling in rndis_wext.
+ */
+ dev->net->set_multicast_list = rndis_wext_set_multicast_list;
+ tmp = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST;
+ retval = rndis_set_oid(dev, OID_GEN_CURRENT_PACKET_FILTER, &tmp,
+ sizeof(tmp));
+
+ len = sizeof(tmp);
+ retval = rndis_query_oid(dev, OID_802_3_MAXIMUM_LIST_SIZE, &tmp, &len);
+ priv->multicast_size = le32_to_cpu(tmp);
+ if (retval < 0 || priv->multicast_size < 0)
+ priv->multicast_size = 0;
+ if (priv->multicast_size > 0)
+ dev->net->flags |= IFF_MULTICAST;
+ else
+ dev->net->flags &= ~IFF_MULTICAST;
+
+ priv->iwstats.qual.qual = 0;
+ priv->iwstats.qual.level = 0;
+ priv->iwstats.qual.updated = IW_QUAL_QUAL_UPDATED
+ | IW_QUAL_LEVEL_UPDATED
+ | IW_QUAL_NOISE_INVALID
+ | IW_QUAL_QUAL_INVALID
+ | IW_QUAL_LEVEL_INVALID;
+
+ rndis_wext_get_caps(dev);
+ set_default_iw_params(dev);
+
+ /* turn radio on */
+ priv->radio_on = 1;
+ disassociate(dev, 1);
+
+ /* because rndis_command() sleeps we need to use workqueue */
+ priv->workqueue = create_singlethread_workqueue("rndis_wlan");
+ INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
+ queue_delayed_work(priv->workqueue, &priv->stats_work,
+ round_jiffies_relative(STATS_UPDATE_JIFFIES));
+ INIT_WORK(&priv->work, rndis_wext_worker);
+
+ return 0;
+
+fail:
+ kfree(priv);
+ return retval;
+}
+
+
+static void rndis_wext_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct rndis_wext_private *priv = get_rndis_wext_priv(dev);
+
+ /* turn radio off */
+ disassociate(dev, 0);
+
+ cancel_delayed_work_sync(&priv->stats_work);
+ cancel_work_sync(&priv->work);
+ flush_workqueue(priv->workqueue);
+ destroy_workqueue(priv->workqueue);
+
+ if (priv && priv->wpa_ie_len)
+ kfree(priv->wpa_ie);
+ kfree(priv);
+
+ rndis_unbind(dev, intf);
+}
+
+
+static int rndis_wext_reset(struct usbnet *dev)
+{
+ return deauthenticate(dev);
+}
+
+
+static const struct driver_info bcm4320b_info = {
+ .description = "Wireless RNDIS device, BCM4320b based",
+ .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .bind = rndis_wext_bind,
+ .unbind = rndis_wext_unbind,
+ .status = rndis_status,
+ .rx_fixup = rndis_rx_fixup,
+ .tx_fixup = rndis_tx_fixup,
+ .reset = rndis_wext_reset,
+ .early_init = bcm4320_early_init,
+ .link_change = rndis_wext_link_change,
+};
+
+static const struct driver_info bcm4320a_info = {
+ .description = "Wireless RNDIS device, BCM4320a based",
+ .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .bind = rndis_wext_bind,
+ .unbind = rndis_wext_unbind,
+ .status = rndis_status,
+ .rx_fixup = rndis_rx_fixup,
+ .tx_fixup = rndis_tx_fixup,
+ .reset = rndis_wext_reset,
+ .early_init = bcm4320_early_init,
+ .link_change = rndis_wext_link_change,
+};
+
+static const struct driver_info rndis_wext_info = {
+ .description = "Wireless RNDIS device",
+ .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT,
+ .bind = rndis_wext_bind,
+ .unbind = rndis_wext_unbind,
+ .status = rndis_status,
+ .rx_fixup = rndis_rx_fixup,
+ .tx_fixup = rndis_tx_fixup,
+ .reset = rndis_wext_reset,
+ .early_init = bcm4320_early_init,
+ .link_change = rndis_wext_link_change,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static const struct usb_device_id products [] = {
+#define RNDIS_MASTER_INTERFACE \
+ .bInterfaceClass = USB_CLASS_COMM, \
+ .bInterfaceSubClass = 2 /* ACM */, \
+ .bInterfaceProtocol = 0x0ff
+
+/* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom
+ * parameters available. Chipset marked as 'BCM4320SKFBG' in NDISwrapper-wiki.
+ */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0411,
+ .idProduct = 0x00bc, /* Buffalo WLI-U2-KG125S */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0baf,
+ .idProduct = 0x011b, /* U.S. Robotics USR5421 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x050d,
+ .idProduct = 0x011b, /* Belkin F5D7051 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x1799, /* Belkin has two vendor ids */
+ .idProduct = 0x011b, /* Belkin F5D7051 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x13b1,
+ .idProduct = 0x0014, /* Linksys WUSB54GSv2 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x13b1,
+ .idProduct = 0x0026, /* Linksys WUSB54GSC */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0b05,
+ .idProduct = 0x1717, /* Asus WL169gE */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0a5c,
+ .idProduct = 0xd11b, /* Eminent EM4045 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x1690,
+ .idProduct = 0x0715, /* BT Voyager 1055 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320b_info,
+},
+/* These devices have DriverVer < 4.xx.xx.xx and do not have any custom
+ * parameters available, hardware probably contain older firmware version with
+ * no way of updating. Chipset marked as 'BCM4320????' in NDISwrapper-wiki.
+ */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x13b1,
+ .idProduct = 0x000e, /* Linksys WUSB54GSv1 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320a_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0baf,
+ .idProduct = 0x0111, /* U.S. Robotics USR5420 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320a_info,
+}, {
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_DEVICE,
+ .idVendor = 0x0411,
+ .idProduct = 0x004b, /* BUFFALO WLI-USB-G54 */
+ RNDIS_MASTER_INTERFACE,
+ .driver_info = (unsigned long) &bcm4320a_info,
+},
+/* Generic Wireless RNDIS devices that we don't have exact
+ * idVendor/idProduct/chip yet.
+ */
+{
+ /* RNDIS is MSFT's un-official variant of CDC ACM */
+ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+ .driver_info = (unsigned long) &rndis_wext_info,
+}, {
+ /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
+ USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
+ .driver_info = (unsigned long) &rndis_wext_info,
+},
+ { }, // END
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver rndis_wlan_driver = {
+ .name = "rndis_wlan",
+ .id_table = products,
+ .probe = usbnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = usbnet_suspend,
+ .resume = usbnet_resume,
+};
+
+static int __init rndis_wlan_init(void)
+{
+ return usb_register(&rndis_wlan_driver);
+}
+module_init(rndis_wlan_init);
+
+static void __exit rndis_wlan_exit(void)
+{
+ usb_deregister(&rndis_wlan_driver);
+}
+module_exit(rndis_wlan_exit);
+
+MODULE_AUTHOR("Bjorge Dijkstra");
+MODULE_AUTHOR("Jussi Kivilinna");
+MODULE_DESCRIPTION("Driver for RNDIS based USB Wireless adapters");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index ab52f221cd71..b31f0c26c32b 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -1736,7 +1736,8 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
WARNING(rt2x00dev,
"TX status report missed for entry %p\n",
entry_done);
- rt2x00lib_txdone(entry_done, TX_FAIL_OTHER, 0);
+ rt2x00pci_txdone(rt2x00dev, entry_done, TX_FAIL_OTHER,
+ 0);
entry_done = rt2x00_get_data_entry_done(ring);
}
diff --git a/drivers/net/wireless/rtl8180_dev.c b/drivers/net/wireless/rtl8180_dev.c
index 07f37b0ccf91..27ebd689aa21 100644
--- a/drivers/net/wireless/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl8180_dev.c
@@ -36,6 +36,7 @@ MODULE_LICENSE("GPL");
static struct pci_device_id rtl8180_table[] __devinitdata = {
/* rtl8185 */
{ PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) },
{ PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x701f) },
/* rtl8180 */
OpenPOWER on IntegriCloud