]> err.no Git - linux-2.6/blobdiff - drivers/net/wireless/b43legacy/main.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6] / drivers / net / wireless / b43legacy / main.c
index f0749510bcd7a193088194ce6b6e935bcd47a105..5f3f34e1dbfdc140f426c4660258ba54029780fd 100644 (file)
@@ -3,7 +3,7 @@
  *  Broadcom B43legacy wireless driver
  *
  *  Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>
- *  Copyright (c) 2005 Stefano Brivio <st3@riseup.net>
+ *  Copyright (c) 2005-2008 Stefano Brivio <stefano.brivio@polimi.it>
  *  Copyright (c) 2005, 2006 Michael Buesch <mb@bu3sch.de>
  *  Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
  *  Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
@@ -60,6 +60,8 @@ MODULE_AUTHOR("Stefano Brivio");
 MODULE_AUTHOR("Michael Buesch");
 MODULE_LICENSE("GPL");
 
+MODULE_FIRMWARE(B43legacy_SUPPORTED_FIRMWARE_ID);
+
 #if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO)
 static int modparam_pio;
 module_param_named(pio, modparam_pio, int, 0444);
@@ -75,18 +77,6 @@ module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444);
 MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames"
                 " Preemption");
 
-static int modparam_short_retry = B43legacy_DEFAULT_SHORT_RETRY_LIMIT;
-module_param_named(short_retry, modparam_short_retry, int, 0444);
-MODULE_PARM_DESC(short_retry, "Short-Retry-Limit (0 - 15)");
-
-static int modparam_long_retry = B43legacy_DEFAULT_LONG_RETRY_LIMIT;
-module_param_named(long_retry, modparam_long_retry, int, 0444);
-MODULE_PARM_DESC(long_retry, "Long-Retry-Limit (0 - 15)");
-
-static int modparam_noleds;
-module_param_named(noleds, modparam_noleds, int, 0444);
-MODULE_PARM_DESC(noleds, "Turn off all LED activity");
-
 static char modparam_fwpostfix[16];
 module_param_string(fwpostfix, modparam_fwpostfix, 16, 0444);
 MODULE_PARM_DESC(fwpostfix, "Postfix for the firmware files to load.");
@@ -237,8 +227,8 @@ static void b43legacy_ram_write(struct b43legacy_wldev *dev, u16 offset,
 
        B43legacy_WARN_ON(offset % 4 != 0);
 
-       status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
-       if (status & B43legacy_SBF_XFER_REG_BYTESWAP)
+       status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       if (status & B43legacy_MACCTL_BE)
                val = swab32(val);
 
        b43legacy_write32(dev, B43legacy_MMIO_RAM_CONTROL, offset);
@@ -446,9 +436,9 @@ static void b43legacy_time_lock(struct b43legacy_wldev *dev)
 {
        u32 status;
 
-       status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
-       status |= B43legacy_SBF_TIME_UPDATE;
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
+       status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       status |= B43legacy_MACCTL_TBTTHOLD;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
        mmiowb();
 }
 
@@ -456,9 +446,9 @@ static void b43legacy_time_unlock(struct b43legacy_wldev *dev)
 {
        u32 status;
 
-       status = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
-       status &= ~B43legacy_SBF_TIME_UPDATE;
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, status);
+       status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       status &= ~B43legacy_MACCTL_TBTTHOLD;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
 }
 
 static void b43legacy_tsf_write_locked(struct b43legacy_wldev *dev, u64 tsf)
@@ -659,7 +649,7 @@ void b43legacy_dummy_transmission(struct b43legacy_wldev *dev)
                b43legacy_ram_write(dev, i * 4, buffer[i]);
 
        /* dummy read follows */
-       b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
+       b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
 
        b43legacy_write16(dev, 0x0568, 0x0000);
        b43legacy_write16(dev, 0x07C0, 0x0000);
@@ -806,9 +796,9 @@ static void b43legacy_jssi_write(struct b43legacy_wldev *dev, u32 jssi)
 static void b43legacy_generate_noise_sample(struct b43legacy_wldev *dev)
 {
        b43legacy_jssi_write(dev, 0x7F7F7F7F);
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD,
+       b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
                          b43legacy_read32(dev,
-                         B43legacy_MMIO_STATUS2_BITFIELD)
+                         B43legacy_MMIO_MACCMD)
                          | (1 << 4));
        B43legacy_WARN_ON(dev->noisecalc.channel_at_start !=
                            dev->phy.channel);
@@ -907,8 +897,8 @@ static void handle_irq_atim_end(struct b43legacy_wldev *dev)
 {
        if (!dev->reg124_set_0x4) /*FIXME rename this variable*/
                return;
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD,
-                         b43legacy_read32(dev, B43legacy_MMIO_STATUS2_BITFIELD)
+       b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
+                         b43legacy_read32(dev, B43legacy_MMIO_MACCMD)
                          | 0x4);
 }
 
@@ -988,7 +978,7 @@ static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev,
        plcp.data = 0;
        b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate);
        dur = ieee80211_generic_frame_duration(dev->wl->hw,
-                                              dev->wl->if_id,
+                                              dev->wl->vif,
                                               size,
                                               B43legacy_RATE_TO_100KBPS(rate));
        /* Write PLCP in two parts and timing for packet transfer */
@@ -1054,7 +1044,7 @@ static u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
        hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                         IEEE80211_STYPE_PROBE_RESP);
        dur = ieee80211_generic_frame_duration(dev->wl->hw,
-                                              dev->wl->if_id,
+                                              dev->wl->vif,
                                               *dest_size,
                                               B43legacy_RATE_TO_100KBPS(rate));
        hdr->duration_id = dur;
@@ -1118,9 +1108,9 @@ static void b43legacy_update_templates(struct b43legacy_wldev *dev)
        b43legacy_write_probe_resp_template(dev, 0x268, 0x4A,
                                            B43legacy_CCK_RATE_11MB);
 
-       status = b43legacy_read32(dev, B43legacy_MMIO_STATUS2_BITFIELD);
+       status = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
        status |= 0x03;
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD, status);
+       b43legacy_write32(dev, B43legacy_MMIO_MACCMD, status);
 }
 
 static void b43legacy_refresh_templates(struct b43legacy_wldev *dev,
@@ -1178,7 +1168,7 @@ static void handle_irq_beacon(struct b43legacy_wldev *dev)
                return;
 
        dev->irq_savedstate &= ~B43legacy_IRQ_BEACON;
-       status = b43legacy_read32(dev, B43legacy_MMIO_STATUS2_BITFIELD);
+       status = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
 
        if (!dev->cached_beacon || ((status & 0x1) && (status & 0x2))) {
                /* ACK beacon IRQ. */
@@ -1194,14 +1184,14 @@ static void handle_irq_beacon(struct b43legacy_wldev *dev)
                b43legacy_write_beacon_template(dev, 0x68, 0x18,
                                                B43legacy_CCK_RATE_1MB);
                status |= 0x1;
-               b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD,
+               b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
                                  status);
        }
        if (!(status & 0x2)) {
                b43legacy_write_beacon_template(dev, 0x468, 0x1A,
                                                B43legacy_CCK_RATE_1MB);
                status |= 0x2;
-               b43legacy_write32(dev, B43legacy_MMIO_STATUS2_BITFIELD,
+               b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
                                  status);
        }
 }
@@ -1217,7 +1207,6 @@ static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev)
        u32 dma_reason[ARRAY_SIZE(dev->dma_reason)];
        u32 merged_dma_reason = 0;
        int i;
-       int activity = 0;
        unsigned long flags;
 
        spin_lock_irqsave(&dev->wl->irq_lock, flags);
@@ -1234,8 +1223,15 @@ static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev)
        if (unlikely(reason & B43legacy_IRQ_MAC_TXERR))
                b43legacyerr(dev->wl, "MAC transmission error\n");
 
-       if (unlikely(reason & B43legacy_IRQ_PHY_TXERR))
+       if (unlikely(reason & B43legacy_IRQ_PHY_TXERR)) {
                b43legacyerr(dev->wl, "PHY transmission error\n");
+               rmb();
+               if (unlikely(atomic_dec_and_test(&dev->phy.txerr_cnt))) {
+                       b43legacyerr(dev->wl, "Too many PHY TX errors, "
+                                             "restarting the controller\n");
+                       b43legacy_controller_restart(dev, "PHY TX errors");
+               }
+       }
 
        if (unlikely(merged_dma_reason & (B43legacy_DMAIRQ_FATALMASK |
                                          B43legacy_DMAIRQ_NONFATALMASK))) {
@@ -1281,7 +1277,6 @@ static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev)
                        b43legacy_pio_rx(dev->pio.queue0);
                else
                        b43legacy_dma_rx(dev->dma.rx_ring0);
-               /* We intentionally don't set "activity" to 1, here. */
        }
        B43legacy_WARN_ON(dma_reason[1] & B43legacy_DMAIRQ_RX_DONE);
        B43legacy_WARN_ON(dma_reason[2] & B43legacy_DMAIRQ_RX_DONE);
@@ -1290,20 +1285,13 @@ static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev)
                        b43legacy_pio_rx(dev->pio.queue3);
                else
                        b43legacy_dma_rx(dev->dma.rx_ring3);
-               activity = 1;
        }
        B43legacy_WARN_ON(dma_reason[4] & B43legacy_DMAIRQ_RX_DONE);
        B43legacy_WARN_ON(dma_reason[5] & B43legacy_DMAIRQ_RX_DONE);
 
-       if (reason & B43legacy_IRQ_TX_OK) {
+       if (reason & B43legacy_IRQ_TX_OK)
                handle_irq_transmit_status(dev);
-               activity = 1;
-               /* TODO: In AP mode, this also causes sending of powersave
-                        responses. */
-       }
 
-       if (!modparam_noleds)
-               b43legacy_leds_update(dev, activity);
        b43legacy_interrupt_enable(dev, dev->irq_savedstate);
        mmiowb();
        spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
@@ -1419,7 +1407,7 @@ static void b43legacy_release_firmware(struct b43legacy_wldev *dev)
 static void b43legacy_print_fw_helptext(struct b43legacy_wl *wl)
 {
        b43legacyerr(wl, "You must go to http://linuxwireless.org/en/users/"
-                    "Drivers/bcm43xx#devicefirmware "
+                    "Drivers/b43#devicefirmware "
                     "and download the correct firmware (version 3).\n");
 }
 
@@ -1562,9 +1550,20 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
        u16 fwpatch;
        u16 fwdate;
        u16 fwtime;
-       u32 tmp;
+       u32 tmp, macctl;
        int err = 0;
 
+       /* Jump the microcode PSM to offset 0 */
+       macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       B43legacy_WARN_ON(macctl & B43legacy_MACCTL_PSM_RUN);
+       macctl |= B43legacy_MACCTL_PSM_JMP0;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
+       /* Zero out all microcode PSM registers and shared memory. */
+       for (i = 0; i < 64; i++)
+               b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, i, 0);
+       for (i = 0; i < 4096; i += 2)
+               b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, i, 0);
+
        /* Upload Microcode. */
        data = (__be32 *) (dev->fw.ucode->data + hdr_len);
        len = (dev->fw.ucode->size - hdr_len) / sizeof(__be32);
@@ -1595,7 +1594,12 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
 
        b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON,
                          B43legacy_IRQ_ALL);
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, 0x00020402);
+
+       /* Start the microcode PSM */
+       macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       macctl &= ~B43legacy_MACCTL_PSM_JMP0;
+       macctl |= B43legacy_MACCTL_PSM_RUN;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
 
        /* Wait for the microcode to load and respond */
        i = 0;
@@ -1608,9 +1612,13 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
                        b43legacyerr(dev->wl, "Microcode not responding\n");
                        b43legacy_print_fw_helptext(dev->wl);
                        err = -ENODEV;
-                       goto out;
+                       goto error;
+               }
+               msleep_interruptible(50);
+               if (signal_pending(current)) {
+                       err = -EINTR;
+                       goto error;
                }
-               udelay(10);
        }
        /* dummy read follows */
        b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
@@ -1631,19 +1639,26 @@ static int b43legacy_upload_microcode(struct b43legacy_wldev *dev)
                             " is supported. You must change your firmware"
                             " files.\n");
                b43legacy_print_fw_helptext(dev->wl);
-               b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, 0);
                err = -EOPNOTSUPP;
-               goto out;
+               goto error;
        }
-       b43legacydbg(dev->wl, "Loading firmware version 0x%X, patch level %u "
-              "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", fwrev, fwpatch,
-              (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF,
-              (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F);
+       b43legacyinfo(dev->wl, "Loading firmware version 0x%X, patch level %u "
+                     "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", fwrev, fwpatch,
+                     (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF,
+                     (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F,
+                     fwtime & 0x1F);
 
        dev->fw.rev = fwrev;
        dev->fw.patch = fwpatch;
 
-out:
+       return 0;
+
+error:
+       macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       macctl &= ~B43legacy_MACCTL_PSM_RUN;
+       macctl |= B43legacy_MACCTL_PSM_JMP0;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
+
        return err;
 }
 
@@ -1750,12 +1765,11 @@ static int b43legacy_gpio_init(struct b43legacy_wldev *dev)
        u32 mask;
        u32 set;
 
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL,
                          b43legacy_read32(dev,
-                         B43legacy_MMIO_STATUS_BITFIELD)
+                         B43legacy_MMIO_MACCTL)
                          & 0xFFFF3FFF);
 
-       b43legacy_leds_switch_all(dev, 0);
        b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK,
                          b43legacy_read16(dev,
                          B43legacy_MMIO_GPIO_MASK)
@@ -1767,7 +1781,7 @@ static int b43legacy_gpio_init(struct b43legacy_wldev *dev)
                mask |= 0x0060;
                set |= 0x0060;
        }
-       if (dev->dev->bus->sprom.r1.boardflags_lo & B43legacy_BFL_PACTRL) {
+       if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL) {
                b43legacy_write16(dev, B43legacy_MMIO_GPIO_MASK,
                                  b43legacy_read16(dev,
                                  B43legacy_MMIO_GPIO_MASK)
@@ -1811,17 +1825,23 @@ void b43legacy_mac_enable(struct b43legacy_wldev *dev)
 {
        dev->mac_suspended--;
        B43legacy_WARN_ON(dev->mac_suspended < 0);
+       B43legacy_WARN_ON(irqs_disabled());
        if (dev->mac_suspended == 0) {
-               b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
+               b43legacy_write32(dev, B43legacy_MMIO_MACCTL,
                                  b43legacy_read32(dev,
-                                 B43legacy_MMIO_STATUS_BITFIELD)
-                                 | B43legacy_SBF_MAC_ENABLED);
+                                 B43legacy_MMIO_MACCTL)
+                                 | B43legacy_MACCTL_ENABLED);
                b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON,
                                  B43legacy_IRQ_MAC_SUSPENDED);
                /* the next two are dummy reads */
-               b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
+               b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
                b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
                b43legacy_power_saving_ctl_bits(dev, -1, -1);
+
+               /* Re-enable IRQs. */
+               spin_lock_irq(&dev->wl->irq_lock);
+               b43legacy_interrupt_enable(dev, dev->irq_savedstate);
+               spin_unlock_irq(&dev->wl->irq_lock);
        }
 }
 
@@ -1831,20 +1851,31 @@ void b43legacy_mac_suspend(struct b43legacy_wldev *dev)
        int i;
        u32 tmp;
 
+       might_sleep();
+       B43legacy_WARN_ON(irqs_disabled());
        B43legacy_WARN_ON(dev->mac_suspended < 0);
+
        if (dev->mac_suspended == 0) {
+               /* Mask IRQs before suspending MAC. Otherwise
+                * the MAC stays busy and won't suspend. */
+               spin_lock_irq(&dev->wl->irq_lock);
+               tmp = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
+               spin_unlock_irq(&dev->wl->irq_lock);
+               b43legacy_synchronize_irq(dev);
+               dev->irq_savedstate = tmp;
+
                b43legacy_power_saving_ctl_bits(dev, -1, 1);
-               b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
+               b43legacy_write32(dev, B43legacy_MMIO_MACCTL,
                                  b43legacy_read32(dev,
-                                 B43legacy_MMIO_STATUS_BITFIELD)
-                                 & ~B43legacy_SBF_MAC_ENABLED);
+                                 B43legacy_MMIO_MACCTL)
+                                 & ~B43legacy_MACCTL_ENABLED);
                b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
-               for (i = 10000; i; i--) {
+               for (i = 40; i; i--) {
                        tmp = b43legacy_read32(dev,
                                               B43legacy_MMIO_GEN_IRQ_REASON);
                        if (tmp & B43legacy_IRQ_MAC_SUSPENDED)
                                goto out;
-                       udelay(1);
+                       msleep(1);
                }
                b43legacyerr(dev->wl, "MAC suspend failed\n");
        }
@@ -1989,27 +2020,10 @@ static void b43legacy_mgmtframe_txantenna(struct b43legacy_wldev *dev,
                              B43legacy_SHM_SH_PRPHYCTL, tmp);
 }
 
-/* Returns TRUE, if the radio is enabled in hardware. */
-static bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev)
-{
-       if (dev->phy.rev >= 3) {
-               if (!(b43legacy_read32(dev, B43legacy_MMIO_RADIO_HWENABLED_HI)
-                     & B43legacy_MMIO_RADIO_HWENABLED_HI_MASK))
-                       return 1;
-       } else {
-               if (b43legacy_read16(dev, B43legacy_MMIO_RADIO_HWENABLED_LO)
-                   & B43legacy_MMIO_RADIO_HWENABLED_LO_MASK)
-                       return 1;
-       }
-       return 0;
-}
-
 /* This is the opposite of b43legacy_chip_init() */
 static void b43legacy_chip_exit(struct b43legacy_wldev *dev)
 {
-       b43legacy_radio_turn_off(dev);
-       if (!modparam_noleds)
-               b43legacy_leds_exit(dev);
+       b43legacy_radio_turn_off(dev, 1);
        b43legacy_gpio_cleanup(dev);
        /* firmware is released later */
 }
@@ -2022,12 +2036,15 @@ static int b43legacy_chip_init(struct b43legacy_wldev *dev)
        struct b43legacy_phy *phy = &dev->phy;
        int err;
        int tmp;
-       u32 value32;
+       u32 value32, macctl;
        u16 value16;
 
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD,
-                         B43legacy_SBF_CORE_READY
-                         | B43legacy_SBF_400);
+       /* Initialize the MAC control */
+       macctl = B43legacy_MACCTL_IHR_ENABLED | B43legacy_MACCTL_SHM_ENABLED;
+       if (dev->phy.gmode)
+               macctl |= B43legacy_MACCTL_GMODE;
+       macctl |= B43legacy_MACCTL_INFRA;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
 
        err = b43legacy_request_firmware(dev);
        if (err)
@@ -2039,9 +2056,10 @@ static int b43legacy_chip_init(struct b43legacy_wldev *dev)
        err = b43legacy_gpio_init(dev);
        if (err)
                goto out; /* firmware is released later */
+
        err = b43legacy_upload_initvals(dev);
        if (err)
-               goto err_gpio_cleanup;
+               goto err_gpio_clean;
        b43legacy_radio_turn_on(dev);
 
        b43legacy_write16(dev, 0x03E6, 0x0000);
@@ -2066,12 +2084,12 @@ static int b43legacy_chip_init(struct b43legacy_wldev *dev)
        if (dev->dev->id.revision < 5)
                b43legacy_write32(dev, 0x010C, 0x01000000);
 
-       value32 = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
-       value32 &= ~B43legacy_SBF_MODE_NOTADHOC;
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, value32);
-       value32 = b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD);
-       value32 |= B43legacy_SBF_MODE_NOTADHOC;
-       b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, value32);
+       value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       value32 &= ~B43legacy_MACCTL_INFRA;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32);
+       value32 = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       value32 |= B43legacy_MACCTL_INFRA;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, value32);
 
        if (b43legacy_using_pio(dev)) {
                b43legacy_write32(dev, 0x0210, 0x00000100);
@@ -2113,14 +2131,17 @@ static int b43legacy_chip_init(struct b43legacy_wldev *dev)
        b43legacy_write16(dev, B43legacy_MMIO_POWERUP_DELAY,
                          dev->dev->bus->chipco.fast_pwrup_delay);
 
+       /* PHY TX errors counter. */
+       atomic_set(&phy->txerr_cnt, B43legacy_PHY_TX_BADNESS_LIMIT);
+
        B43legacy_WARN_ON(err != 0);
        b43legacydbg(dev->wl, "Chip initialized\n");
 out:
        return err;
 
 err_radio_off:
-       b43legacy_radio_turn_off(dev);
-err_gpio_cleanup:
+       b43legacy_radio_turn_off(dev, 1);
+err_gpio_clean:
        b43legacy_gpio_cleanup(dev);
        goto out;
 }
@@ -2140,7 +2161,7 @@ static void b43legacy_periodic_every120sec(struct b43legacy_wldev *dev)
 static void b43legacy_periodic_every60sec(struct b43legacy_wldev *dev)
 {
        b43legacy_phy_lo_mark_all_unused(dev);
-       if (dev->dev->bus->sprom.r1.boardflags_lo & B43legacy_BFL_RSSI) {
+       if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) {
                b43legacy_mac_suspend(dev);
                b43legacy_calc_nrssi_slope(dev);
                b43legacy_mac_enable(dev);
@@ -2156,20 +2177,9 @@ static void b43legacy_periodic_every30sec(struct b43legacy_wldev *dev)
 static void b43legacy_periodic_every15sec(struct b43legacy_wldev *dev)
 {
        b43legacy_phy_xmitpower(dev); /* FIXME: unless scanning? */
-}
 
-static void b43legacy_periodic_every1sec(struct b43legacy_wldev *dev)
-{
-       bool radio_hw_enable;
-
-       /* check if radio hardware enabled status changed */
-       radio_hw_enable = b43legacy_is_hw_radio_enabled(dev);
-       if (unlikely(dev->radio_hw_enable != radio_hw_enable)) {
-               dev->radio_hw_enable = radio_hw_enable;
-               b43legacyinfo(dev->wl, "Radio hardware status changed to %s\n",
-                      (radio_hw_enable) ? "enabled" : "disabled");
-               b43legacy_leds_update(dev, 0);
-       }
+       atomic_set(&dev->phy.txerr_cnt, B43legacy_PHY_TX_BADNESS_LIMIT);
+       wmb();
 }
 
 static void do_periodic_work(struct b43legacy_wldev *dev)
@@ -2177,94 +2187,45 @@ static void do_periodic_work(struct b43legacy_wldev *dev)
        unsigned int state;
 
        state = dev->periodic_state;
-       if (state % 120 == 0)
+       if (state % 8 == 0)
                b43legacy_periodic_every120sec(dev);
-       if (state % 60 == 0)
+       if (state % 4 == 0)
                b43legacy_periodic_every60sec(dev);
-       if (state % 30 == 0)
+       if (state % 2 == 0)
                b43legacy_periodic_every30sec(dev);
-       if (state % 15 == 0)
-               b43legacy_periodic_every15sec(dev);
-       b43legacy_periodic_every1sec(dev);
+       b43legacy_periodic_every15sec(dev);
 }
 
-/* Estimate a "Badness" value based on the periodic work
- * state-machine state. "Badness" is worse (bigger), if the
- * periodic work will take longer.
+/* Periodic work locking policy:
+ *     The whole periodic work handler is protected by
+ *     wl->mutex. If another lock is needed somewhere in the
+ *     pwork callchain, it's aquired in-place, where it's needed.
  */
-static int estimate_periodic_work_badness(unsigned int state)
-{
-       int badness = 0;
-
-       if (state % 120 == 0) /* every 120 sec */
-               badness += 10;
-       if (state % 60 == 0) /* every 60 sec */
-               badness += 5;
-       if (state % 30 == 0) /* every 30 sec */
-               badness += 1;
-       if (state % 15 == 0) /* every 15 sec */
-               badness += 1;
-
-#define BADNESS_LIMIT  4
-       return badness;
-}
-
 static void b43legacy_periodic_work_handler(struct work_struct *work)
 {
-       struct b43legacy_wldev *dev =
-                            container_of(work, struct b43legacy_wldev,
-                            periodic_work.work);
-       unsigned long flags;
+       struct b43legacy_wldev *dev = container_of(work, struct b43legacy_wldev,
+                                            periodic_work.work);
+       struct b43legacy_wl *wl = dev->wl;
        unsigned long delay;
-       u32 savedirqs = 0;
-       int badness;
 
-       mutex_lock(&dev->wl->mutex);
+       mutex_lock(&wl->mutex);
 
        if (unlikely(b43legacy_status(dev) != B43legacy_STAT_STARTED))
                goto out;
        if (b43legacy_debug(dev, B43legacy_DBG_PWORK_STOP))
                goto out_requeue;
 
-       badness = estimate_periodic_work_badness(dev->periodic_state);
-       if (badness > BADNESS_LIMIT) {
-               spin_lock_irqsave(&dev->wl->irq_lock, flags);
-               /* Suspend TX as we don't want to transmit packets while
-                * we recalibrate the hardware. */
-               b43legacy_tx_suspend(dev);
-               savedirqs = b43legacy_interrupt_disable(dev,
-                                                         B43legacy_IRQ_ALL);
-               /* Periodic work will take a long time, so we want it to
-                * be preemtible and release the spinlock. */
-               spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
-               b43legacy_synchronize_irq(dev);
-
-               do_periodic_work(dev);
-
-               spin_lock_irqsave(&dev->wl->irq_lock, flags);
-               b43legacy_interrupt_enable(dev, savedirqs);
-               b43legacy_tx_resume(dev);
-               mmiowb();
-               spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
-       } else {
-               /* Take the global driver lock. This will lock any operation. */
-               spin_lock_irqsave(&dev->wl->irq_lock, flags);
+       do_periodic_work(dev);
 
-               do_periodic_work(dev);
-
-               mmiowb();
-               spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
-       }
        dev->periodic_state++;
 out_requeue:
        if (b43legacy_debug(dev, B43legacy_DBG_PWORK_FAST))
                delay = msecs_to_jiffies(50);
        else
-               delay = round_jiffies(HZ);
-       queue_delayed_work(dev->wl->hw->workqueue,
-                          &dev->periodic_work, delay);
+               delay = round_jiffies_relative(HZ * 15);
+       queue_delayed_work(wl->hw->workqueue, &dev->periodic_work, delay);
 out:
-       mutex_unlock(&dev->wl->mutex);
+       mutex_unlock(&wl->mutex);
 }
 
 static void b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev)
