Commit 36dbd954 authored by Michael Buesch's avatar Michael Buesch Committed by John W. Linville

b43: Use a threaded IRQ handler

Use a threaded IRQ handler to allow locking the mutex and
sleeping while executing an interrupt.
This removes usage of the irq_lock spinlock, but introduces
a new hardirq_lock, which is _only_ used for the PCI/SSB lowlevel
hard-irq handler. Sleeping busses (SDIO) will use mutex instead.
Signed-off-by: default avatarMichael Buesch <mb@bu3sch.de>
Tested-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent b275f285
...@@ -616,6 +616,12 @@ struct b43_wl { ...@@ -616,6 +616,12 @@ struct b43_wl {
/* Pointer to the ieee80211 hardware data structure */ /* Pointer to the ieee80211 hardware data structure */
struct ieee80211_hw *hw; 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 /* 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 * 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). * to be dynamically lowered at runtime (Firmware does not support QoS).
...@@ -623,8 +629,6 @@ struct b43_wl { ...@@ -623,8 +629,6 @@ struct b43_wl {
* from the mac80211 subsystem. */ * from the mac80211 subsystem. */
u16 mac80211_initially_registered_queues; u16 mac80211_initially_registered_queues;
struct mutex mutex;
spinlock_t irq_lock;
/* R/W lock for data transmission. /* R/W lock for data transmission.
* Transmissions on 2+ queues can run concurrently, but somebody else * Transmissions on 2+ queues can run concurrently, but somebody else
* might sync with TX by write_lock_irqsave()'ing. */ * might sync with TX by write_lock_irqsave()'ing. */
...@@ -665,8 +669,7 @@ struct b43_wl { ...@@ -665,8 +669,7 @@ struct b43_wl {
bool radiotap_enabled; bool radiotap_enabled;
bool radio_enabled; bool radio_enabled;
/* The beacon we are currently using (AP or IBSS mode). /* The beacon we are currently using (AP or IBSS mode). */
* This beacon stuff is protected by the irq_lock. */
struct sk_buff *current_beacon; struct sk_buff *current_beacon;
bool beacon0_uploaded; bool beacon0_uploaded;
bool beacon1_uploaded; bool beacon1_uploaded;
...@@ -754,14 +757,6 @@ enum { ...@@ -754,14 +757,6 @@ enum {
smp_wmb(); \ smp_wmb(); \
} while (0) } 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) */ /* Data structure for one wireless device (802.11 core) */
struct b43_wldev { struct b43_wldev {
struct ssb_device *dev; struct ssb_device *dev;
...@@ -807,14 +802,12 @@ struct b43_wldev { ...@@ -807,14 +802,12 @@ struct b43_wldev {
u32 dma_reason[6]; u32 dma_reason[6];
/* The currently active generic-interrupt mask. */ /* The currently active generic-interrupt mask. */
u32 irq_mask; u32 irq_mask;
/* Link Quality calculation context. */ /* Link Quality calculation context. */
struct b43_noise_calculation noisecalc; struct b43_noise_calculation noisecalc;
/* if > 0 MAC is suspended. if == 0 MAC is enabled. */ /* if > 0 MAC is suspended. if == 0 MAC is enabled. */
int mac_suspended; int mac_suspended;
/* Interrupt Service Routine tasklet (bottom-half) */
struct tasklet_struct isr_tasklet;
/* Periodic tasks */ /* Periodic tasks */
struct delayed_work periodic_work; struct delayed_work periodic_work;
unsigned int periodic_state; unsigned int periodic_state;
......
...@@ -46,8 +46,6 @@ struct b43_debugfs_fops { ...@@ -46,8 +46,6 @@ struct b43_debugfs_fops {
struct file_operations fops; struct file_operations fops;
/* Offset of struct b43_dfs_file in struct b43_dfsentry */ /* Offset of struct b43_dfs_file in struct b43_dfsentry */
size_t file_struct_offset; size_t file_struct_offset;
/* Take wl->irq_lock before calling read/write? */
bool take_irqlock;
}; };
static inline static inline
...@@ -372,14 +370,12 @@ static ssize_t txstat_read_file(struct b43_wldev *dev, ...@@ -372,14 +370,12 @@ static ssize_t txstat_read_file(struct b43_wldev *dev,
{ {
struct b43_txstatus_log *log = &dev->dfsentry->txstatlog; struct b43_txstatus_log *log = &dev->dfsentry->txstatlog;
ssize_t count = 0; ssize_t count = 0;
unsigned long flags;
int i, idx; int i, idx;
struct b43_txstatus *stat; struct b43_txstatus *stat;
spin_lock_irqsave(&log->lock, flags);
if (log->end < 0) { if (log->end < 0) {
fappend("Nothing transmitted, yet\n"); fappend("Nothing transmitted, yet\n");
goto out_unlock; goto out;
} }
fappend("b43 TX status reports:\n\n" fappend("b43 TX status reports:\n\n"
"index | cookie | seq | phy_stat | frame_count | " "index | cookie | seq | phy_stat | frame_count | "
...@@ -409,13 +405,11 @@ static ssize_t txstat_read_file(struct b43_wldev *dev, ...@@ -409,13 +405,11 @@ static ssize_t txstat_read_file(struct b43_wldev *dev,
break; break;
i++; i++;
} }
out_unlock: out:
spin_unlock_irqrestore(&log->lock, flags);
return count; return count;
} }
/* wl->irq_lock is locked */
static int restart_write_file(struct b43_wldev *dev, static int restart_write_file(struct b43_wldev *dev,
const char *buf, size_t count) const char *buf, size_t count)
{ {
...@@ -556,12 +550,7 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf, ...@@ -556,12 +550,7 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
goto out_unlock; goto out_unlock;
} }
memset(buf, 0, bufsize); memset(buf, 0, bufsize);
if (dfops->take_irqlock) { ret = dfops->read(dev, buf, bufsize);
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);
if (ret <= 0) { if (ret <= 0) {
free_pages((unsigned long)buf, buforder); free_pages((unsigned long)buf, buforder);
err = ret; err = ret;
...@@ -623,12 +612,7 @@ static ssize_t b43_debugfs_write(struct file *file, ...@@ -623,12 +612,7 @@ static ssize_t b43_debugfs_write(struct file *file,
err = -EFAULT; err = -EFAULT;
goto out_freepage; goto out_freepage;
} }
if (dfops->take_irqlock) { err = dfops->write(dev, buf, count);
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);
if (err) if (err)
goto out_freepage; goto out_freepage;
...@@ -641,7 +625,7 @@ static ssize_t b43_debugfs_write(struct file *file, ...@@ -641,7 +625,7 @@ static ssize_t b43_debugfs_write(struct file *file,
} }
#define B43_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \ #define B43_DEBUGFS_FOPS(name, _read, _write) \
static struct b43_debugfs_fops fops_##name = { \ static struct b43_debugfs_fops fops_##name = { \
.read = _read, \ .read = _read, \
.write = _write, \ .write = _write, \
...@@ -652,20 +636,19 @@ static ssize_t b43_debugfs_write(struct file *file, ...@@ -652,20 +636,19 @@ static ssize_t b43_debugfs_write(struct file *file,
}, \ }, \
.file_struct_offset = offsetof(struct b43_dfsentry, \ .file_struct_offset = offsetof(struct b43_dfsentry, \
file_##name), \ file_##name), \
.take_irqlock = _take_irqlock, \
} }
B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file, 1); B43_DEBUGFS_FOPS(shm16read, shm16read__read_file, shm16read__write_file);
B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file, 1); B43_DEBUGFS_FOPS(shm16write, NULL, shm16write__write_file);
B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file, 1); B43_DEBUGFS_FOPS(shm32read, shm32read__read_file, shm32read__write_file);
B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file, 1); B43_DEBUGFS_FOPS(shm32write, NULL, shm32write__write_file);
B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file, 1); B43_DEBUGFS_FOPS(mmio16read, mmio16read__read_file, mmio16read__write_file);
B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file, 1); B43_DEBUGFS_FOPS(mmio16write, NULL, mmio16write__write_file);
B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file, 1); B43_DEBUGFS_FOPS(mmio32read, mmio32read__read_file, mmio32read__write_file);
B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file, 1); B43_DEBUGFS_FOPS(mmio32write, NULL, mmio32write__write_file);
B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0); B43_DEBUGFS_FOPS(txstat, txstat_read_file, NULL);
B43_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1); B43_DEBUGFS_FOPS(restart, NULL, restart_write_file);
B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL, 0); B43_DEBUGFS_FOPS(loctls, loctls_read_file, NULL);
bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature)
...@@ -738,7 +721,6 @@ void b43_debugfs_add_device(struct b43_wldev *dev) ...@@ -738,7 +721,6 @@ void b43_debugfs_add_device(struct b43_wldev *dev)
return; return;
} }
log->end = -1; log->end = -1;
spin_lock_init(&log->lock);
dev->dfsentry = e; dev->dfsentry = e;
...@@ -822,7 +804,6 @@ void b43_debugfs_remove_device(struct b43_wldev *dev) ...@@ -822,7 +804,6 @@ void b43_debugfs_remove_device(struct b43_wldev *dev)
kfree(e); kfree(e);
} }
/* Called with IRQs disabled. */
void b43_debugfs_log_txstat(struct b43_wldev *dev, void b43_debugfs_log_txstat(struct b43_wldev *dev,
const struct b43_txstatus *status) const struct b43_txstatus *status)
{ {
...@@ -834,14 +815,12 @@ void b43_debugfs_log_txstat(struct b43_wldev *dev, ...@@ -834,14 +815,12 @@ void b43_debugfs_log_txstat(struct b43_wldev *dev,
if (!e) if (!e)
return; return;
log = &e->txstatlog; log = &e->txstatlog;
spin_lock(&log->lock); /* IRQs are already disabled. */
i = log->end + 1; i = log->end + 1;
if (i == B43_NR_LOGGED_TXSTATUS) if (i == B43_NR_LOGGED_TXSTATUS)
i = 0; i = 0;
log->end = i; log->end = i;
cur = &(log->log[i]); cur = &(log->log[i]);
memcpy(cur, status, sizeof(*cur)); memcpy(cur, status, sizeof(*cur));
spin_unlock(&log->lock);
} }
void b43_debugfs_init(void) void b43_debugfs_init(void)
......
...@@ -23,9 +23,10 @@ struct dentry; ...@@ -23,9 +23,10 @@ struct dentry;
#define B43_NR_LOGGED_TXSTATUS 100 #define B43_NR_LOGGED_TXSTATUS 100
struct b43_txstatus_log { struct b43_txstatus_log {
/* This structure is protected by wl->mutex */
struct b43_txstatus *log; struct b43_txstatus *log;
int end; int end;
spinlock_t lock;
}; };
struct b43_dfs_file { struct b43_dfs_file {
......
...@@ -1387,7 +1387,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) ...@@ -1387,7 +1387,6 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
return err; return err;
} }
/* Called with IRQs disabled. */
void b43_dma_handle_txstatus(struct b43_wldev *dev, void b43_dma_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status) const struct b43_txstatus *status)
{ {
...@@ -1402,7 +1401,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ...@@ -1402,7 +1401,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
if (unlikely(!ring)) if (unlikely(!ring))
return; return;
spin_lock(&ring->lock); /* IRQs are already disabled. */ spin_lock_irq(&ring->lock);
B43_WARN_ON(!ring->tx); B43_WARN_ON(!ring->tx);
ops = ring->ops; ops = ring->ops;
...@@ -1463,7 +1462,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev, ...@@ -1463,7 +1462,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
} }
} }
spin_unlock(&ring->lock); spin_unlock_irq(&ring->lock);
} }
void b43_dma_get_tx_stats(struct b43_wldev *dev, void b43_dma_get_tx_stats(struct b43_wldev *dev,
......
...@@ -291,7 +291,7 @@ static struct ieee80211_supported_band b43_band_2GHz = { ...@@ -291,7 +291,7 @@ static struct ieee80211_supported_band b43_band_2GHz = {
static void b43_wireless_core_exit(struct b43_wldev *dev); static void b43_wireless_core_exit(struct b43_wldev *dev);
static int b43_wireless_core_init(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_wireless_core_start(struct b43_wldev *dev);
static int b43_ratelimit(struct b43_wl *wl) static int b43_ratelimit(struct b43_wl *wl)
...@@ -685,16 +685,6 @@ static void b43_short_slot_timing_disable(struct b43_wldev *dev) ...@@ -685,16 +685,6 @@ static void b43_short_slot_timing_disable(struct b43_wldev *dev)
b43_set_slot_time(dev, 20); 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 /* DummyTransmission function, as documented on
* http://bcm-v4.sipsolutions.net/802.11/DummyTransmission * http://bcm-v4.sipsolutions.net/802.11/DummyTransmission
*/ */
...@@ -720,8 +710,7 @@ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on) ...@@ -720,8 +710,7 @@ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on)
buffer[0] = 0x000B846E; buffer[0] = 0x000B846E;
} }
spin_lock_irq(&wl->irq_lock); write_lock_irq(&wl->tx_lock);
write_lock(&wl->tx_lock);
for (i = 0; i < 5; i++) for (i = 0; i < 5; i++)
b43_ram_write(dev, i * 4, buffer[i]); b43_ram_write(dev, i * 4, buffer[i]);
...@@ -779,8 +768,7 @@ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on) ...@@ -779,8 +768,7 @@ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on)
if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5)
b43_radio_write16(dev, 0x0051, 0x0037); b43_radio_write16(dev, 0x0051, 0x0037);
write_unlock(&wl->tx_lock); write_unlock_irq(&wl->tx_lock);
spin_unlock_irq(&wl->irq_lock);
} }
static void key_write(struct b43_wldev *dev, static void key_write(struct b43_wldev *dev,
...@@ -1620,6 +1608,27 @@ static void handle_irq_beacon(struct b43_wldev *dev) ...@@ -1620,6 +1608,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) static void b43_beacon_update_trigger_work(struct work_struct *work)
{ {
struct b43_wl *wl = container_of(work, struct b43_wl, struct b43_wl *wl = container_of(work, struct b43_wl,
...@@ -1629,19 +1638,22 @@ static void b43_beacon_update_trigger_work(struct work_struct *work) ...@@ -1629,19 +1638,22 @@ static void b43_beacon_update_trigger_work(struct work_struct *work)
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
dev = wl->current_dev; dev = wl->current_dev;
if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) { if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) {
spin_lock_irq(&wl->irq_lock); if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) {
/* update beacon right away or defer to irq */ /* wl->mutex is enough. */
handle_irq_beacon(dev); b43_do_beacon_update_trigger_work(dev);
/* The handler might have updated the IRQ mask. */ mmiowb();
b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask); } else {
mmiowb(); spin_lock_irq(&wl->hardirq_lock);
spin_unlock_irq(&wl->irq_lock); b43_do_beacon_update_trigger_work(dev);
mmiowb();
spin_unlock_irq(&wl->hardirq_lock);
}
} }
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
} }
/* Asynchronously update the packet templates in template RAM. /* 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) static void b43_update_templates(struct b43_wl *wl)
{ {
struct sk_buff *beacon; struct sk_buff *beacon;
...@@ -1778,18 +1790,15 @@ static void handle_irq_ucode_debug(struct b43_wldev *dev) ...@@ -1778,18 +1790,15 @@ static void handle_irq_ucode_debug(struct b43_wldev *dev)
B43_DEBUGIRQ_REASON_REG, B43_DEBUGIRQ_ACK); B43_DEBUGIRQ_REASON_REG, B43_DEBUGIRQ_ACK);
} }
/* Interrupt handler bottom-half */ static void b43_do_interrupt_thread(struct b43_wldev *dev)
static void b43_interrupt_tasklet(struct b43_wldev *dev)
{ {
u32 reason; u32 reason;
u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; u32 dma_reason[ARRAY_SIZE(dev->dma_reason)];
u32 merged_dma_reason = 0; u32 merged_dma_reason = 0;
int i; 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; reason = dev->irq_reason;
for (i = 0; i < ARRAY_SIZE(dma_reason); i++) { for (i = 0; i < ARRAY_SIZE(dma_reason); i++) {
...@@ -1822,8 +1831,6 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev) ...@@ -1822,8 +1831,6 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev)
dma_reason[2], dma_reason[3], dma_reason[2], dma_reason[3],
dma_reason[4], dma_reason[5]); dma_reason[4], dma_reason[5]);
b43_controller_restart(dev, "DMA error"); b43_controller_restart(dev, "DMA error");
mmiowb();
spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
return; return;
} }
if (merged_dma_reason & B43_DMAIRQ_NONFATALMASK) { if (merged_dma_reason & B43_DMAIRQ_NONFATALMASK) {
...@@ -1867,47 +1874,36 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev) ...@@ -1867,47 +1874,36 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev)
if (reason & B43_IRQ_TX_OK) if (reason & B43_IRQ_TX_OK)
handle_irq_transmit_status(dev); 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); 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]); mutex_lock(&dev->wl->mutex);
b43_write32(dev, B43_MMIO_DMA1_REASON, dev->dma_reason[1]); b43_do_interrupt_thread(dev);
b43_write32(dev, B43_MMIO_DMA2_REASON, dev->dma_reason[2]); mmiowb();
b43_write32(dev, B43_MMIO_DMA3_REASON, dev->dma_reason[3]); mutex_unlock(&dev->wl->mutex);
b43_write32(dev, B43_MMIO_DMA4_REASON, dev->dma_reason[4]);
/* Unused ring return IRQ_HANDLED;
b43_write32(dev, B43_MMIO_DMA5_REASON, dev->dma_reason[5]);
*/
} }
/* Interrupt handler top-half */ static irqreturn_t b43_do_interrupt(struct b43_wldev *dev)
static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
{ {
irqreturn_t ret = IRQ_NONE;
struct b43_wldev *dev = dev_id;
u32 reason; 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); reason = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);
if (reason == 0xffffffff) /* shared IRQ */ if (reason == 0xffffffff) /* shared IRQ */
goto out; return IRQ_NONE;
ret = IRQ_HANDLED;
reason &= dev->irq_mask; reason &= dev->irq_mask;
if (!reason) if (!reason)
goto out; return IRQ_HANDLED;
dev->dma_reason[0] = b43_read32(dev, B43_MMIO_DMA0_REASON) dev->dma_reason[0] = b43_read32(dev, B43_MMIO_DMA0_REASON)
& 0x0001DC00; & 0x0001DC00;
...@@ -1924,15 +1920,38 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id) ...@@ -1924,15 +1920,38 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
& 0x0000DC00; & 0x0000DC00;
*/ */
b43_interrupt_ack(dev, reason); /* ACK the interrupt. */
/* disable all IRQs. They are enabled again in the bottom half. */ 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); 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; 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(); mmiowb();
spin_unlock(&dev->wl->irq_lock); spin_unlock(&dev->wl->hardirq_lock);
return ret; return ret;
} }
...@@ -3038,15 +3057,12 @@ static void b43_security_init(struct b43_wldev *dev) ...@@ -3038,15 +3057,12 @@ static void b43_security_init(struct b43_wldev *dev)
static int b43_rng_read(struct hwrng *rng, u32 *data) static int b43_rng_read(struct hwrng *rng, u32 *data)
{ {
struct b43_wl *wl = (struct b43_wl *)rng->priv; struct b43_wl *wl = (struct b43_wl *)rng->priv;
unsigned long flags;
/* Don't take wl->mutex here, as it could deadlock with /* FIXME: We need to take wl->mutex here to make sure the device
* hwrng internal locking. It's not needed to take * is not going away from under our ass. However it could deadlock
* wl->mutex here, anyway. */ * with hwrng internal locking. */
spin_lock_irqsave(&wl->irq_lock, flags);
*data = b43_read16(wl->current_dev, B43_MMIO_RNG); *data = b43_read16(wl->current_dev, B43_MMIO_RNG);
spin_unlock_irqrestore(&wl->irq_lock, flags);
return (sizeof(u16)); return (sizeof(u16));
} }
...@@ -3283,22 +3299,20 @@ static int b43_op_get_tx_stats(struct ieee80211_hw *hw, ...@@ -3283,22 +3299,20 @@ static int b43_op_get_tx_stats(struct ieee80211_hw *hw,
struct ieee80211_tx_queue_stats *stats) struct ieee80211_tx_queue_stats *stats)
{ {
struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev = wl->current_dev; struct b43_wldev *dev;
unsigned long flags;
int err = -ENODEV; int err = -ENODEV;
if (!dev) mutex_lock(&wl->mutex);
goto out; dev = wl->current_dev;
spin_lock_irqsave(&wl->irq_lock, flags); if (dev && b43_status(dev) >= B43_STAT_STARTED) {
if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
if (b43_using_pio_transfers(dev)) if (b43_using_pio_transfers(dev))
b43_pio_get_tx_stats(dev, stats); b43_pio_get_tx_stats(dev, stats);
else else
b43_dma_get_tx_stats(dev, stats); b43_dma_get_tx_stats(dev, stats);
err = 0; err = 0;
} }
spin_unlock_irqrestore(&wl->irq_lock, flags); mutex_unlock(&wl->mutex);
out:
return err; return err;
} }
...@@ -3306,11 +3320,10 @@ static int b43_op_get_stats(struct ieee80211_hw *hw, ...@@ -3306,11 +3320,10 @@ static int b43_op_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats) struct ieee80211_low_level_stats *stats)
{ {
struct b43_wl *wl = hw_to_b43_wl(hw); 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)); memcpy(stats, &wl->ieee_stats, sizeof(*stats));
spin_unlock_irqrestore(&wl->irq_lock, flags); mutex_unlock(&wl->mutex);
return 0; return 0;
} }
...@@ -3322,7 +3335,6 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw) ...@@ -3322,7 +3335,6 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw)
u64 tsf; u64 tsf;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
spin_lock_irq(&wl->irq_lock);
dev = wl->current_dev; dev = wl->current_dev;
if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED))
...@@ -3330,7 +3342,6 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw) ...@@ -3330,7 +3342,6 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw)
else else
tsf = 0; tsf = 0;
spin_unlock_irq(&wl->irq_lock);
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
return tsf; return tsf;
...@@ -3342,13 +3353,11 @@ static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf) ...@@ -3342,13 +3353,11 @@ static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf)
struct b43_wldev *dev; struct b43_wldev *dev;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
spin_lock_irq(&wl->irq_lock);
dev = wl->current_dev; dev = wl->current_dev;
if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED)) if (dev && (b43_status(dev) >= B43_STAT_INITIALIZED))
b43_tsf_write(dev, tsf); b43_tsf_write(dev, tsf);
spin_unlock_irq(&wl->irq_lock);
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
} }
...@@ -3434,7 +3443,7 @@ static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan) ...@@ -3434,7 +3443,7 @@ static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan)
prev_status = b43_status(down_dev); prev_status = b43_status(down_dev);
/* Shutdown the currently running core. */ /* Shutdown the currently running core. */
if (prev_status >= B43_STAT_STARTED) 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) if (prev_status >= B43_STAT_INITIALIZED)
b43_wireless_core_exit(down_dev); b43_wireless_core_exit(down_dev);
...@@ -3498,7 +3507,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -3498,7 +3507,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
struct b43_wldev *dev; struct b43_wldev *dev;
struct b43_phy *phy; struct b43_phy *phy;
struct ieee80211_conf *conf = &hw->conf; struct ieee80211_conf *conf = &hw->conf;
unsigned long flags;
int antenna; int antenna;
int err = 0; int err = 0;
...@@ -3529,13 +3537,11 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) ...@@ -3529,13 +3537,11 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
/* Adjust the desired TX power level. */ /* Adjust the desired TX power level. */
if (conf->power_level != 0) { if (conf->power_level != 0) {
spin_lock_irqsave(&wl->irq_lock, flags);
if (conf->power_level != phy->desired_txpower) { if (conf->power_level != phy->desired_txpower) {
phy->desired_txpower = conf->power_level; phy->desired_txpower = conf->power_level;
b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME | b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME |
B43_TXPWR_IGNORE_TSSI); B43_TXPWR_IGNORE_TSSI);
} }
spin_unlock_irqrestore(&wl->irq_lock, flags);
} }
/* Antennas for RX and management frame TX. */ /* Antennas for RX and management frame TX. */
...@@ -3620,7 +3626,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -3620,7 +3626,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
{ {
struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev; struct b43_wldev *dev;
unsigned long flags;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
...@@ -3630,7 +3635,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -3630,7 +3635,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
B43_WARN_ON(wl->vif != vif); B43_WARN_ON(wl->vif != vif);
spin_lock_irqsave(&wl->irq_lock, flags);
if (changed & BSS_CHANGED_BSSID) { if (changed & BSS_CHANGED_BSSID) {
if (conf->bssid) if (conf->bssid)
memcpy(wl->bssid, conf->bssid, ETH_ALEN); memcpy(wl->bssid, conf->bssid, ETH_ALEN);
...@@ -3648,7 +3652,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw, ...@@ -3648,7 +3652,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BSSID) if (changed & BSS_CHANGED_BSSID)
b43_write_mac_bssid_templates(dev); b43_write_mac_bssid_templates(dev);
} }
spin_unlock_irqrestore(&wl->irq_lock, flags);
b43_mac_suspend(dev); b43_mac_suspend(dev);
...@@ -3683,18 +3686,15 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ...@@ -3683,18 +3686,15 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
u8 algorithm; u8 algorithm;
u8 index; u8 index;
int err; int err;
unsigned long flags;
static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
if (modparam_nohwcrypt) if (modparam_nohwcrypt)
return -ENOSPC; /* User disabled HW-crypto */ return -ENOSPC; /* User disabled HW-crypto */
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
spin_lock_irq(&wl->irq_lock); write_lock_irqsave(&wl->tx_lock, flags);
write_lock(&wl->tx_lock); /* mutex -> Every config operation must take it.
/* 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 * tx_lock -> We modify the dev->key array, which is accessed
* in the TX handler. * in the TX handler.
*/ */
...@@ -3789,8 +3789,7 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ...@@ -3789,8 +3789,7 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
sta ? sta->addr : bcast_addr); sta ? sta->addr : bcast_addr);
b43_dump_keymemory(dev); b43_dump_keymemory(dev);
} }
write_unlock(&wl->tx_lock); write_unlock_irqrestore(&wl->tx_lock, flags);
spin_unlock_irq(&wl->irq_lock);
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
return err; return err;
...@@ -3801,15 +3800,15 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw, ...@@ -3801,15 +3800,15 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw,
u64 multicast) u64 multicast)
{ {
struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev = wl->current_dev; struct b43_wldev *dev;
unsigned long flags;
mutex_lock(&wl->mutex);
dev = wl->current_dev;
if (!dev) { if (!dev) {
*fflags = 0; *fflags = 0;
return; goto out_unlock;
} }
spin_lock_irqsave(&wl->irq_lock, flags);
*fflags &= FIF_PROMISC_IN_BSS | *fflags &= FIF_PROMISC_IN_BSS |
FIF_ALLMULTI | FIF_ALLMULTI |
FIF_FCSFAIL | FIF_FCSFAIL |
...@@ -3830,41 +3829,66 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw, ...@@ -3830,41 +3829,66 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw,
if (changed && b43_status(dev) >= B43_STAT_INITIALIZED) if (changed && b43_status(dev) >= B43_STAT_INITIALIZED)
b43_adjust_opmode(dev); b43_adjust_opmode(dev);
spin_unlock_irqrestore(&wl->irq_lock, flags);
out_unlock:
mutex_unlock(&wl->mutex);
} }
/* Locking: wl->mutex */ /* Locking: wl->mutex
static void b43_wireless_core_stop(struct b43_wldev *dev) * 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; struct b43_wl *wl = dev->wl;
unsigned long flags; struct b43_wldev *orig_dev;
if (b43_status(dev) < B43_STAT_STARTED) redo:
return; if (!dev || b43_status(dev) < B43_STAT_STARTED)
return dev;
/* Disable and sync interrupts. We must do this before than /* Disable periodic work. Unlock to avoid deadlocks. */
* setting the status to INITIALIZED, as the interrupt handler mutex_unlock(&wl->mutex);
* won't care about IRQs then. */ cancel_delayed_work_sync(&dev->periodic_work);
spin_lock_irqsave(&wl->irq_lock, flags); mutex_lock(&wl->mutex);
b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); dev = wl->current_dev;
b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* flush */ if (!dev || b43_status(dev) < B43_STAT_STARTED) {
spin_unlock_irqrestore(&wl->irq_lock, flags); /* Whoops, aliens ate up the device while we were unlocked. */
b43_synchronize_irq(dev); return dev;
}
write_lock_irqsave(&wl->tx_lock, flags); /* Disable interrupts on the device. */
b43_set_status(dev, B43_STAT_INITIALIZED); b43_set_status(dev, B43_STAT_INITIALIZED);
write_unlock_irqrestore(&wl->tx_lock, flags); if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) {
/* wl->mutex is locked. That is enough. */
b43_pio_stop(dev); 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); mutex_unlock(&wl->mutex);
/* Must unlock as it would otherwise deadlock. No races here. synchronize_irq(dev->dev->irq);
* Cancel the possibly running self-rearming periodic work. */
cancel_delayed_work_sync(&dev->periodic_work);
mutex_lock(&wl->mutex); 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));
b43_pio_stop(dev);
b43_mac_suspend(dev); b43_mac_suspend(dev);
free_irq(dev->dev->irq, dev); free_irq(dev->dev->irq, dev);
b43dbg(wl, "Wireless interface stopped\n"); b43dbg(wl, "Wireless interface stopped\n");
return dev;
} }
/* Locking: wl->mutex */ /* Locking: wl->mutex */
...@@ -3875,8 +3899,9 @@ static int b43_wireless_core_start(struct b43_wldev *dev) ...@@ -3875,8 +3899,9 @@ static int b43_wireless_core_start(struct b43_wldev *dev)
B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED); B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED);
drain_txstatus_queue(dev); drain_txstatus_queue(dev);
err = request_irq(dev->dev->irq, b43_interrupt_handler, err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler,
IRQF_SHARED, KBUILD_MODNAME, dev); b43_interrupt_thread_handler,
IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) { if (err) {
b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq);
goto out; goto out;
...@@ -4155,8 +4180,8 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) ...@@ -4155,8 +4180,8 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
{ {
u32 macctl; u32 macctl;
B43_WARN_ON(b43_status(dev) > B43_STAT_INITIALIZED); B43_WARN_ON(dev && b43_status(dev) > B43_STAT_INITIALIZED);
if (b43_status(dev) != B43_STAT_INITIALIZED) if (!dev || b43_status(dev) != B43_STAT_INITIALIZED)
return; return;
b43_set_status(dev, B43_STAT_UNINIT); b43_set_status(dev, B43_STAT_UNINIT);
...@@ -4309,7 +4334,6 @@ static int b43_op_add_interface(struct ieee80211_hw *hw, ...@@ -4309,7 +4334,6 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
{ {
struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev; struct b43_wldev *dev;
unsigned long flags;
int err = -EOPNOTSUPP; int err = -EOPNOTSUPP;
/* TODO: allow WDS/AP devices to coexist */ /* TODO: allow WDS/AP devices to coexist */
...@@ -4333,12 +4357,10 @@ static int b43_op_add_interface(struct ieee80211_hw *hw, ...@@ -4333,12 +4357,10 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
wl->if_type = conf->type; wl->if_type = conf->type;
memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN); memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN);
spin_lock_irqsave(&wl->irq_lock, flags);
b43_adjust_opmode(dev); b43_adjust_opmode(dev);
b43_set_pretbtt(dev); b43_set_pretbtt(dev);
b43_set_synth_pu_delay(dev, 0); b43_set_synth_pu_delay(dev, 0);
b43_upload_card_macaddress(dev); b43_upload_card_macaddress(dev);
spin_unlock_irqrestore(&wl->irq_lock, flags);
err = 0; err = 0;
out_mutex_unlock: out_mutex_unlock:
...@@ -4352,7 +4374,6 @@ static void b43_op_remove_interface(struct ieee80211_hw *hw, ...@@ -4352,7 +4374,6 @@ static void b43_op_remove_interface(struct ieee80211_hw *hw,
{ {
struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev = wl->current_dev; struct b43_wldev *dev = wl->current_dev;
unsigned long flags;
b43dbg(wl, "Removing Interface type %d\n", conf->type); b43dbg(wl, "Removing Interface type %d\n", conf->type);
...@@ -4364,11 +4385,9 @@ static void b43_op_remove_interface(struct ieee80211_hw *hw, ...@@ -4364,11 +4385,9 @@ static void b43_op_remove_interface(struct ieee80211_hw *hw,
wl->operating = 0; wl->operating = 0;
spin_lock_irqsave(&wl->irq_lock, flags);
b43_adjust_opmode(dev); b43_adjust_opmode(dev);
memset(wl->mac_addr, 0, ETH_ALEN); memset(wl->mac_addr, 0, ETH_ALEN);
b43_upload_card_macaddress(dev); b43_upload_card_macaddress(dev);
spin_unlock_irqrestore(&wl->irq_lock, flags);
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
} }
...@@ -4428,10 +4447,15 @@ static void b43_op_stop(struct ieee80211_hw *hw) ...@@ -4428,10 +4447,15 @@ static void b43_op_stop(struct ieee80211_hw *hw)
cancel_work_sync(&(wl->beacon_update_trigger)); cancel_work_sync(&(wl->beacon_update_trigger));
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
if (b43_status(dev) >= B43_STAT_STARTED) if (b43_status(dev) >= B43_STAT_STARTED) {
b43_wireless_core_stop(dev); dev = b43_wireless_core_stop(dev);
if (!dev)
goto out_unlock;
}
b43_wireless_core_exit(dev); b43_wireless_core_exit(dev);
wl->radio_enabled = 0; wl->radio_enabled = 0;
out_unlock:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
cancel_work_sync(&(wl->txpower_adjust_work)); cancel_work_sync(&(wl->txpower_adjust_work));
...@@ -4441,11 +4465,10 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, ...@@ -4441,11 +4465,10 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw,
struct ieee80211_sta *sta, bool set) struct ieee80211_sta *sta, bool set)
{ {
struct b43_wl *wl = hw_to_b43_wl(hw); 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); b43_update_templates(wl);
spin_unlock_irqrestore(&wl->irq_lock, flags); mutex_unlock(&wl->mutex);
return 0; return 0;
} }
...@@ -4526,8 +4549,13 @@ static void b43_chip_reset(struct work_struct *work) ...@@ -4526,8 +4549,13 @@ static void b43_chip_reset(struct work_struct *work)
prev_status = b43_status(dev); prev_status = b43_status(dev);
/* Bring the device down... */ /* Bring the device down... */
if (prev_status >= B43_STAT_STARTED) if (prev_status >= B43_STAT_STARTED) {
b43_wireless_core_stop(dev); dev = b43_wireless_core_stop(dev);
if (!dev) {
err = -ENODEV;
goto out;
}
}
if (prev_status >= B43_STAT_INITIALIZED) if (prev_status >= B43_STAT_INITIALIZED)
b43_wireless_core_exit(dev); b43_wireless_core_exit(dev);
...@@ -4742,9 +4770,6 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl) ...@@ -4742,9 +4770,6 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl)
wldev->wl = wl; wldev->wl = wl;
b43_set_status(wldev, B43_STAT_UNINIT); b43_set_status(wldev, B43_STAT_UNINIT);
wldev->bad_frames_preempt = modparam_bad_frames_preempt; 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); INIT_LIST_HEAD(&wldev->list);
err = b43_wireless_core_attach(wldev); err = b43_wireless_core_attach(wldev);
...@@ -4841,11 +4866,11 @@ static int b43_wireless_init(struct ssb_device *dev) ...@@ -4841,11 +4866,11 @@ static int b43_wireless_init(struct ssb_device *dev)
/* Initialize struct b43_wl */ /* Initialize struct b43_wl */
wl->hw = hw; wl->hw = hw;
spin_lock_init(&wl->irq_lock);
rwlock_init(&wl->tx_lock); rwlock_init(&wl->tx_lock);
spin_lock_init(&wl->leds_lock); spin_lock_init(&wl->leds_lock);
spin_lock_init(&wl->shm_lock); spin_lock_init(&wl->shm_lock);
mutex_init(&wl->mutex); mutex_init(&wl->mutex);
spin_lock_init(&wl->hardirq_lock);
INIT_LIST_HEAD(&wl->devlist); INIT_LIST_HEAD(&wl->devlist);
INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work); 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->txpower_adjust_work, b43_phy_txpower_adjust_work);
...@@ -4946,8 +4971,8 @@ static int b43_suspend(struct ssb_device *dev, pm_message_t state) ...@@ -4946,8 +4971,8 @@ static int b43_suspend(struct ssb_device *dev, pm_message_t state)
wldev->suspend_in_progress = true; wldev->suspend_in_progress = true;
wldev->suspend_init_status = b43_status(wldev); wldev->suspend_init_status = b43_status(wldev);
if (wldev->suspend_init_status >= B43_STAT_STARTED) if (wldev->suspend_init_status >= B43_STAT_STARTED)
b43_wireless_core_stop(wldev); wldev = b43_wireless_core_stop(wldev);
if (wldev->suspend_init_status >= B43_STAT_INITIALIZED) if (wldev && wldev->suspend_init_status >= B43_STAT_INITIALIZED)
b43_wireless_core_exit(wldev); b43_wireless_core_exit(wldev);
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
......
...@@ -347,7 +347,6 @@ void b43_phy_txpower_adjust_work(struct work_struct *work) ...@@ -347,7 +347,6 @@ void b43_phy_txpower_adjust_work(struct work_struct *work)
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
} }
/* Called with wl->irq_lock locked */
void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags)
{ {
struct b43_phy *phy = &dev->phy; struct b43_phy *phy = &dev->phy;
......
...@@ -131,7 +131,7 @@ enum b43_txpwr_result { ...@@ -131,7 +131,7 @@ enum b43_txpwr_result {
* If the parameter "ignore_tssi" is true, the TSSI values should * If the parameter "ignore_tssi" is true, the TSSI values should
* be ignored and a recalculation of the power settings should be * be ignored and a recalculation of the power settings should be
* done even if the TSSI values did not change. * 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. * Must not be NULL.
* @adjust_txpower: Write the previously calculated TX power settings * @adjust_txpower: Write the previously calculated TX power settings
* (from @recalc_txpower) to the hardware. * (from @recalc_txpower) to the hardware.
...@@ -379,7 +379,6 @@ void b43_software_rfkill(struct b43_wldev *dev, bool blocked); ...@@ -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 * Compare the current TX power output to the desired power emission
* and schedule an adjustment in case it mismatches. * and schedule an adjustment in case it mismatches.
* Requires wl->irq_lock locked.
* *
* @flags: OR'ed enum b43_phy_txpower_check_flags flags. * @flags: OR'ed enum b43_phy_txpower_check_flags flags.
* See the docs below. * See the docs below.
......
...@@ -2823,8 +2823,6 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev) ...@@ -2823,8 +2823,6 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
b43_mac_suspend(dev); b43_mac_suspend(dev);
spin_lock_irq(&dev->wl->irq_lock);
/* Calculate the new attenuation values. */ /* Calculate the new attenuation values. */
bbatt = gphy->bbatt.att; bbatt = gphy->bbatt.att;
bbatt += gphy->bbatt_delta; bbatt += gphy->bbatt_delta;
...@@ -2864,11 +2862,6 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev) ...@@ -2864,11 +2862,6 @@ static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
gphy->rfatt.att = rfatt; gphy->rfatt.att = rfatt;
gphy->bbatt.att = bbatt; 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)) if (b43_debug(dev, B43_DBG_XMITPOWER))
b43dbg(dev->wl, "Adjusting TX power\n"); b43dbg(dev->wl, "Adjusting TX power\n");
......
...@@ -141,8 +141,7 @@ struct b43_phy_g { ...@@ -141,8 +141,7 @@ struct b43_phy_g {
int tgt_idle_tssi; int tgt_idle_tssi;
/* Current idle TSSI */ /* Current idle TSSI */
int cur_idle_tssi; int cur_idle_tssi;
/* The current average TSSI. /* The current average TSSI. */
* Needs irq_lock, as it's updated in the IRQ path. */
u8 average_tssi; u8 average_tssi;
/* Current TX power level attenuation control values */ /* Current TX power level attenuation control values */
struct b43_bbatt bbatt; struct b43_bbatt bbatt;
......
...@@ -570,7 +570,6 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb) ...@@ -570,7 +570,6 @@ int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
return err; return err;
} }
/* Called with IRQs disabled. */
void b43_pio_handle_txstatus(struct b43_wldev *dev, void b43_pio_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status) const struct b43_txstatus *status)
{ {
...@@ -584,7 +583,7 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev, ...@@ -584,7 +583,7 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev,
return; return;
B43_WARN_ON(!pack); B43_WARN_ON(!pack);
spin_lock(&q->lock); /* IRQs are already disabled. */ spin_lock_irq(&q->lock);
info = IEEE80211_SKB_CB(pack->skb); info = IEEE80211_SKB_CB(pack->skb);
...@@ -604,7 +603,7 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev, ...@@ -604,7 +603,7 @@ void b43_pio_handle_txstatus(struct b43_wldev *dev,
q->stopped = 0; q->stopped = 0;
} }
spin_unlock(&q->lock); spin_unlock_irq(&q->lock);
} }
void b43_pio_get_tx_stats(struct b43_wldev *dev, void b43_pio_get_tx_stats(struct b43_wldev *dev,
......
...@@ -94,7 +94,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev, ...@@ -94,7 +94,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct b43_wldev *wldev = dev_to_b43_wldev(dev); struct b43_wldev *wldev = dev_to_b43_wldev(dev);
unsigned long flags;
int err; int err;
int mode; int mode;
...@@ -120,7 +119,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev, ...@@ -120,7 +119,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev,
} }
mutex_lock(&wldev->wl->mutex); mutex_lock(&wldev->wl->mutex);
spin_lock_irqsave(&wldev->wl->irq_lock, flags);
if (wldev->phy.ops->interf_mitigation) { if (wldev->phy.ops->interf_mitigation) {
err = wldev->phy.ops->interf_mitigation(wldev, mode); err = wldev->phy.ops->interf_mitigation(wldev, mode);
...@@ -132,7 +130,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev, ...@@ -132,7 +130,6 @@ static ssize_t b43_attr_interfmode_store(struct device *dev,
err = -ENOSYS; err = -ENOSYS;
mmiowb(); mmiowb();
spin_unlock_irqrestore(&wldev->wl->irq_lock, flags);
mutex_unlock(&wldev->wl->mutex); mutex_unlock(&wldev->wl->mutex);
return err ? err : count; return err ? err : count;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment