aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/b43
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2009-09-09 17:33:45 -0700
committerDavid S. Miller <davem@davemloft.net>2009-09-09 17:33:45 -0700
commitea6a634ef7f0ab1d1f48ba0ad4f50e96d6065312 (patch)
treefbf291540b824183e0d9292906e9570fd344ebaa /drivers/net/wireless/b43
parentfa1a9c681377c57e233038e50479e7d7a5cc3108 (diff)
parentb2e3abdc708f8c0eff194af25362fdb239abe241 (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'drivers/net/wireless/b43')
-rw-r--r--drivers/net/wireless/b43/Kconfig4
-rw-r--r--drivers/net/wireless/b43/b43.h30
-rw-r--r--drivers/net/wireless/b43/debugfs.c67
-rw-r--r--drivers/net/wireless/b43/debugfs.h3
-rw-r--r--drivers/net/wireless/b43/dma.c31
-rw-r--r--drivers/net/wireless/b43/dma.h3
-rw-r--r--drivers/net/wireless/b43/main.c460
-rw-r--r--drivers/net/wireless/b43/main.h4
-rw-r--r--drivers/net/wireless/b43/phy_common.c1
-rw-r--r--drivers/net/wireless/b43/phy_common.h3
-rw-r--r--drivers/net/wireless/b43/phy_g.c7
-rw-r--r--drivers/net/wireless/b43/phy_g.h3
-rw-r--r--drivers/net/wireless/b43/pio.c71
-rw-r--r--drivers/net/wireless/b43/pio.h6
-rw-r--r--drivers/net/wireless/b43/sysfs.c3
-rw-r--r--drivers/net/wireless/b43/xmit.c10
16 files changed, 291 insertions, 415 deletions
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index 2af3b352232..83e38134acc 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -42,8 +42,8 @@ config B43_PCICORE_AUTOSELECT
default y
config B43_PCMCIA
- bool "Broadcom 43xx PCMCIA device support (EXPERIMENTAL)"
- depends on B43 && SSB_PCMCIAHOST_POSSIBLE && EXPERIMENTAL
+ bool "Broadcom 43xx PCMCIA device support"
+ depends on B43 && SSB_PCMCIAHOST_POSSIBLE
select SSB_PCMCIAHOST
---help---
Broadcom 43xx PCMCIA device support.
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index a1b3b731935..09cfe68537b 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -616,6 +616,12 @@ struct b43_wl {
/* Pointer to the ieee80211 hardware data structure */
struct ieee80211_hw *hw;
+ /* Global driver mutex. Every operation must run with this mutex locked. */
+ struct mutex mutex;
+ /* Hard-IRQ spinlock. This lock protects things used in the hard-IRQ
+ * handler, only. This basically is just the IRQ mask register. */
+ spinlock_t hardirq_lock;
+
/* The number of queues that were registered with the mac80211 subsystem
* initially. This is a backup copy of hw->queues in case hw->queues has
* to be dynamically lowered at runtime (Firmware does not support QoS).
@@ -623,16 +629,12 @@ struct b43_wl {
* from the mac80211 subsystem. */
u16 mac80211_initially_registered_queues;
- struct mutex mutex;
- spinlock_t irq_lock;
/* R/W lock for data transmission.
* Transmissions on 2+ queues can run concurrently, but somebody else
* might sync with TX by write_lock_irqsave()'ing. */
rwlock_t tx_lock;
/* Lock for LEDs access. */
spinlock_t leds_lock;
- /* Lock for SHM access. */
- spinlock_t shm_lock;
/* We can only have one operating interface (802.11 core)
* at a time. General information about this interface follows.
@@ -665,8 +667,7 @@ struct b43_wl {
bool radiotap_enabled;
bool radio_enabled;
- /* The beacon we are currently using (AP or IBSS mode).
- * This beacon stuff is protected by the irq_lock. */
+ /* The beacon we are currently using (AP or IBSS mode). */
struct sk_buff *current_beacon;
bool beacon0_uploaded;
bool beacon1_uploaded;
@@ -680,6 +681,11 @@ struct b43_wl {
* This is scheduled when we determine that the actual TX output
* power doesn't match what we want. */
struct work_struct txpower_adjust_work;
+
+ /* Packet transmit work */
+ struct work_struct tx_work;
+ /* Queue of packets to be transmitted. */
+ struct sk_buff_head tx_queue;
};
/* The type of the firmware file. */
@@ -754,14 +760,6 @@ enum {
smp_wmb(); \
} while (0)
-/* XXX--- HOW LOCKING WORKS IN B43 ---XXX
- *
- * You should always acquire both, wl->mutex and wl->irq_lock unless:
- * - You don't need to acquire wl->irq_lock, if the interface is stopped.
- * - You don't need to acquire wl->mutex in the IRQ handler, IRQ tasklet
- * and packet TX path (and _ONLY_ there.)
- */
-
/* Data structure for one wireless device (802.11 core) */
struct b43_wldev {
struct ssb_device *dev;
@@ -807,14 +805,12 @@ struct b43_wldev {
u32 dma_reason[6];
/* The currently active generic-interrupt mask. */
u32 irq_mask;
+
/* Link Quality calculation context. */
struct b43_noise_calculation noisecalc;
/* if > 0 MAC is suspended. if == 0 MAC is enabled. */
int mac_suspended;
- /* Interrupt Service Routine tasklet (bottom-half) */
- struct tasklet_struct isr_tasklet;
-
/* Periodic tasks */
struct delayed_work periodic_work;
unsigned int periodic_state;
diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c
index 45e3d6af69f..8f64943e3f6 100644
--- a/drivers/net/wireless/b43/debugfs.c
+++ b/drivers/net/wireless/b43/debugfs.c
@@ -46,8 +46,6 @@ struct b43_debugfs_fops {
struct file_operations fops;
/* Offset of struct b43_dfs_file in struct b43_dfsentry */
size_t file_struct_offset;
- /* Take wl->irq_lock before calling read/write? */
- bool take_irqlock;
};
static inline
@@ -127,7 +125,6 @@ static int shm16write__write_file(struct b43_wldev *dev,
unsigned int routing, addr, mask, set;
u16 val;
int res;
- unsigned long flags;
res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
&routing, &addr, &mask, &set);
@@ -144,15 +141,13 @@ static int shm16write__write_file(struct b43_wldev *dev,
if ((mask > 0xFFFF) || (set > 0xFFFF))
return -E2BIG;
- spin_lock_irqsave(&dev->wl->shm_lock, flags);
if (mask == 0)
val = 0;
else
- val = __b43_shm_read16(dev, routing, addr);
+ val = b43_shm_read16(dev, routing, addr);
val &= mask;
val |= set;
- __b43_shm_write16(dev, routing, addr, val);
- spin_unlock_irqrestore(&dev->wl->shm_lock, flags);
+ b43_shm_write16(dev, routing, addr, val);
return 0;
}
@@ -206,7 +201,6 @@ static int shm32write__write_file(struct b43_wldev *dev,
unsigned int routing, addr, mask, set;
u32 val;
int res;
- unsigned long flags;
res = sscanf(buf, "0x%X 0x%X 0x%X 0x%X",
&routing, &addr, &mask, &set);
@@ -223,15 +217,13 @@ static int shm32write__write_file(struct b43_wldev *dev,
if ((mask > 0xFFFFFFFF) || (set > 0xFFFFFFFF))
return -E2BIG;
- spin_lock_irqsave(&dev->wl->shm_lock, flags);
if (mask == 0)
val = 0;
else
- val = __b43_shm_read32(dev, routing, addr);
+ val = b43_shm_read32(dev, routing, addr);
val &= mask;
val |= set;
- __b43_shm_write32(dev, routing, addr, val);
- spin_unlock_irqrestore(&dev->wl->shm_lock, flags);
+ b43_shm_write32(dev, routing, addr, val);
return 0;
}
@@ -372,14 +364,12 @@ static ssize_t txstat_read_file(struct b43_wldev *dev,
{
struct b43_txstatus_log *log = &dev->dfsentry->txstatlog;
ssize_t count = 0;
- unsigned long flags;
int i, idx;
struct b43_txstatus *stat;
- spin_lock_irqsave(&log->lock, flags);
if (log->end < 0) {
fappend("Nothing transmitted, yet\n");
- goto out_unlock;
+ goto out;
}
fappend("b43 TX status reports:\n\n"
"index | cookie | seq | phy_stat | frame_count | "
@@ -409,13 +399,11 @@ static ssize_t txstat_read_file(struct b43_wldev *dev,
break;
i++;
}
-out_unlock:
- spin_unlock_irqrestore(&log->lock, flags);
+out:
return count;
}
-/* wl->irq_lock is locked */
static int restart_write_file(struct b43_wldev *dev,
const char *buf, size_t count)
{
@@ -556,12 +544,7 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
goto out_unlock;
}
memset(buf, 0, bufsize);
- if (dfops->take_irqlock) {
- spin_lock_irq(&dev->wl->irq_lock);
- ret = dfops->read(dev, buf, bufsize);
- spin_unlock_irq(&dev->wl->irq_lock);
- } else
- ret = dfops->read(dev, buf, bufsize);
+ ret = dfops->read(dev, buf, bufsize);
if (ret <= 0) {
free_pages((unsigned long)buf, buforder);
err = ret;
@@ -623,12 +606,7 @@ static ssize_t b43_debugfs_write(struct file *file,
err = -EFAULT;
goto out_freepage;
}
- if (dfops->take_irqlock) {
- spin_lock_irq(&dev->wl->irq_lock);
- err = dfops->write(dev, buf, count);
- spin_unlock_irq(&dev->wl->irq_lock);
- } else
- err = dfops->write(dev, buf, count);
+ err = dfops->write(dev, buf, count);
if (err)
goto out_freepage;
@@ -641,7 +619,7 @@ out_unlock:
}
-#define B43_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \
+#define B43_DEBUGFS_FOPS(name, _read, _write) \
static struct b43_debugfs_fops fops_##name = { \
.read = _read, \
.write = _write, \
@@ -652,20 +630,19 @@ out_unlock:
}, \
.file_struct_offset = offsetof(struct b43_dfsentry, \
file_##name), \
- .take_irqlock = _take_irqlock, \
}
-B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file, 1);
-B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file, 1);
-B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file, 1);
-B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file, 1);
-B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file, 1);
-B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file, 1);
-B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file, 1);
-B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file, 1);
-B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0);
-B43_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1);
-B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL, 0);
+B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file);
+B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file);
+B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file);
+B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file);
+B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file);
+B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file);
+B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file);
+B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file);
+B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL);
+B43_DEBUGFS_FOPS(restart, NULL, restart_write_file);
+B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL);
bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
@@ -738,7 +715,6 @@ void b43_debugfs_add_device(struct b43_wldev *dev)
return;
}
log->end = -1;
- spin_lock_init(&log->lock);
dev->dfsentry = e;
@@ -822,7 +798,6 @@ void b43_debugfs_remove_device(struct b43_wldev *dev)
kfree(e);
}
-/* Called with IRQs disabled. */
void b43_debugfs_log_txstat(struct b43_wldev *dev,
const struct b43_txstatus *status)
{
@@ -834,14 +809,12 @@ void b43_debugfs_log_txstat(struct b43_wldev *dev,
if (!e)
return;
log = &e->txstatlog;
- spin_lock(&log->lock); /* IRQs are already disabled. */
i = log->end + 1;
if (i == B43_NR_LOGGED_TXSTATUS)
i = 0;
log->end = i;
cur = &(log->log[i]);
memcpy(cur, status, sizeof(*cur));
- spin_unlock(&log->lock);
}
void b43_debugfs_init(void)
diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h
index b9d4de4a979..e47b4b488b0 100644
--- a/drivers/net/wireless/b43/debugfs.h
+++ b/drivers/net/wireless/b43/debugfs.h
@@ -23,9 +23,10 @@ struct dentry;
#define B43_NR_LOGGED_TXSTATUS 100
struct b43_txstatus_log {
+ /* This structure is protected by wl->mutex */
+
struct b43_txstatus *log;
int end;
- spinlock_t lock;
};
struct b43_dfs_file {
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index 289aaf6dfe7..a467ee260a1 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -856,7 +856,6 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
} else
B43_WARN_ON(1);
}
- spin_lock_init(&ring->lock);
#ifdef CONFIG_B43_DEBUG
ring->last_injected_overflow = jiffies;
#endif
@@ -1315,7 +1314,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
struct b43_dmaring *ring;
struct ieee80211_hdr *hdr;
int err = 0;
- unsigned long flags;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
hdr = (struct ieee80211_hdr *)skb->data;
@@ -1331,8 +1329,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
dev, skb_get_queue_mapping(skb));
}
- spin_lock_irqsave(&ring->lock, flags);
-
B43_WARN_ON(!ring->tx);
if (unlikely(ring->stopped)) {
@@ -1343,7 +1339,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
if (b43_debug(dev, B43_DBG_DMAVERBOSE))
b43err(dev->wl, "Packet after queue stopped\n");
err = -ENOSPC;
- goto out_unlock;
+ goto out;
}
if (unlikely(WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME))) {
@@ -1351,7 +1347,7 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
* full, but queues not stopped. */
b43err(dev->wl, "DMA queue overflow\n");
err = -ENOSPC;
- goto out_unlock;
+ goto out;
}
/* Assign the queue number to the ring (if not already done before)
@@ -1365,11 +1361,11 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
* anymore and must not transmit it unencrypted. */
dev_kfree_skb_any(skb);
err = 0;
- goto out_unlock;
+ goto out;
}
if (unlikely(err)) {
b43err(dev->wl, "DMA tx mapping failure\n");
- goto out_unlock;
+ goto out;
}
ring->nr_tx_packets++;
if ((free_slots(ring) < TX_SLOTS_PER_FRAME) ||
@@ -1381,13 +1377,11 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
}
}
-out_unlock:
- spin_unlock_irqrestore(&ring->lock, flags);
+out:
return err;
}
-/* Called with IRQs disabled. */
void b43_dma_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status)
{
@@ -1402,8 +1396,6 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
if (unlikely(!ring))
return;
- spin_lock(&ring->lock); /* IRQs are already disabled. */
-
B43_WARN_ON(!ring->tx);
ops = ring->ops;
while (1) {
@@ -1462,8 +1454,6 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index);
}
}
-
- spin_unlock(&ring->lock);
}
void b43_dma_get_tx_stats(struct b43_wldev *dev,
@@ -1471,17 +1461,14 @@ void b43_dma_get_tx_stats(struct b43_wldev *dev,
{
const int nr_queues = dev->wl->hw->queues;
struct b43_dmaring *ring;
- unsigned long flags;
int i;
for (i = 0; i < nr_queues; i++) {
ring = select_ring_by_priority(dev, i);
- spin_lock_irqsave(&ring->lock, flags);
stats[i].len = ring->used_slots / TX_SLOTS_PER_FRAME;
stats[i].limit = ring->nr_slots / TX_SLOTS_PER_FRAME;
stats[i].count = ring->nr_tx_packets;
- spin_unlock_irqrestore(&ring->lock, flags);
}
}
@@ -1592,22 +1579,14 @@ void b43_dma_rx(struct b43_dmaring *ring)
static void b43_dma_tx_suspend_ring(struct b43_dmaring *ring)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ring->lock, flags);
B43_WARN_ON(!ring->tx);
ring->ops->tx_suspend(ring);
- spin_unlock_irqrestore(&ring->lock, flags);
}
static void b43_dma_tx_resume_ring(struct b43_dmaring *ring)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ring->lock, flags);
B43_WARN_ON(!ring->tx);
ring->ops->tx_resume(ring);
- spin_unlock_irqrestore(&ring->lock, flags);
}
void b43_dma_tx_suspend(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h
index 05dde646d83..f0b0838fb5b 100644
--- a/drivers/net/wireless/b43/dma.h
+++ b/drivers/net/wireless/b43/dma.h
@@ -2,7 +2,6 @@
#define B43_DMA_H_
#include <linux/ieee80211.h>
-#include <linux/spinlock.h>
#include "b43.h"
@@ -244,8 +243,6 @@ struct b43_dmaring {
/* The QOS priority assigned to this ring. Only used for TX rings.
* This is the mac80211 "queue" value. */
u8 queue_prio;
- /* Lock, only used for TX. */
- spinlock_t lock;
struct b43_wldev *dev;
#ifdef CONFIG_B43_DEBUG
/* Maximum number of used slots. */
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index ae05f667114..7a9a3fa5542 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -291,7 +291,7 @@ static struct ieee80211_supported_band b43_band_2GHz = {
static void b43_wireless_core_exit(struct b43_wldev *dev);
static int b43_wireless_core_init(struct b43_wldev *dev);
-static void b43_wireless_core_stop(struct b43_wldev *dev);
+static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev);
static int b43_wireless_core_start(struct b43_wldev *dev);
static int b43_ratelimit(struct b43_wl *wl)
@@ -390,7 +390,7 @@ static inline void b43_shm_control_word(struct b43_wldev *dev,
b43_write32(dev, B43_MMIO_SHM_CONTROL, control);
}
-u32 __b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset)
+u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset)
{
u32 ret;
@@ -413,20 +413,7 @@ out:
return ret;
}
-u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset)
-{
- struct b43_wl *wl = dev->wl;
- unsigned long flags;
- u32 ret;
-
- spin_lock_irqsave(&wl->shm_lock, flags);
- ret = __b43_shm_read32(dev, routing, offset);
- spin_unlock_irqrestore(&wl->shm_lock, flags);
-
- return ret;
-}
-
-u16 __b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset)
+u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset)
{
u16 ret;
@@ -447,20 +434,7 @@ out:
return ret;
}
-u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset)
-{
- struct b43_wl *wl = dev->wl;
- unsigned long flags;
- u16 ret;
-
- spin_lock_irqsave(&wl->shm_lock, flags);
- ret = __b43_shm_read16(dev, routing, offset);
- spin_unlock_irqrestore(&wl->shm_lock, flags);
-
- return ret;
-}
-
-void __b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value)
+void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value)
{
if (routing == B43_SHM_SHARED) {
B43_WARN_ON(offset & 0x0001);
@@ -480,17 +454,7 @@ void __b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value
b43_write32(dev, B43_MMIO_SHM_DATA, value);
}
-void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value)
-{
- struct b43_wl *wl = dev->wl;
- unsigned long flags;
-
- spin_lock_irqsave(&wl->shm_lock, flags);
- __b43_shm_write32(dev, routing, offset, value);
- spin_unlock_irqrestore(&wl->shm_lock, flags);
-}
-
-void __b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value)
+void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value)
{
if (routing == B43_SHM_SHARED) {
B43_WARN_ON(offset & 0x0001);
@@ -506,16 +470,6 @@ void __b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value
b43_write16(dev, B43_MMIO_SHM_DATA, value);
}
-void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value)
-{
- struct b43_wl *wl = dev->wl;
- unsigned long flags;
-
- spin_lock_irqsave(&wl->shm_lock, flags);
- __b43_shm_write16(dev, routing, offset, value);
- spin_unlock_irqrestore(&wl->shm_lock, flags);
-}
-
/* Read HostFlags */
u64 b43_hf_read(struct b43_wldev *dev)
{
@@ -685,22 +639,11 @@ static void b43_short_slot_timing_disable(struct b43_wldev *dev)
b43_set_slot_time(dev, 20);
}
-/* Synchronize IRQ top- and bottom-half.
- * IRQs must be masked before calling this.
- * This must not be called with the irq_lock held.
- */
-static void b43_synchronize_irq(struct b43_wldev *dev)
-{
- synchronize_irq(dev->dev->irq);
- tasklet_kill(&dev->isr_tasklet);
-}
-
/* DummyTransmission function, as documented on
* http://bcm-v4.sipsolutions.net/802.11/DummyTransmission
*/
void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on)
{
- struct b43_wl *wl = dev->wl;
struct b43_phy *phy = &dev->phy;
unsigned int i, max_loop;
u16 value;
@@ -720,9 +663,6 @@ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on)
buffer[0] = 0x000B846E;
}
- spin_lock_irq(&wl->irq_lock);
- write_lock(&wl->tx_lock);
-
for (i = 0; i < 5; i++)
b43_ram_write(dev, i * 4, buffer[i]);
@@ -778,9 +718,6 @@ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on)
}
if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5)
b43_radio_write16(dev, 0x0051, 0x0037);
-
- write_unlock(&wl->tx_lock);
- spin_unlock_irq(&wl->irq_lock);
}
static void key_write(struct b43_wldev *dev,
@@ -1620,6 +1557,27 @@ static void handle_irq_beacon(struct b43_wldev *dev)
}
}
+static void b43_do_beacon_update_trigger_work(struct b43_wldev *dev)
+{
+ u32 old_irq_mask = dev->irq_mask;
+
+ /* update beacon right away or defer to irq */
+ handle_irq_beacon(dev);
+ if (old_irq_mask != dev->irq_mask) {
+ /* The handler updated the IRQ mask. */
+ B43_WARN_ON(!dev->irq_mask);
+ if (b43_read32(dev, B43_MMIO_GEN_IRQ_MASK)) {
+ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask);
+ } else {
+ /* Device interrupts are currently disabled. That means
+ * we just ran the hardirq handler and scheduled the
+ * IRQ thread. The thread will write the IRQ mask when
+ * it finished, so there's nothing to do here. Writing
+ * the mask _here_ would incorrectly re-enable IRQs. */
+ }
+ }
+}
+
static void b43_beacon_update_trigger_work(struct work_struct *work)
{
struct b43_wl *wl = container_of(work, struct b43_wl,
@@ -1629,19 +1587,22 @@ static void b43_beacon_update_trigger_work(struct work_struct *work)
mutex_lock(&wl->mutex);
dev = wl->current_dev;
if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) {
- spin_lock_irq(&wl->irq_lock);
- /* update beacon right away or defer to irq */
- handle_irq_beacon(dev);
- /* The handler might have updated the IRQ mask. */
- b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask);
- mmiowb();
- spin_unlock_irq(&wl->irq_lock);
+ if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) {
+ /* wl->mutex is enough. */
+ b43_do_beacon_update_trigger_work(dev);
+ mmiowb();
+ } else {
+ spin_lock_irq(&wl->hardirq_lock);
+ b43_do_beacon_update_trigger_work(dev);
+ mmiowb();
+ spin_unlock_irq(&wl->hardirq_lock);
+ }
}
mutex_unlock(&wl->mutex);
}
/* Asynchronously update the packet templates in template RAM.
- * Locking: Requires wl->irq_lock to be locked. */
+ * Locking: Requires wl->mutex to be locked. */
static void b43_update_templates(struct b43_wl *wl)
{
struct sk_buff *beacon;
@@ -1778,18 +1739,15 @@ out:
B43_DEBUGIRQ_REASON_REG, B43_DEBUGIRQ_ACK);
}
-/* Interrupt handler bottom-half */
-static void b43_interrupt_tasklet(struct b43_wldev *dev)
+static void b43_do_interrupt_thread(struct b43_wldev *dev)
{
u32 reason;
u32 dma_reason[ARRAY_SIZE(dev->dma_reason)];
u32 merged_dma_reason = 0;
int i;
- unsigned long flags;
-
- spin_lock_irqsave(&dev->wl->irq_lock, flags);
- B43_WARN_ON(b43_status(dev) != B43_STAT_STARTED);
+ if (unlikely(b43_status(dev) != B43_STAT_STARTED))
+ return;
reason = dev->irq_reason;
for (i = 0; i < ARRAY_SIZE(dma_reason); i++) {
@@ -1822,8 +1780,6 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev)
dma_reason[2], dma_reason[3],
dma_reason[4], dma_reason[5]);
b43_controller_restart(dev, "DMA error");
- mmiowb();
- spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
return;
}
if (merged_dma_reason & B43_DMAIRQ_NONFATALMASK) {
@@ -1867,47 +1823,36 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev)
if (reason & B43_IRQ_TX_OK)
handle_irq_transmit_status(dev);
+ /* Re-enable interrupts on the device by restoring the current interrupt mask. */
b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask);
- mmiowb();
- spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
}
-static void b43_interrupt_ack(struct b43_wldev *dev, u32 reason)
+/* Interrupt thread handler. Handles device interrupts in thread context. */
+static irqreturn_t b43_interrupt_thread_handler(int irq, void *dev_id)
{
- b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, reason);
+ struct b43_wldev *dev = dev_id;
- b43_write32(dev, B43_MMIO_DMA0_REASON, dev->dma_reason[0]);
- b43_write32(dev, B43_MMIO_DMA1_REASON, dev->dma_reason[1]);
- b43_write32(dev, B43_MMIO_DMA2_REASON, dev->dma_reason[2]);
- b43_write32(dev, B43_MMIO_DMA3_REASON, dev->dma_reason[3]);
- b43_write32(dev, B43_MMIO_DMA4_REASON, dev->dma_reason[4]);
-/* Unused ring
- b43_write32(dev, B43_MMIO_DMA5_REASON, dev->dma_reason[5]);
-*/
+ mutex_lock(&dev->wl->mutex);
+ b43_do_interrupt_thread(dev);
+ mmiowb();
+ mutex_unlock(&dev->wl->mutex);
+
+ return IRQ_HANDLED;
}
-/* Interrupt handler top-half */
-static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
+static irqreturn_t b43_do_interrupt(struct b43_wldev *dev)
{
- irqreturn_t ret = IRQ_NONE;
- struct b43_wldev *dev = dev_id;
u32 reason;
- B43_WARN_ON(!dev);
+ /* This code runs under wl->hardirq_lock, but _only_ on non-SDIO busses.
+ * On SDIO, this runs under wl->mutex. */
- spin_lock(&dev->wl->irq_lock);
-
- if (unlikely(b43_status(dev) < B43_STAT_STARTED)) {
- /* This can only happen on shared IRQ lines. */
- goto out;
- }
reason = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);
if (reason == 0xffffffff) /* shared IRQ */
- goto out;
- ret = IRQ_HANDLED;
+ return IRQ_NONE;
reason &= dev->irq_mask;
if (!reason)
- goto out;
+ return IRQ_HANDLED;
dev->dma_reason[0] = b43_read32(dev, B43_MMIO_DMA0_REASON)
& 0x0001DC00;
@@ -1924,15 +1869,38 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
& 0x0000DC00;
*/
- b43_interrupt_ack(dev, reason);
- /* disable all IRQs. They are enabled again in the bottom half. */
+ /* ACK the interrupt. */
+ b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, reason);
+ b43_write32(dev, B43_MMIO_DMA0_REASON, dev->dma_reason[0]);
+ b43_write32(dev, B43_MMIO_DMA1_REASON, dev->dma_reason[1]);
+ b43_write32(dev, B43_MMIO_DMA2_REASON, dev->dma_reason[2]);
+ b43_write32(dev, B43_MMIO_DMA3_REASON, dev->dma_reason[3]);
+ b43_write32(dev, B43_MMIO_DMA4_REASON, dev->dma_reason[4]);
+/* Unused ring
+ b43_write32(dev, B43_MMIO_DMA5_REASON, dev->dma_reason[5]);
+*/
+
+ /* Disable IRQs on the device. The IRQ thread handler will re-enable them. */
b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0);
- /* save the reason code and call our bottom half. */
+ /* Save the reason bitmasks for the IRQ thread handler. */
dev->irq_reason = reason;
- tasklet_schedule(&dev->isr_tasklet);
-out:
+
+ return IRQ_WAKE_THREAD;
+}
+
+/* Interrupt handler top-half. This runs with interrupts disabled. */
+static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
+{
+ struct b43_wldev *dev = dev_id;
+ irqreturn_t ret;
+
+ if (unlikely(b43_status(dev) < B43_STAT_STARTED))
+ return IRQ_NONE;
+
+ spin_lock(&dev->wl->hardirq_lock);
+ ret = b43_do_interrupt(dev);
mmiowb();
- spin_unlock(&dev->wl->irq_lock);
+ spin_unlock(&dev->wl->hardirq_lock);
return ret;
}
@@ -3038,15 +3006,12 @@ static void b43_security_init(struct b43_wldev *dev)
static int b43_rng_read(struct hwrng *rng, u32 *data)
{
struct b43_wl *wl = (struct b43_wl *)rng->priv;
- unsigned long flags;
- /* Don't take wl->mutex here, as it could deadlock with
- * hwrng internal locking. It's not needed to take
- * wl->mutex here, anyway. */
+ /* FIXME: We need to take wl->mutex here to make sure the device
+ * is not going away from under our ass. However it could deadlock
+ * with hwrng internal locking. */
- spin_lock_irqsave(&wl->irq_lock, flags);
*data = b43_read16(wl->current_dev, B43_MMIO_RNG);
- spin_unlock_irqrestore(&wl->irq_lock, flags);
return (sizeof(u16));
}
@@ -3082,46 +3047,52 @@ static int b43_rng_init(struct b43_wl *wl)
return err;
}
-static int b43_op_tx(struct ieee80211_hw *hw,
- struct sk_buff *skb)
+static void b43_tx_work(struct work_struct *work)
{
- struct b43_wl *wl = hw_to_b43_wl(hw);
- struct b43_wldev *dev = wl->current_dev;
- unsigned long flags;
- int err;
+ struct b43_wl *wl = container_of(work, struct b43_wl, tx_work);
+ struct b43_wldev *dev;
+ struct sk_buff *skb;
+ int err = 0;
- if (unlikely(skb->len < 2 + 2 + 6)) {
- /* Too short, this can't be a valid frame. */
- goto drop_packet;
+ mutex_lock(&wl->mutex);
+ dev = wl->current_dev;
+ if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED)) {
+ mutex_unlock(&wl->mutex);
+ return;
}
- B43_WARN_ON(skb_shinfo(skb)->nr_frags);
- if (unlikely(!dev))
- goto drop_packet;
- /* Transmissions on seperate queues can run concurrently. */
- read_lock_irqsave(&wl->tx_lock, flags);
+ while (skb_queue_len(&wl->tx_queue)) {
+ skb = skb_dequeue(&wl->tx_queue);
- err = -ENODEV;
- if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
if (b43_using_pio_transfers(dev))
err = b43_pio_tx(dev, skb);
else
err = b43_dma_tx(dev, skb);
+ if (unlikely(err))
+ dev_kfree_skb(skb); /* Drop it */
}
- read_unlock_irqrestore(&wl->tx_lock, flags);
+ mutex_unlock(&wl->mutex);
+}
- if (unlikely(err))
- goto drop_packet;
- return NETDEV_TX_OK;
+static int b43_op_tx(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ struct b43_wl *wl = hw_to_b43_wl(hw);
+
+ if (unlikely(skb->len < 2 + 2 + 6)) {
+ /* Too short, this can't be a valid frame. */
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+ B43_WARN_ON(skb_shinfo(skb)->nr_frags);
+
+ skb_queue_tail(&wl->tx_queue, skb);
+ ieee80211_queue_work(wl->hw, &wl->tx_work);
-drop_packet:
- /* We can not transmit this packet. Drop it. */
- dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
-/* Locking: wl->irq_lock */
static void b43_qos_params_upload(struct b43_wldev *dev,
const struct ieee80211_tx_queue_params *p,
u16 shm_offset)
@@ -3130,6 +3101,9 @@ static void b43_qos_params_upload(struct b43_wldev *dev,
int bslots, tmp;
unsigned int i;
+ if (!dev->qos_enabled)
+ return;
+
bslots = b43_read16(dev, B43_MMIO_RNG) & p->cw_min;
memset(&params, 0, sizeof(params));
@@ -3175,6 +3149,9 @@ static void b43_qos_upload_all(struct b43_wldev *dev)
struct b43_qos_params *params;
unsigned int i;
+ if (!dev->qos_enabled)
+ return;
+
BUILD_BUG_ON(ARRAY_SIZE(b43_qos_shm_offsets) !=
ARRAY_SIZE(wl->qos_params));
@@ -3234,6 +3211,16 @@ static void b43_qos_clear(struct b43_wl *wl)
/* Initialize the core's QOS capabilities */
static void b43_qos_init(struct b43_wldev *dev)
{
+ if (!dev->qos_enabled) {
+ /* Disable QOS support. */
+ b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_EDCF);
+ b43_write16(dev, B43_MMIO_IFSCTL,
+ b43_read16(dev, B43_MMIO_IFSCTL)
+ & ~B43_MMIO_IFSCTL_USE_EDCF);
+ b43dbg(dev->wl, "QoS disabled\n");
+ return;
+ }
+
/* Upload the current QOS parameters. */
b43_qos_upload_all(dev);
@@ -3242,6 +3229,7 @@ static void b43_qos_init(struct b43_wldev *dev)
b43_write16(dev, B43_MMIO_IFSCTL,
b43_read16(dev, B43_MMIO_IFSCTL)
| B43_MMIO_IFSCTL_USE_EDCF);
+ b43dbg(dev->wl, "QoS enabled\n");
}
static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
@@ -3283,22 +3271,20 @@ static int b43_op_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
- struct b43_wldev *dev = wl->current_dev;
- unsigned long flags;
+ struct b43_wldev *dev;
int err = -ENODEV;
- if (!dev)
- goto out;
- spin_lock_irqsave(&wl->irq_lock, flags);
- if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
+ mutex_lock(&wl->mutex);
+ dev = wl->current_dev;
+ if (dev && b43_status(dev) >= B43_STAT_STARTED) {
if (b43_using_pio_transfers(dev))
b43_pio_get_tx_stats(dev, stats);
else
b43_dma_get_tx_stats(dev, stats);
err = 0;
}
- spin_unlock_irqrestore(&wl->irq_lock, flags);
-out:
+ mutex_unlock(&wl->mutex);
+
return err;
}
@@ -3306,11 +3292,10 @@ static int b43_op_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
- unsigned long flags;
- spin_lock_irqsave(&wl->irq_lock, flags);
+ mutex_lock(&wl->mutex);
memcpy(stats, &wl->ieee_stats, sizeof(*stats));
- spin_unlock_irqrestore(&wl->irq_lock, flags);
+ mutex_unlock(&wl->mutex);
return 0;
}
@@ -3322,7 +3307,6 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw)
u64 tsf;
mutex_lock(&wl->mutex);
- spin_lock_irq(&wl->irq_lock);
dev = wl->current_dev;
if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED))
@@ -3330,7 +3314,6 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw)
else
tsf = 0;
- spin_unlock_irq(&wl->irq_lock);
mutex_unlock(&wl->mutex);
return tsf;
@@ -3342,13 +3325,11 @@ static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf)
struct b43_wldev *dev;
mutex_lock(&wl->mutex);
- spin_lock_irq(&wl->irq_lock);
dev = wl->current_dev;
if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED))
b43_tsf_write(dev, tsf);
- spin_unlock_irq(&wl->irq_lock);
mutex_unlock(&wl->mutex);
}
@@ -3434,7 +3415,7 @@ static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan)
prev_status = b43_status(down_dev);
/* Shutdown the currently running core. */
if (prev_status >= B43_STAT_STARTED)
- b43_wireless_core_stop(down_dev);
+ down_dev = b43_wireless_core_stop(down_dev);
if (prev_status >= B43_STAT_INITIALIZED)
b43_wireless_core_exit(down_dev);
@@ -3498,7 +3479,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
struct b43_wldev *dev;
struct b43_phy *phy;
struct ieee80211_conf *conf = &hw->conf;
- unsigned long flags;
int antenna;
int err = 0;
@@ -3529,13 +3509,11 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
/* Adjust the desired TX power level. */
if (conf->power_level != 0) {
- spin_lock_irqsave(&wl->irq_lock, flags);
if (conf->power_level != phy->desired_txpower) {
phy->desired_txpower = conf->power_level;
b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME |
B43_TXPWR_IGNORE_TSSI);
}
- spin_unlock_irqrestore(&wl->irq_lock, flags);
}
/* Antennas for RX and management frame TX. */
@@ -3620,7 +3598,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
- unsigned long flags;
mutex_lock(&wl->mutex);
@@ -3630,7 +3607,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
B43_WARN_ON(wl->vif != vif);
- spin_lock_irqsave(&wl->irq_lock, flags);
if (changed & BSS_CHANGED_BSSID) {
if (conf->bssid)
memcpy(wl->bssid, conf->bssid, ETH_ALEN);
@@ -3648,7 +3624,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BSSID)
b43_write_mac_bssid_templates(dev);
}
- spin_unlock_irqrestore(&wl->irq_lock, flags);
b43_mac_suspend(dev);
@@ -3689,15 +3664,6 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
return -ENOSPC; /* User disabled HW-crypto */
mutex_lock(&wl->mutex);
- spin_lock_irq(&wl->irq_lock);
- write_lock(&wl->tx_lock);
- /* Why do we need all this locking here?
- * mutex -> Every config operation must take it.
- * irq_lock -> We modify the dev->key array, which is accessed
- * in the IRQ handlers.
- * tx_lock -> We modify the dev->key array, which is accessed
- * in the TX handler.
- */
dev = wl->current_dev;
err = -ENODEV;
@@ -3789,8 +3755,6 @@ out_unlock:
sta ? sta->addr : bcast_addr);
b43_dump_keymemory(dev);
}
- write_unlock(&wl->tx_lock);
- spin_unlock_irq(&wl->irq_lock);
mutex_unlock(&wl->mutex);
return err;
@@ -3801,15 +3765,15 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw,
u64 multicast)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
- struct b43_wldev *dev = wl->current_dev;
- unsigned long flags;
+ struct b43_wldev *dev;
+ mutex_lock(&wl->mutex);
+ dev = wl->current_dev;
if (!dev) {
*fflags = 0;
- return;
+ goto out_unlock;
}
- spin_lock_irqsave(&wl->irq_lock, flags);
*fflags &= FIF_PROMISC_IN_BSS |
FIF_ALLMULTI |
FIF_FCSFAIL |
@@ -3830,41 +3794,70 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw,
if (changed && b43_status(dev) >= B43_STAT_INITIALIZED)
b43_adjust_opmode(dev);
- spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+out_unlock:
+ mutex_unlock(&wl->mutex);
}
-/* Locking: wl->mutex */
-static void b43_wireless_core_stop(struct b43_wldev *dev)
+/* Locking: wl->mutex
+ * Returns the current dev. This might be different from the passed in dev,
+ * because the core might be gone away while we unlocked the mutex. */
+static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
- unsigned long flags;
+ struct b43_wldev *orig_dev;
- if (b43_status(dev) < B43_STAT_STARTED)
- return;
+redo:
+ if (!dev || b43_status(dev) < B43_STAT_STARTED)
+ return dev;
- /* Disable and sync interrupts. We must do this before than
- * setting the status to INITIALIZED, as the interrupt handler
- * won't care about IRQs then. */
- spin_lock_irqsave(&wl->irq_lock, flags);
- b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0);
- b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* flush */
- spin_unlock_irqrestore(&wl->irq_lock, flags);
- b43_synchronize_irq(dev);
+ /* Cancel work. Unlock to avoid deadlocks. */
+ mutex_unlock(&wl->mutex);
+ cancel_delayed_work_sync(&dev->periodic_work);
+ cancel_work_sync(&wl->tx_work);
+ mutex_lock(&wl->mutex);
+ dev = wl->current_dev;
+ if (!dev || b43_status(dev) < B43_STAT_STARTED) {
+ /* Whoops, aliens ate up the device while we were unlocked. */
+ return dev;
+ }
- write_lock_irqsave(&wl->tx_lock, flags);
+ /* Disable interrupts on the device. */
b43_set_status(dev, B43_STAT_INITIALIZED);
- write_unlock_irqrestore(&wl->tx_lock, flags);
-
- b43_pio_stop(dev);
+ if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) {
+ /* wl->mutex is locked. That is enough. */
+ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0);
+ b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */
+ } else {
+ spin_lock_irq(&wl->hardirq_lock);
+ b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0);
+ b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */
+ spin_unlock_irq(&wl->hardirq_lock);
+ }
+ /* Synchronize the interrupt handlers. Unlock to avoid deadlocks. */
+ orig_dev = dev;
mutex_unlock(&wl->mutex);
- /* Must unlock as it would otherwise deadlock. No races here.
- * Cancel the possibly running self-rearming periodic work. */
- cancel_delayed_work_sync(&dev->periodic_work);
+ synchronize_irq(dev->dev->irq);
mutex_lock(&wl->mutex);
+ dev = wl->current_dev;
+ if (!dev)
+ return dev;
+ if (dev != orig_dev) {
+ if (b43_status(dev) >= B43_STAT_STARTED)
+ goto redo;
+ return dev;
+ }
+ B43_WARN_ON(b43_read32(dev, B43_MMIO_GEN_IRQ_MASK));
+
+ /* Drain the TX queue */
+ while (skb_queue_len(&wl->tx_queue))
+ dev_kfree_skb(skb_dequeue(&wl->tx_queue));
b43_mac_suspend(dev);
free_irq(dev->dev->irq, dev);
b43dbg(wl, "Wireless interface stopped\n");
+
+ return dev;
}
/* Locking: wl->mutex */
@@ -3875,8 +3868,9 @@ static int b43_wireless_core_start(struct b43_wldev *dev)
B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED);
drain_txstatus_queue(dev);
- err = request_irq(dev->dev->irq, b43_interrupt_handler,
- IRQF_SHARED, KBUILD_MODNAME, dev);
+ err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler,
+ b43_interrupt_thread_handler,
+ IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) {
b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq);
goto out;
@@ -4098,16 +4092,20 @@ static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev)
bus->pcicore.dev->id.revision <= 5) {
/* IMCFGLO timeouts workaround. */
tmp = ssb_read32(dev->dev, SSB_IMCFGLO);
- tmp &= ~SSB_IMCFGLO_REQTO;
- tmp &= ~SSB_IMCFGLO_SERTO;
switch (bus->bustype) {
case SSB_BUSTYPE_PCI:
case SSB_BUSTYPE_PCMCIA:
+ tmp &= ~SSB_IMCFGLO_REQTO;
+ tmp &= ~SSB_IMCFGLO_SERTO;
tmp |= 0x32;
break;
case SSB_BUSTYPE_SSB:
+ tmp &= ~SSB_IMCFGLO_REQTO;
+ tmp &= ~SSB_IMCFGLO_SERTO;
tmp |= 0x53;
break;
+ default:
+ break;
}
ssb_write32(dev->dev, SSB_IMCFGLO, tmp);
}
@@ -4155,8 +4153,8 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
{
u32 macctl;
- B43_WARN_ON(b43_status(dev) > B43_STAT_INITIALIZED);
- if (b43_status(dev) != B43_STAT_INITIALIZED)
+ B43_WARN_ON(dev && b43_status(dev) > B43_STAT_INITIALIZED);
+ if (!dev || b43_status(dev) != B43_STAT_INITIALIZED)
return;
b43_set_status(dev, B43_STAT_UNINIT);
@@ -4309,7 +4307,6 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
- unsigned long flags;
int err = -EOPNOTSUPP;
/* TODO: allow WDS/AP devices to coexist */
@@ -4333,12 +4330,10 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
wl->if_type = conf->type;
memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN);
- spin_lock_irqsave(&wl->irq_lock, flags);
b43_adjust_opmode(dev);
b43_set_pretbtt(dev);
b43_set_synth_pu_delay(dev, 0);
b43_upload_card_macaddress(dev);
- spin_unlock_irqrestore(&wl->irq_lock, flags);
err = 0;
out_mutex_unlock:
@@ -4352,7 +4347,6 @@ static void b43_op_remove_interface(struct ieee80211_hw *hw,
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev = wl->current_dev;
- unsigned long flags;
b43dbg(wl, "Removing Interface type %d\n", conf->type);
@@ -4364,11 +4358,9 @@ static void b43_op_remove_interface(struct ieee80211_hw *hw,
wl->operating = 0;
- spin_lock_irqsave(&wl->irq_lock, flags);
b43_adjust_opmode(dev);
memset(wl->mac_addr, 0, ETH_ALEN);
b43_upload_card_macaddress(dev);
- spin_unlock_irqrestore(&wl->irq_lock, flags);
mutex_unlock(&wl->mutex);
}
@@ -4428,10 +4420,15 @@ static void b43_op_stop(struct ieee80211_hw *hw)
cancel_work_sync(&(wl->beacon_update_trigger));
mutex_lock(&wl->mutex);
- if (b43_status(dev) >= B43_STAT_STARTED)
- b43_wireless_core_stop(dev);
+ if (b43_status(dev) >= B43_STAT_STARTED) {
+ dev = b43_wireless_core_stop(dev);
+ if (!dev)
+ goto out_unlock;
+ }
b43_wireless_core_exit(dev);
wl->radio_enabled = 0;
+
+out_unlock:
mutex_unlock(&wl->mutex);
cancel_work_sync(&(wl->txpower_adjust_work));
@@ -4441,11 +4438,10 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, bool set)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
- unsigned long flags;
- spin_lock_irqsave(&wl->irq_lock, flags);
+ mutex_lock(&wl->mutex);
b43_update_templates(wl);
- spin_unlock_irqrestore(&wl->irq_lock, flags);
+ mutex_unlock(&wl->mutex);
return 0;
}
@@ -4526,8 +4522,13 @@ static void b43_chip_reset(struct work_struct *work)
prev_status = b43_status(dev);
/* Bring the device down... */
- if (prev_status >= B43_STAT_STARTED)
- b43_wireless_core_stop(dev);
+ if (prev_status >= B43_STAT_STARTED) {
+ dev = b43_wireless_core_stop(dev);
+ if (!dev) {
+ err = -ENODEV;
+ goto out;
+ }
+ }
if (prev_status >= B43_STAT_INITIALIZED)
b43_wireless_core_exit(dev);
@@ -4742,9 +4743,6 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl)
wldev->wl = wl;
b43_set_status(wldev, B43_STAT_UNINIT);
wldev->bad_frames_preempt = modparam_bad_frames_preempt;
- tasklet_init(&wldev->isr_tasklet,
- (void (*)(unsigned long))b43_interrupt_tasklet,
- (unsigned long)wldev);
INIT_LIST_HEAD(&wldev->list);
err = b43_wireless_core_attach(wldev);
@@ -4841,14 +4839,14 @@ static int b43_wireless_init(struct ssb_device *dev)
/* Initialize struct b43_wl */
wl->hw = hw;
- spin_lock_init(&wl->irq_lock);
- rwlock_init(&wl->tx_lock);
spin_lock_init(&wl->leds_lock);
- spin_lock_init(&wl->shm_lock);
mutex_init(&wl->mutex);
+ spin_lock_init(&wl->hardirq_lock);
INIT_LIST_HEAD(&wl->devlist);
INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work);
+ INIT_WORK(&wl->tx_work, b43_tx_work);
+ skb_queue_head_init(&wl->tx_queue);
ssb_set_devtypedata(dev, wl);
b43info(wl, "Broadcom %04X WLAN found (core revision %u)\n",
@@ -4946,8 +4944,8 @@ static int b43_suspend(struct ssb_device *dev, pm_message_t state)
wldev->suspend_in_progress = true;
wldev->suspend_init_status = b43_status(wldev);
if (wldev->suspend_init_status >= B43_STAT_STARTED)
- b43_wireless_core_stop(wldev);
- if (wldev->suspend_init_status >= B43_STAT_INITIALIZED)
+ wldev = b43_wireless_core_stop(wldev);
+ if (wldev && wldev->suspend_init_status >= B43_STAT_INITIALIZED)
b43_wireless_core_exit(wldev);
mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h
index 0406e06781d..40db03678d9 100644
--- a/drivers/net/wireless/b43/main.h
+++ b/drivers/net/wireless/b43/main.h
@@ -112,13 +112,9 @@ void b43_tsf_read(struct b43_wldev *dev, u64 * tsf);
void b43_tsf_write(struct b43_wldev *dev, u64 tsf);
u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset);
-u32 __b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset);
u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset);
-u16 __b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset);
void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value);
-void __b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value);
void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value);
-void __b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value);
u64 b43_hf_read(struct b43_wldev *dev);
void b43_hf_write(struct b43_wldev *dev, u64 value);
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index 6e704becda6..75b26e175e8 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -347,7 +347,6 @@ void b43_phy_txpower_adjust_work(struct work_struct *work)
mutex_unlock(&wl->mutex);
}
-/* Called with wl->irq_lock locked */
void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags)
{
struct b43_phy *phy = &dev->phy;
diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h
index 28e384633c3..9edd4e8e0c8 100644
--- a/drivers/net/wireless/b43/phy_common.h
+++ b/drivers/net/wireless/b43/phy_common.h
@@ -131,7 +131,7 @@ enum b43_txpwr_result {
* If the parameter "ignore_tssi" is true, the TSSI values should
* be ignored and a recalculation of the power settings should be
* done even if the TSSI values did not change.
- * This callback is called with wl->irq_lock held and must not sleep.
+ * This function may sleep, but should not.
* Must not be NULL.
* @adjust_txpower: Write the previously calculated TX power settings
* (from @recalc_txpower) to the hardware.
@@ -379,7 +379,6 @@ void b43_software_rfkill(struct b43_wldev *dev, bool blocked);
*
* Compare the current TX power output to the desired power emission
* and schedule an adjustment in case it mismatches.
- * Requires wl->irq_lock locked.
*
* @flags: OR'ed enum b43_phy_txpower_check_flags flags.
* See the docs below.
diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c
index 5afa4df0b02..382826a8da8 100644
--- a/drivers/net/wireless/b43/phy_g.c
+++ b/drivers/net/wireless/b43/phy_g.c
@@ -2823,8 +2823,6 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
b43_mac_suspend(dev);
- spin_lock_irq(&dev->wl->irq_lock);
-
/* Calculate the new attenuation values. */
bbatt = gphy->bbatt.att;
bbatt += gphy->bbatt_delta;
@@ -2864,11 +2862,6 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
gphy->rfatt.att = rfatt;
gphy->bbatt.att = bbatt;
- /* We drop the lock early, so we can sleep during hardware
- * adjustment. Possible races with op_recalc_txpower are harmless,
- * as we will be called once again in case we raced. */
- spin_unlock_irq(&dev->wl->irq_lock);
-
if (b43_debug(dev, B43_DBG_XMITPOWER))
b43dbg(dev->wl, "Adjusting TX power\n");
diff --git a/drivers/net/wireless/b43/phy_g.h b/drivers/net/wireless/b43/phy_g.h
index 718947fd41a..8569fdd4c6b 100644
--- a/drivers/net/wireless/b43/phy_g.h
+++ b/drivers/net/wireless/b43/phy_g.h
@@ -141,8 +141,7 @@ struct b43_phy_g {
int tgt_idle_tssi;
/* Current idle TSSI */
int cur_idle_tssi;
- /* The current average TSSI.
- * Needs irq_lock, as it's updated in the IRQ path. */
+ /* The current average TSSI. */
u8 average_tssi;
/* Current TX power level attenuation control values */
struct b43_bbatt bbatt;
diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c
index 3fd653c78b1..3498b68385e 100644
--- a/drivers/net/wireless/b43/pio.c
+++ b/drivers/net/wireless/b43/pio.c
@@ -32,9 +32,6 @@
#include <linux/delay.h>
-static void b43_pio_rx_work(struct work_struct *work);
-
-
static u16 generate_cookie(struct b43_pio_txqueue *q,
struct b43_pio_txpacket *pack)
{
@@ -144,7 +141,6 @@ static struct b43_pio_txqueue *b43_setup_pioqueue_tx(struct b43_wldev *dev,
q = kzalloc(sizeof(*q), GFP_KERNEL);
if (!q)
return NULL;
- spin_lock_init(&q->lock);
q->dev = dev;
q->rev = dev->dev->id.revision;
q->mmio_base = index_to_pioqueue_base(dev, index) +
@@ -179,12 +175,10 @@ static struct b43_pio_rxqueue *b43_setup_pioqueue_rx(struct b43_wldev *dev,
q = kzalloc(sizeof(*q), GFP_KERNEL);
if (!q)
return NULL;
- spin_lock_init(&q->lock);
q->dev = dev;
q->rev = dev->dev->id.revision;
q->mmio_base = index_to_pioqueue_base(dev, index) +
pio_rxqueue_offset(dev);
- INIT_WORK(&q->rx_work, b43_pio_rx_work);
/* Enable Direct FIFO RX (PIO) on the engine. */
b43_dma_direct_fifo_rx(dev, index, 1);
@@ -249,13 +243,6 @@ void b43_pio_free(struct b43_wldev *dev)
destroy_queue_tx(pio, tx_queue_AC_BK);
}
-void b43_pio_stop(struct b43_wldev *dev)
-{
- if (!b43_using_pio_transfers(dev))
- return;
- cancel_work_sync(&dev->pio.rx_queue->rx_work);
-}
-
int b43_pio_init(struct b43_wldev *dev)
{
struct b43_pio *pio = &dev->pio;
@@ -494,7 +481,6 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
{
struct b43_pio_txqueue *q;
struct ieee80211_hdr *hdr;
- unsigned long flags;
unsigned int hdrlen, total_len;
int err = 0;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -512,20 +498,18 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
q = select_queue_by_priority(dev, skb_get_queue_mapping(skb));
}
- spin_lock_irqsave(&q->lock, flags);
-
hdrlen = b43_txhdr_size(dev);
total_len = roundup(skb->len + hdrlen, 4);
if (unlikely(total_len > q->buffer_size)) {
err = -ENOBUFS;
b43dbg(dev->wl, "PIO: TX packet longer than queue.\n");
- goto out_unlock;
+ goto out;
}
if (unlikely(q->free_packet_slots == 0)) {
err = -ENOBUFS;
b43warn(dev->wl, "PIO: TX packet overflow.\n");
- goto out_unlock;
+ goto out;
}
B43_WARN_ON(q->buffer_used > q->buffer_size);
@@ -534,7 +518,7 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
err = -EBUSY;
ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
q->stopped = 1;
- goto out_unlock;
+ goto out;
}
/* Assign the queue number to the ring (if not already done before)
@@ -548,11 +532,11 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
* anymore and must not transmit it unencrypted. */
dev_kfree_skb_any(skb);
err = 0;
- goto out_unlock;
+ goto out;
}
if (unlikely(err)) {
b43err(dev->wl, "PIO transmission failure\n");
- goto out_unlock;
+ goto out;
}
q->nr_tx_packets++;
@@ -564,13 +548,10 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
q->stopped = 1;
}
-out_unlock:
- spin_unlock_irqrestore(&q->lock, flags);
-
+out:
return err;
}
-/* Called with IRQs disabled. */
void b43_pio_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status)
{
@@ -584,8 +565,6 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev,
return;
B43_WARN_ON(!pack);
- spin_lock(&q->lock); /* IRQs are already disabled. */
-
info = IEEE80211_SKB_CB(pack->skb);
b43_fill_txstatus_report(dev, info, status);
@@ -603,8 +582,6 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev,
ieee80211_wake_queue(dev->wl->hw, q->queue_prio);
q->stopped = 0;
}
-
- spin_unlock(&q->lock);
}
void b43_pio_get_tx_stats(struct b43_wldev *dev,
@@ -612,17 +589,14 @@ void b43_pio_get_tx_stats(struct b43_wldev *dev,
{
const int nr_queues = dev->wl->hw->queues;
struct b43_pio_txqueue *q;
- unsigned long flags;
int i;
for (i = 0; i < nr_queues; i++) {
q = select_queue_by_priority(dev, i);
- spin_lock_irqsave(&q->lock, flags);
stats[i].len = B43_PIO_MAX_NR_TXPACKETS - q->free_packet_slots;
stats[i].limit = B43_PIO_MAX_NR_TXPACKETS;
stats[i].count = q->nr_tx_packets;
- spin_unlock_irqrestore(&q->lock, flags);
}
}
@@ -760,37 +734,23 @@ rx_error:
return 1;
}
-/* RX workqueue. We can sleep, yay! */
-static void b43_pio_rx_work(struct work_struct *work)
+void b43_pio_rx(struct b43_pio_rxqueue *q)
{
- struct b43_pio_rxqueue *q = container_of(work, struct b43_pio_rxqueue,
- rx_work);
- unsigned int budget = 50;
+ unsigned int count = 0;
bool stop;
- do {
- spin_lock_irq(&q->lock);
+ while (1) {
stop = (pio_rx_frame(q) == 0);
- spin_unlock_irq(&q->lock);
- cond_resched();
if (stop)
break;
- } while (--budget);
-}
-
-/* Called with IRQs disabled. */
-void b43_pio_rx(struct b43_pio_rxqueue *q)
-{
- /* Due to latency issues we must run the RX path in
- * a workqueue to be able to schedule between packets. */
- ieee80211_queue_work(q->dev->wl->hw, &q->rx_work);
+ cond_resched();
+ if (WARN_ON_ONCE(++count > 10000))
+ break;
+ }
}
static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q)
{
- unsigned long flags;
-
- spin_lock_irqsave(&q->lock, flags);
if (q->rev >= 8) {
b43_piotx_write32(q, B43_PIO8_TXCTL,
b43_piotx_read32(q, B43_PIO8_TXCTL)
@@ -800,14 +760,10 @@ static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q)
b43_piotx_read16(q, B43_PIO_TXCTL)
| B43_PIO_TXCTL_SUSPREQ);
}
- spin_unlock_irqrestore(&q->lock, flags);
}
static void b43_pio_tx_resume_queue(struct b43_pio_txqueue *q)
{
- unsigned long flags;
-
- spin_lock_irqsave(&q->lock, flags);
if (q->rev >= 8) {
b43_piotx_write32(q, B43_PIO8_TXCTL,
b43_piotx_read32(q, B43_PIO8_TXCTL)
@@ -817,7 +773,6 @@ static void b43_pio_tx_resume_queue(struct b43_pio_txqueue *q)
b43_piotx_read16(q, B43_PIO_TXCTL)
& ~B43_PIO_TXCTL_SUSPREQ);
}
- spin_unlock_irqrestore(&q->lock, flags);
}
void b43_pio_tx_suspend(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/b43/pio.h b/drivers/net/wireless/b43/pio.h
index 6c174c91ca2..7dd649c9dda 100644
--- a/drivers/net/wireless/b43/pio.h
+++ b/drivers/net/wireless/b43/pio.h
@@ -70,7 +70,6 @@ struct b43_pio_txpacket {
struct b43_pio_txqueue {
struct b43_wldev *dev;
- spinlock_t lock;
u16 mmio_base;
/* The device queue buffer size in bytes. */
@@ -103,12 +102,8 @@ struct b43_pio_txqueue {
struct b43_pio_rxqueue {
struct b43_wldev *dev;
- spinlock_t lock;
u16 mmio_base;
- /* Work to reduce latency issues on RX. */
- struct work_struct rx_work;
-
/* Shortcut to the 802.11 core revision. This is to
* avoid horrible pointer dereferencing in the fastpaths. */
u8 rev;
@@ -162,7 +157,6 @@ static inline void b43_piorx_write32(struct b43_pio_rxqueue *q,
int b43_pio_init(struct b43_wldev *dev);
-void b43_pio_stop(struct b43_wldev *dev);
void b43_pio_free(struct b43_wldev *dev);
int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb);
diff --git a/drivers/net/wireless/b43/sysfs.c b/drivers/net/wireless/b43/sysfs.c
index 5adaa3692d7..f1ae4e05a32 100644
--- a/drivers/net/wireless/b43/sysfs.c
+++ b/drivers/net/wireless/b43/sysfs.c
@@ -94,7 +94,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev,
const char *buf, size_t count)
{
struct b43_wldev *wldev = dev_to_b43_wldev(dev);
- unsigned long flags;
int err;
int mode;
@@ -120,7 +119,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev,
}
mutex_lock(&wldev->wl->mutex);
- spin_lock_irqsave(&wldev->wl->irq_lock, flags);
if (wldev->phy.ops->interf_mitigation) {
err = wldev->phy.ops->interf_mitigation(wldev, mode);
@@ -132,7 +130,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev,
err = -ENOSYS;
mmiowb();
- spin_unlock_irqrestore(&wldev->wl->irq_lock, flags);
mutex_unlock(&wldev->wl->mutex);
return err ? err : count;
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index e7075d2c775..14f541248b5 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -267,11 +267,11 @@ int b43_generate_txhdr(struct b43_wldev *dev,
*/
ieee80211_get_tkip_key(info->control.hw_key, skb_frag,
IEEE80211_TKIP_P1_KEY, (u8*)phase1key);
- /* phase1key is in host endian */
- for (i = 0; i < 5; i++)
- phase1key[i] = cpu_to_le16(phase1key[i]);
-
- memcpy(txhdr->iv, phase1key, 10);
+ /* phase1key is in host endian. Copy to little-endian txhdr->iv. */
+ for (i = 0; i < 5; i++) {
+ txhdr->iv[i * 2 + 0] = phase1key[i];
+ txhdr->iv[i * 2 + 1] = phase1key[i] >> 8;
+ }
/* iv16 */
memcpy(txhdr->iv + 10, ((u8 *) wlhdr) + wlhdr_len, 3);
} else {