@@ -2366,9 +2327,9 @@ static int b43legacy_rng_init(struct b43legacy_wl *wl)
        return err;
 }
 
-static int b43legacy_tx(struct ieee80211_hw *hw,
-                       struct sk_buff *skb,
-                       struct ieee80211_tx_control *ctl)
+static int b43legacy_op_tx(struct ieee80211_hw *hw,
+                          struct sk_buff *skb,
+                          struct ieee80211_tx_control *ctl)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
@@ -2392,15 +2353,15 @@ out:
        return NETDEV_TX_OK;
 }
 
-static int b43legacy_conf_tx(struct ieee80211_hw *hw,
-                            int queue,
-                            const struct ieee80211_tx_queue_params *params)
+static int b43legacy_op_conf_tx(struct ieee80211_hw *hw,
+                               int queue,
+                               const struct ieee80211_tx_queue_params *params)
 {
        return 0;
 }
 
-static int b43legacy_get_tx_stats(struct ieee80211_hw *hw,
-                                 struct ieee80211_tx_queue_stats *stats)
+static int b43legacy_op_get_tx_stats(struct ieee80211_hw *hw,
+                                    struct ieee80211_tx_queue_stats *stats)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
@@ -2422,8 +2383,8 @@ out:
        return err;
 }
 
-static int b43legacy_get_stats(struct ieee80211_hw *hw,
-                              struct ieee80211_low_level_stats *stats)
+static int b43legacy_op_get_stats(struct ieee80211_hw *hw,
+                                 struct ieee80211_low_level_stats *stats)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        unsigned long flags;
@@ -2572,8 +2533,8 @@ static int b43legacy_antenna_from_ieee80211(u8 antenna)
        }
 }
 
-static int b43legacy_dev_config(struct ieee80211_hw *hw,
-                               struct ieee80211_conf *conf)
+static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
+                                  struct ieee80211_conf *conf)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev;
@@ -2634,6 +2595,8 @@ static int b43legacy_dev_config(struct ieee80211_hw *hw,
                        b43legacy_short_slot_timing_disable(dev);
        }
 
+       dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_RADIOTAP);
+
        /* Adjust the desired TX power level. */
        if (conf->power_level != 0) {
                if (conf->power_level != phy->power_level) {
@@ -2660,7 +2623,7 @@ static int b43legacy_dev_config(struct ieee80211_hw *hw,
                                              " physically off. Press the"
                                              " button to turn it on.\n");
                } else {
-                       b43legacy_radio_turn_off(dev);
+                       b43legacy_radio_turn_off(dev, 0);
                        b43legacyinfo(dev->wl, "Radio turned off by"
                                      " software\n");
                }
@@ -2676,37 +2639,11 @@ out_unlock_mutex:
        return err;
 }
 
-static int b43legacy_dev_set_key(struct ieee80211_hw *hw,
-                                enum set_key_cmd cmd,
-                                const u8 *local_addr, const u8 *addr,
-                                struct ieee80211_key_conf *key)
-{
-       struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
-       struct b43legacy_wldev *dev = wl->current_dev;
-       unsigned long flags;
-       int err = -EOPNOTSUPP;
-       DECLARE_MAC_BUF(mac);
-
-       if (!dev)
-               return -ENODEV;
-       mutex_lock(&wl->mutex);
-       spin_lock_irqsave(&wl->irq_lock, flags);
-
-       if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
-               err = -ENODEV;
-       }
-       spin_unlock_irqrestore(&wl->irq_lock, flags);
-       mutex_unlock(&wl->mutex);
-       b43legacydbg(wl, "Using software based encryption for "
-                    "mac: %s\n", print_mac(mac, addr));
-       return err;
-}
-
-static void b43legacy_configure_filter(struct ieee80211_hw *hw,
-                                      unsigned int changed,
-                                      unsigned int *fflags,
-                                      int mc_count,
-                                      struct dev_addr_list *mc_list)
+static void b43legacy_op_configure_filter(struct ieee80211_hw *hw,
+                                         unsigned int changed,
+                                         unsigned int *fflags,
+                                         int mc_count,
+                                         struct dev_addr_list *mc_list)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
@@ -2741,9 +2678,9 @@ static void b43legacy_configure_filter(struct ieee80211_hw *hw,
        spin_unlock_irqrestore(&wl->irq_lock, flags);
 }
 
-static int b43legacy_config_interface(struct ieee80211_hw *hw,
-                                     int if_id,
-                                     struct ieee80211_if_conf *conf)
+static int b43legacy_op_config_interface(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        struct ieee80211_if_conf *conf)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
@@ -2753,7 +2690,7 @@ static int b43legacy_config_interface(struct ieee80211_hw *hw,
                return -ENODEV;
        mutex_lock(&wl->mutex);
        spin_lock_irqsave(&wl->irq_lock, flags);
-       B43legacy_WARN_ON(wl->if_id != if_id);
+       B43legacy_WARN_ON(wl->vif != vif);
        if (conf->bssid)
                memcpy(wl->bssid, conf->bssid, ETH_ALEN);
        else
@@ -2781,6 +2718,17 @@ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev)
 
        if (b43legacy_status(dev) < B43legacy_STAT_STARTED)
                return;
+
+       /* 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);
+       dev->irq_savedstate = b43legacy_interrupt_disable(dev,
+                                                         B43legacy_IRQ_ALL);
+       b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK); /* flush */
+       spin_unlock_irqrestore(&wl->irq_lock, flags);
+       b43legacy_synchronize_irq(dev);
+
        b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED);
 
        mutex_unlock(&wl->mutex);
@@ -2791,14 +2739,6 @@ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev)
 
        ieee80211_stop_queues(wl->hw); /* FIXME this could cause a deadlock */
 
-       /* Disable and sync interrupts. */
-       spin_lock_irqsave(&wl->irq_lock, flags);
-       dev->irq_savedstate = b43legacy_interrupt_disable(dev,
-                                                         B43legacy_IRQ_ALL);
-       b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK); /* flush */
-       spin_unlock_irqrestore(&wl->irq_lock, flags);
-       b43legacy_synchronize_irq(dev);
-
        b43legacy_mac_suspend(dev);
        free_irq(dev->dev->irq, dev);
        b43legacydbg(wl, "Wireless interface stopped\n");
@@ -2939,8 +2879,6 @@ static void setup_struct_phy_for_init(struct b43legacy_wldev *dev,
        memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig));
        memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos));
 
-       /* Flags */
-       phy->locked = 0;
        /* Assume the radio is enabled. If it's not enabled, the state will
         * immediately get fixed on the first periodic work run. */
        dev->radio_hw_enable = 1;
@@ -2973,7 +2911,6 @@ static void setup_struct_phy_for_init(struct b43legacy_wldev *dev,
        phy->lofcal = 0xFFFF;
        phy->initval = 0xFFFF;
 
-       spin_lock_init(&phy->lock);
        phy->interfmode = B43legacy_INTERFMODE_NONE;
        phy->channel = 0xFF;
 }
@@ -3026,29 +2963,51 @@ static void b43legacy_imcfglo_timeouts_workaround(struct b43legacy_wldev *dev)
 #endif /* CONFIG_SSB_DRIVER_PCICORE */
 }
 
+/* Write the short and long frame retry limit values. */
+static void b43legacy_set_retry_limits(struct b43legacy_wldev *dev,
+                                      unsigned int short_retry,
+                                      unsigned int long_retry)
+{
+       /* The retry limit is a 4-bit counter. Enforce this to avoid overflowing
+        * the chip-internal counter. */
+       short_retry = min(short_retry, (unsigned int)0xF);
+       long_retry = min(long_retry, (unsigned int)0xF);
+
+       b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0006, short_retry);
+       b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0007, long_retry);
+}
+
 /* Shutdown a wireless core */
 /* Locking: wl->mutex */
 static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev)
 {
        struct b43legacy_wl *wl = dev->wl;
        struct b43legacy_phy *phy = &dev->phy;
+       u32 macctl;
 
        B43legacy_WARN_ON(b43legacy_status(dev) > B43legacy_STAT_INITIALIZED);
        if (b43legacy_status(dev) != B43legacy_STAT_INITIALIZED)
                return;
        b43legacy_set_status(dev, B43legacy_STAT_UNINIT);
 
+       /* Stop the microcode PSM. */
+       macctl = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
+       macctl &= ~B43legacy_MACCTL_PSM_RUN;
+       macctl |= B43legacy_MACCTL_PSM_JMP0;
+       b43legacy_write32(dev, B43legacy_MMIO_MACCTL, macctl);
+
        mutex_unlock(&wl->mutex);
        /* Must unlock as it would otherwise deadlock. No races here.
         * Cancel possibly pending workqueues. */
        cancel_work_sync(&dev->restart_work);
        mutex_lock(&wl->mutex);
 
+       b43legacy_leds_exit(dev);
        b43legacy_rng_exit(dev->wl);
        b43legacy_pio_free(dev);
        b43legacy_dma_free(dev);
        b43legacy_chip_exit(dev);
-       b43legacy_radio_turn_off(dev);
+       b43legacy_radio_turn_off(dev, 1);
        b43legacy_switch_analog(dev, 0);
        if (phy->dyn_tssi_tbl)
                kfree(phy->tssi2dbm);
@@ -3090,7 +3049,6 @@ static void prepare_phy_data_for_init(struct b43legacy_wldev *dev)
 
        /* Flags */
        phy->calibrated = 0;
-       phy->locked = 0;
 
        if (phy->_lo_pairs)
                memset(phy->_lo_pairs, 0,
@@ -3150,7 +3108,7 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev)
                hf |= B43legacy_HF_SYMW;
                if (phy->rev == 1)
                        hf |= B43legacy_HF_GDCW;
-               if (sprom->r1.boardflags_lo & B43legacy_BFL_PACTRL)
+               if (sprom->boardflags_lo & B43legacy_BFL_PACTRL)
                        hf |= B43legacy_HF_OFDMPABOOST;
        } else if (phy->type == B43legacy_PHYTYPE_B) {
                hf |= B43legacy_HF_SYMW;
@@ -3159,16 +3117,9 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev)
        }
        b43legacy_hf_write(dev, hf);
 
-       /* Short/Long Retry Limit.
-        * The retry-limit is a 4-bit counter. Enforce this to avoid overflowing
-        * the chip-internal counter.
-        */
-       tmp = limit_value(modparam_short_retry, 0, 0xF);
-       b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS,
-                             0x0006, tmp);
-       tmp = limit_value(modparam_long_retry, 0, 0xF);
-       b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS,
-                             0x0007, tmp);
+       b43legacy_set_retry_limits(dev,
+                                  B43legacy_DEFAULT_SHORT_RETRY_LIMIT,
+                                  B43legacy_DEFAULT_LONG_RETRY_LIMIT);
 
        b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
                              0x0044, 3);
@@ -3212,14 +3163,13 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev)
        b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0414, 0x01F4);
 
        ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */
-       memset(wl->bssid, 0, ETH_ALEN);
-       memset(wl->mac_addr, 0, ETH_ALEN);
        b43legacy_upload_card_macaddress(dev);
        b43legacy_security_init(dev);
        b43legacy_rng_init(wl);
 
        b43legacy_set_status(dev, B43legacy_STAT_INITIALIZED);
 
+       b43legacy_leds_init(dev);
 out:
        return err;
 
@@ -3236,8 +3186,8 @@ err_kfree_lo_control:
        return err;
 }
 
-static int b43legacy_add_interface(struct ieee80211_hw *hw,
-                                  struct ieee80211_if_init_conf *conf)
+static int b43legacy_op_add_interface(struct ieee80211_hw *hw,
+                                     struct ieee80211_if_init_conf *conf)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev;
@@ -3260,7 +3210,7 @@ static int b43legacy_add_interface(struct ieee80211_hw *hw,
 
        dev = wl->current_dev;
        wl->operating = 1;
-       wl->if_id = conf->if_id;
+       wl->vif = conf->vif;
        wl->if_type = conf->type;
        memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN);
 
@@ -3276,8 +3226,8 @@ static int b43legacy_add_interface(struct ieee80211_hw *hw,
        return err;
 }
 
-static void b43legacy_remove_interface(struct ieee80211_hw *hw,
-                                      struct ieee80211_if_init_conf *conf)
+static void b43legacy_op_remove_interface(struct ieee80211_hw *hw,
+                                         struct ieee80211_if_init_conf *conf)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
@@ -3288,7 +3238,8 @@ static void b43legacy_remove_interface(struct ieee80211_hw *hw,
        mutex_lock(&wl->mutex);
 
        B43legacy_WARN_ON(!wl->operating);
-       B43legacy_WARN_ON(wl->if_id != conf->if_id);
+       B43legacy_WARN_ON(wl->vif != conf->vif);
+       wl->vif = NULL;
 
        wl->operating = 0;
 
@@ -3301,19 +3252,33 @@ static void b43legacy_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&wl->mutex);
 }
 
-static int b43legacy_start(struct ieee80211_hw *hw)
+static int b43legacy_op_start(struct ieee80211_hw *hw)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
        int did_init = 0;
-       int err;
+       int err = 0;
+       bool do_rfkill_exit = 0;
+
+       /* First register RFkill.
+        * LEDs that are registered later depend on it. */
+       b43legacy_rfkill_init(dev);
+
+       /* Kill all old instance specific information to make sure
+        * the card won't use it in the short timeframe between start
+        * and mac80211 reconfiguring it. */
+       memset(wl->bssid, 0, ETH_ALEN);
+       memset(wl->mac_addr, 0, ETH_ALEN);
+       wl->filter_flags = 0;
 
        mutex_lock(&wl->mutex);
 
        if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
                err = b43legacy_wireless_core_init(dev);
-               if (err)
+               if (err) {
+                       do_rfkill_exit = 1;
                        goto out_mutex_unlock;
+               }
                did_init = 1;
        }
 
@@ -3322,6 +3287,7 @@ static int b43legacy_start(struct ieee80211_hw *hw)
                if (err) {
                        if (did_init)
                                b43legacy_wireless_core_exit(dev);
+                       do_rfkill_exit = 1;
                        goto out_mutex_unlock;
                }
        }
@@ -3329,14 +3295,19 @@ static int b43legacy_start(struct ieee80211_hw *hw)
 out_mutex_unlock:
        mutex_unlock(&wl->mutex);
 
+       if (do_rfkill_exit)
+               b43legacy_rfkill_exit(dev);
+
        return err;
 }
 
-void b43legacy_stop(struct ieee80211_hw *hw)
+static void b43legacy_op_stop(struct ieee80211_hw *hw)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
 
+       b43legacy_rfkill_exit(dev);
+
        mutex_lock(&wl->mutex);
        if (b43legacy_status(dev) >= B43legacy_STAT_STARTED)
                b43legacy_wireless_core_stop(dev);
@@ -3344,20 +3315,41 @@ void b43legacy_stop(struct ieee80211_hw *hw)
        mutex_unlock(&wl->mutex);
 }
 
+static int b43legacy_op_set_retry_limit(struct ieee80211_hw *hw,
+                                       u32 short_retry_limit,
+                                       u32 long_retry_limit)
+{
+       struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
+       struct b43legacy_wldev *dev;
+       int err = 0;
+
+       mutex_lock(&wl->mutex);
+       dev = wl->current_dev;
+       if (unlikely(!dev ||
+                    (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED))) {
+               err = -ENODEV;
+               goto out_unlock;
+       }
+       b43legacy_set_retry_limits(dev, short_retry_limit, long_retry_limit);
+out_unlock:
+       mutex_unlock(&wl->mutex);
+
+       return err;
+}
 
 static const struct ieee80211_ops b43legacy_hw_ops = {
-       .tx = b43legacy_tx,
-       .conf_tx = b43legacy_conf_tx,
-       .add_interface = b43legacy_add_interface,
-       .remove_interface = b43legacy_remove_interface,
-       .config = b43legacy_dev_config,
-       .config_interface = b43legacy_config_interface,
-       .set_key = b43legacy_dev_set_key,
-       .configure_filter = b43legacy_configure_filter,
-       .get_stats = b43legacy_get_stats,
-       .get_tx_stats = b43legacy_get_tx_stats,
-       .start = b43legacy_start,
-       .stop = b43legacy_stop,
+       .tx                     = b43legacy_op_tx,
+       .conf_tx                = b43legacy_op_conf_tx,
+       .add_interface          = b43legacy_op_add_interface,
+       .remove_interface       = b43legacy_op_remove_interface,
+       .config                 = b43legacy_op_dev_config,
+       .config_interface       = b43legacy_op_config_interface,
+       .configure_filter       = b43legacy_op_configure_filter,
+       .get_stats              = b43legacy_op_get_stats,
+       .get_tx_stats           = b43legacy_op_get_tx_stats,
+       .start                  = b43legacy_op_start,
+       .stop                   = b43legacy_op_stop,
+       .set_retry_limit        = b43legacy_op_set_retry_limit,
 };
 
 /* Hard-reset the chip. Do not call this directly.
@@ -3495,18 +3487,13 @@ static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev)
        else
                have_bphy = 1;
 
-       /* Initialize LEDs structs. */
-       err = b43legacy_leds_init(dev);
-       if (err)
-               goto err_powerdown;
-
        dev->phy.gmode = (have_gphy || have_bphy);
        tmp = dev->phy.gmode ? B43legacy_TMSLOW_GMODE : 0;
        b43legacy_wireless_core_reset(dev, tmp);
 
        err = b43legacy_phy_versioning(dev);
        if (err)
-               goto err_leds_exit;
+               goto err_powerdown;
        /* Check if this device supports multiband. */
        if (!pdev ||
            (pdev->device != 0x4312 &&
@@ -3532,17 +3519,17 @@ static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev)
 
        err = b43legacy_validate_chipaccess(dev);
        if (err)
-               goto err_leds_exit;
+               goto err_powerdown;
        err = b43legacy_setup_modes(dev, have_bphy, have_gphy);
        if (err)
-               goto err_leds_exit;
+               goto err_powerdown;
 
        /* Now set some default "current_dev" */
        if (!wl->current_dev)
                wl->current_dev = dev;
        INIT_WORK(&dev->restart_work, b43legacy_chip_reset);
 
-       b43legacy_radio_turn_off(dev);
+       b43legacy_radio_turn_off(dev, 1);
        b43legacy_switch_analog(dev, 0);
        ssb_device_disable(dev->dev, 0);
        ssb_bus_may_powerdown(bus);
@@ -3550,8 +3537,6 @@ static int b43legacy_wireless_core_attach(struct b43legacy_wldev *dev)
 out:
        return err;
 
-err_leds_exit:
-       b43legacy_leds_exit(dev);
 err_powerdown:
        ssb_bus_may_powerdown(bus);
        return err;
@@ -3634,12 +3619,7 @@ static void b43legacy_sprom_fixup(struct ssb_bus *bus)
        if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE &&
            bus->boardinfo.type == 0x4E &&
            bus->boardinfo.rev > 0x40)
-               bus->sprom.r1.boardflags_lo |= B43legacy_BFL_PACTRL;
-
-       /* Convert Antennagain values to Q5.2 */
-       if (bus->sprom.r1.antenna_gain_bg == 0xFF)
-               bus->sprom.r1.antenna_gain_bg = 2; /* if unset, use 2 dBm */
-       bus->sprom.r1.antenna_gain_bg <<= 2;
+               bus->sprom.boardflags_lo |= B43legacy_BFL_PACTRL;
 }
 
 static void b43legacy_wireless_exit(struct ssb_device *dev,
@@ -3674,10 +3654,10 @@ static int b43legacy_wireless_init(struct ssb_device *dev)
        hw->max_noise = -110;
        hw->queues = 1; /* FIXME: hardware has more queues */
        SET_IEEE80211_DEV(hw, dev->dev);
-       if (is_valid_ether_addr(sprom->r1.et1mac))
-               SET_IEEE80211_PERM_ADDR(hw, sprom->r1.et1mac);
+       if (is_valid_ether_addr(sprom->et1mac))
+               SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac);
        else
-               SET_IEEE80211_PERM_ADDR(hw, sprom->r1.il0mac);
+               SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac);
 
        /* Get and initialize struct b43legacy_wl */
        wl = hw_to_b43legacy_wl(hw);
@@ -3829,6 +3809,32 @@ static struct ssb_driver b43legacy_ssb_driver = {
        .resume         = b43legacy_resume,
 };
 
+static void b43legacy_print_driverinfo(void)
+{
+       const char *feat_pci = "", *feat_leds = "", *feat_rfkill = "",
+                  *feat_pio = "", *feat_dma = "";
+
+#ifdef CONFIG_B43LEGACY_PCI_AUTOSELECT
+       feat_pci = "P";
+#endif
+#ifdef CONFIG_B43LEGACY_LEDS
+       feat_leds = "L";
+#endif
+#ifdef CONFIG_B43LEGACY_RFKILL
+       feat_rfkill = "R";
+#endif
+#ifdef CONFIG_B43LEGACY_PIO
+       feat_pio = "I";
+#endif
+#ifdef CONFIG_B43LEGACY_DMA
+       feat_dma = "D";
+#endif
+       printk(KERN_INFO "Broadcom 43xx-legacy driver loaded "
+              "[ Features: %s%s%s%s%s, Firmware-ID: "
+              B43legacy_SUPPORTED_FIRMWARE_ID " ]\n",
+              feat_pci, feat_leds, feat_rfkill, feat_pio, feat_dma);
+}
+
 static int __init b43legacy_init(void)
 {
        int err;
@@ -3839,6 +3845,8 @@ static int __init b43legacy_init(void)
        if (err)
                goto err_dfs_exit;
 
+       b43legacy_print_driverinfo();
+
        return err;
 
 err_dfs_exit: