]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/wireless/b43/main.c
b43: avoid unregistering device objects during suspend
[linux-2.6-omap-h63xx.git] / drivers / net / wireless / b43 / main.c
index 2add839f504518da0b1ddd380c822d8de64ee6fc..ef65c41af00fc022e4841baa843571f1ae107fda 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/wireless.h>
 #include <linux/workqueue.h>
 #include <linux/skbuff.h>
+#include <linux/io.h>
 #include <linux/dma-mapping.h>
 #include <asm/unaligned.h>
 
@@ -81,6 +82,7 @@ static const struct ssb_device_id b43_ssb_tbl[] = {
        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 7),
        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 9),
        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 10),
+       SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 11),
        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13),
        SSB_DEVTABLE_END
 };
@@ -131,7 +133,7 @@ static struct ieee80211_rate __b43_ratetable[] = {
                .power_level    = 0xFF,                         \
                .antenna_max    = 0xFF,                         \
        }
-static struct ieee80211_channel b43_bg_chantable[] = {
+static struct ieee80211_channel b43_2ghz_chantable[] = {
        CHANTAB_ENT(1, 2412),
        CHANTAB_ENT(2, 2417),
        CHANTAB_ENT(3, 2422),
@@ -147,9 +149,10 @@ static struct ieee80211_channel b43_bg_chantable[] = {
        CHANTAB_ENT(13, 2472),
        CHANTAB_ENT(14, 2484),
 };
+#define b43_2ghz_chantable_size        ARRAY_SIZE(b43_2ghz_chantable)
 
-#define b43_bg_chantable_size  ARRAY_SIZE(b43_bg_chantable)
-static struct ieee80211_channel b43_a_chantable[] = {
+#if 0
+static struct ieee80211_channel b43_5ghz_chantable[] = {
        CHANTAB_ENT(36, 5180),
        CHANTAB_ENT(40, 5200),
        CHANTAB_ENT(44, 5220),
@@ -164,8 +167,8 @@ static struct ieee80211_channel b43_a_chantable[] = {
        CHANTAB_ENT(161, 5805),
        CHANTAB_ENT(165, 5825),
 };
-
-#define b43_a_chantable_size   ARRAY_SIZE(b43_a_chantable)
+#define b43_5ghz_chantable_size        ARRAY_SIZE(b43_5ghz_chantable)
+#endif
 
 static void b43_wireless_core_exit(struct b43_wldev *dev);
 static int b43_wireless_core_init(struct b43_wldev *dev);
@@ -250,13 +253,12 @@ static void b43_ram_write(struct b43_wldev *dev, u16 offset, u32 val)
        b43_write32(dev, B43_MMIO_RAM_DATA, val);
 }
 
-static inline
-    void b43_shm_control_word(struct b43_wldev *dev, u16 routing, u16 offset)
+static inline void b43_shm_control_word(struct b43_wldev *dev,
+                                       u16 routing, u16 offset)
 {
        u32 control;
 
        /* "offset" is the WORD offset. */
-
        control = routing;
        control <<= 16;
        control |= offset;
@@ -265,8 +267,11 @@ static inline
 
 u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset)
 {
+       struct b43_wl *wl = dev->wl;
+       unsigned long flags;
        u32 ret;
 
+       spin_lock_irqsave(&wl->shm_lock, flags);
        if (routing == B43_SHM_SHARED) {
                B43_WARN_ON(offset & 0x0001);
                if (offset & 0x0003) {
@@ -277,20 +282,25 @@ u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset)
                        b43_shm_control_word(dev, routing, (offset >> 2) + 1);
                        ret |= b43_read16(dev, B43_MMIO_SHM_DATA);
 
-                       return ret;
+                       goto out;
                }
                offset >>= 2;
        }
        b43_shm_control_word(dev, routing, offset);
        ret = b43_read32(dev, B43_MMIO_SHM_DATA);
+out:
+       spin_unlock_irqrestore(&wl->shm_lock, flags);
 
        return ret;
 }
 
 u16 b43_shm_read16(struct b43_wldev * dev, u16 routing, u16 offset)
 {
+       struct b43_wl *wl = dev->wl;
+       unsigned long flags;
        u16 ret;
 
+       spin_lock_irqsave(&wl->shm_lock, flags);
        if (routing == B43_SHM_SHARED) {
                B43_WARN_ON(offset & 0x0001);
                if (offset & 0x0003) {
@@ -298,55 +308,63 @@ u16 b43_shm_read16(struct b43_wldev * dev, u16 routing, u16 offset)
                        b43_shm_control_word(dev, routing, offset >> 2);
                        ret = b43_read16(dev, B43_MMIO_SHM_DATA_UNALIGNED);
 
-                       return ret;
+                       goto out;
                }
                offset >>= 2;
        }
        b43_shm_control_word(dev, routing, offset);
        ret = b43_read16(dev, B43_MMIO_SHM_DATA);
+out:
+       spin_unlock_irqrestore(&wl->shm_lock, flags);
 
        return ret;
 }
 
 void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value)
 {
+       struct b43_wl *wl = dev->wl;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->shm_lock, flags);
        if (routing == B43_SHM_SHARED) {
                B43_WARN_ON(offset & 0x0001);
                if (offset & 0x0003) {
                        /* Unaligned access */
                        b43_shm_control_word(dev, routing, offset >> 2);
-                       mmiowb();
                        b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED,
                                    (value >> 16) & 0xffff);
-                       mmiowb();
                        b43_shm_control_word(dev, routing, (offset >> 2) + 1);
-                       mmiowb();
                        b43_write16(dev, B43_MMIO_SHM_DATA, value & 0xffff);
-                       return;
+                       goto out;
                }
                offset >>= 2;
        }
        b43_shm_control_word(dev, routing, offset);
-       mmiowb();
        b43_write32(dev, B43_MMIO_SHM_DATA, value);
+out:
+       spin_unlock_irqrestore(&wl->shm_lock, flags);
 }
 
 void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value)
 {
+       struct b43_wl *wl = dev->wl;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->shm_lock, flags);
        if (routing == B43_SHM_SHARED) {
                B43_WARN_ON(offset & 0x0001);
                if (offset & 0x0003) {
                        /* Unaligned access */
                        b43_shm_control_word(dev, routing, offset >> 2);
-                       mmiowb();
                        b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, value);
-                       return;
+                       goto out;
                }
                offset >>= 2;
        }
        b43_shm_control_word(dev, routing, offset);
-       mmiowb();
        b43_write16(dev, B43_MMIO_SHM_DATA, value);
+out:
+       spin_unlock_irqrestore(&wl->shm_lock, flags);
 }
 
 /* Read HostFlags */
@@ -993,9 +1011,8 @@ static void b43_jssi_write(struct b43_wldev *dev, u32 jssi)
 static void b43_generate_noise_sample(struct b43_wldev *dev)
 {
        b43_jssi_write(dev, 0x7F7F7F7F);
-       b43_write32(dev, B43_MMIO_STATUS2_BITFIELD,
-                   b43_read32(dev, B43_MMIO_STATUS2_BITFIELD)
-                   | (1 << 4));
+       b43_write32(dev, B43_MMIO_MACCMD,
+                   b43_read32(dev, B43_MMIO_MACCMD) | B43_MACCMD_BGNOISE);
        B43_WARN_ON(dev->noisecalc.channel_at_start != dev->phy.channel);
 }
 
@@ -1081,18 +1098,18 @@ static void handle_irq_tbtt_indication(struct b43_wldev *dev)
                if (1 /*FIXME: the last PSpoll frame was sent successfully */ )
                        b43_power_saving_ctl_bits(dev, 0);
        }
-       dev->reg124_set_0x4 = 0;
        if (b43_is_mode(dev->wl, IEEE80211_IF_TYPE_IBSS))
-               dev->reg124_set_0x4 = 1;
+               dev->dfq_valid = 1;
 }
 
 static void handle_irq_atim_end(struct b43_wldev *dev)
 {
-       if (!dev->reg124_set_0x4 /*FIXME rename this variable */ )
-               return;
-       b43_write32(dev, B43_MMIO_STATUS2_BITFIELD,
-                   b43_read32(dev, B43_MMIO_STATUS2_BITFIELD)
-                   | 0x4);
+       if (dev->dfq_valid) {
+               b43_write32(dev, B43_MMIO_MACCMD,
+                           b43_read32(dev, B43_MMIO_MACCMD)
+                           | B43_MACCMD_DFQ_VALID);
+               dev->dfq_valid = 0;
+       }
 }
 
 static void handle_irq_pmq(struct b43_wldev *dev)
@@ -1147,15 +1164,59 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
                                      u16 ram_offset,
                                      u16 shm_size_offset, u8 rate)
 {
-       int len;
-       const u8 *data;
+       unsigned int i, len, variable_len;
+       const struct ieee80211_mgmt *bcn;
+       const u8 *ie;
+       bool tim_found = 0;
 
-       B43_WARN_ON(!dev->cached_beacon);
-       len = min((size_t) dev->cached_beacon->len,
+       bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
+       len = min((size_t) dev->wl->current_beacon->len,
                  0x200 - sizeof(struct b43_plcp_hdr6));
-       data = (const u8 *)(dev->cached_beacon->data);
-       b43_write_template_common(dev, data,
+
+       b43_write_template_common(dev, (const u8 *)bcn,
                                  len, ram_offset, shm_size_offset, rate);
+
+       /* Find the position of the TIM and the DTIM_period value
+        * and write them to SHM. */
+       ie = bcn->u.beacon.variable;
+       variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+       for (i = 0; i < variable_len - 2; ) {
+               uint8_t ie_id, ie_len;
+
+               ie_id = ie[i];
+               ie_len = ie[i + 1];
+               if (ie_id == 5) {
+                       u16 tim_position;
+                       u16 dtim_period;
+                       /* This is the TIM Information Element */
+
+                       /* Check whether the ie_len is in the beacon data range. */
+                       if (variable_len < ie_len + 2 + i)
+                               break;
+                       /* A valid TIM is at least 4 bytes long. */
+                       if (ie_len < 4)
+                               break;
+                       tim_found = 1;
+
+                       tim_position = sizeof(struct b43_plcp_hdr6);
+                       tim_position += offsetof(struct ieee80211_mgmt, u.beacon.variable);
+                       tim_position += i;
+
+                       dtim_period = ie[i + 3];
+
+                       b43_shm_write16(dev, B43_SHM_SHARED,
+                                       B43_SHM_SH_TIMBPOS, tim_position);
+                       b43_shm_write16(dev, B43_SHM_SHARED,
+                                       B43_SHM_SH_DTIMPER, dtim_period);
+                       break;
+               }
+               i += ie_len + 2;
+       }
+       if (!tim_found) {
+               b43warn(dev->wl, "Did not find a valid TIM IE in "
+                       "the beacon template packet. AP or IBSS operation "
+                       "may be broken.\n");
+       }
 }
 
 static void b43_write_probe_resp_plcp(struct b43_wldev *dev,
@@ -1168,7 +1229,7 @@ static void b43_write_probe_resp_plcp(struct b43_wldev *dev,
        plcp.data = 0;
        b43_generate_plcp_hdr(&plcp, size + FCS_LEN, rate);
        dur = ieee80211_generic_frame_duration(dev->wl->hw,
-                                              dev->wl->if_id, size,
+                                              dev->wl->vif, size,
                                               B43_RATE_TO_BASE100KBPS(rate));
        /* Write PLCP in two parts and timing for packet transfer */
        tmp = le32_to_cpu(plcp.data);
@@ -1183,40 +1244,43 @@ static void b43_write_probe_resp_plcp(struct b43_wldev *dev,
  * 2) Patching duration field
  * 3) Stripping TIM
  */
-static u8 *b43_generate_probe_resp(struct b43_wldev *dev,
-                                  u16 * dest_size, u8 rate)
+static const u8 * b43_generate_probe_resp(struct b43_wldev *dev,
+                                         u16 *dest_size, u8 rate)
 {
        const u8 *src_data;
        u8 *dest_data;
        u16 src_size, elem_size, src_pos, dest_pos;
        __le16 dur;
        struct ieee80211_hdr *hdr;
+       size_t ie_start;
+
+       src_size = dev->wl->current_beacon->len;
+       src_data = (const u8 *)dev->wl->current_beacon->data;
 
-       B43_WARN_ON(!dev->cached_beacon);
-       src_size = dev->cached_beacon->len;
-       src_data = (const u8 *)dev->cached_beacon->data;
+       /* Get the start offset of the variable IEs in the packet. */
+       ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+       B43_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt, u.beacon.variable));
 
-       if (unlikely(src_size < 0x24)) {
-               b43dbg(dev->wl, "b43_generate_probe_resp: " "invalid beacon\n");
+       if (B43_WARN_ON(src_size < ie_start))
                return NULL;
-       }
 
        dest_data = kmalloc(src_size, GFP_ATOMIC);
        if (unlikely(!dest_data))
                return NULL;
 
-       /* 0x24 is offset of first variable-len Information-Element
-        * in beacon frame.
-        */
-       memcpy(dest_data, src_data, 0x24);
-       src_pos = dest_pos = 0x24;
-       for (; src_pos < src_size - 2; src_pos += elem_size) {
+       /* Copy the static data and all Information Elements, except the TIM. */
+       memcpy(dest_data, src_data, ie_start);
+       src_pos = ie_start;
+       dest_pos = ie_start;
+       for ( ; src_pos < src_size - 2; src_pos += elem_size) {
                elem_size = src_data[src_pos + 1] + 2;
-               if (src_data[src_pos] != 0x05) {        /* TIM */
-                       memcpy(dest_data + dest_pos, src_data + src_pos,
-                              elem_size);
-                       dest_pos += elem_size;
+               if (src_data[src_pos] == 5) {
+                       /* This is the TIM. */
+                       continue;
                }
+               memcpy(dest_data + dest_pos, src_data + src_pos,
+                      elem_size);
+               dest_pos += elem_size;
        }
        *dest_size = dest_pos;
        hdr = (struct ieee80211_hdr *)dest_data;
@@ -1225,7 +1289,7 @@ static u8 *b43_generate_probe_resp(struct b43_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, *dest_size,
+                                              dev->wl->vif, *dest_size,
                                               B43_RATE_TO_BASE100KBPS(rate));
        hdr->duration_id = dur;
 
@@ -1236,11 +1300,10 @@ static void b43_write_probe_resp_template(struct b43_wldev *dev,
                                          u16 ram_offset,
                                          u16 shm_size_offset, u8 rate)
 {
-       u8 *probe_resp_data;
+       const u8 *probe_resp_data;
        u16 size;
 
-       B43_WARN_ON(!dev->cached_beacon);
-       size = dev->cached_beacon->len;
+       size = dev->wl->current_beacon->len;
        probe_resp_data = b43_generate_probe_resp(dev, &size, rate);
        if (unlikely(!probe_resp_data))
                return;
@@ -1259,39 +1322,21 @@ static void b43_write_probe_resp_template(struct b43_wldev *dev,
        kfree(probe_resp_data);
 }
 
-static int b43_refresh_cached_beacon(struct b43_wldev *dev,
-                                    struct sk_buff *beacon)
-{
-       if (dev->cached_beacon)
-               kfree_skb(dev->cached_beacon);
-       dev->cached_beacon = beacon;
-
-       return 0;
-}
-
-static void b43_update_templates(struct b43_wldev *dev)
-{
-       u32 status;
-
-       B43_WARN_ON(!dev->cached_beacon);
-
-       b43_write_beacon_template(dev, 0x68, 0x18, B43_CCK_RATE_1MB);
-       b43_write_beacon_template(dev, 0x468, 0x1A, B43_CCK_RATE_1MB);
-       b43_write_probe_resp_template(dev, 0x268, 0x4A, B43_CCK_RATE_11MB);
-
-       status = b43_read32(dev, B43_MMIO_STATUS2_BITFIELD);
-       status |= 0x03;
-       b43_write32(dev, B43_MMIO_STATUS2_BITFIELD, status);
-}
-
-static void b43_refresh_templates(struct b43_wldev *dev, struct sk_buff *beacon)
+/* Asynchronously update the packet templates in template RAM.
+ * Locking: Requires wl->irq_lock to be locked. */
+static void b43_update_templates(struct b43_wl *wl, struct sk_buff *beacon)
 {
-       int err;
+       /* This is the top half of the ansynchronous beacon update.
+        * The bottom half is the beacon IRQ.
+        * Beacon update must be asynchronous to avoid sending an
+        * invalid beacon. This can happen for example, if the firmware
+        * transmits a beacon while we are updating it. */
 
-       err = b43_refresh_cached_beacon(dev, beacon);
-       if (unlikely(err))
-               return;
-       b43_update_templates(dev);
+       if (wl->current_beacon)
+               dev_kfree_skb_any(wl->current_beacon);
+       wl->current_beacon = beacon;
+       wl->beacon0_uploaded = 0;
+       wl->beacon1_uploaded = 0;
 }
 
 static void b43_set_ssid(struct b43_wldev *dev, const u8 * ssid, u8 ssid_len)
@@ -1327,33 +1372,34 @@ static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int)
 
 static void handle_irq_beacon(struct b43_wldev *dev)
 {
-       u32 status;
+       struct b43_wl *wl = dev->wl;
+       u32 cmd;
 
-       if (!b43_is_mode(dev->wl, IEEE80211_IF_TYPE_AP))
+       if (!b43_is_mode(wl, IEEE80211_IF_TYPE_AP))
                return;
 
-       dev->irq_savedstate &= ~B43_IRQ_BEACON;
-       status = b43_read32(dev, B43_MMIO_STATUS2_BITFIELD);
+       /* This is the bottom half of the asynchronous beacon update. */
 
-       if (!dev->cached_beacon || ((status & 0x1) && (status & 0x2))) {
-               /* ACK beacon IRQ. */
-               b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_BEACON);
-               dev->irq_savedstate |= B43_IRQ_BEACON;
-               if (dev->cached_beacon)
-                       kfree_skb(dev->cached_beacon);
-               dev->cached_beacon = NULL;
-               return;
-       }
-       if (!(status & 0x1)) {
-               b43_write_beacon_template(dev, 0x68, 0x18, B43_CCK_RATE_1MB);
-               status |= 0x1;
-               b43_write32(dev, B43_MMIO_STATUS2_BITFIELD, status);
+       cmd = b43_read32(dev, B43_MMIO_MACCMD);
+       if (!(cmd & B43_MACCMD_BEACON0_VALID)) {
+               if (!wl->beacon0_uploaded) {
+                       b43_write_beacon_template(dev, 0x68, 0x18,
+                                                 B43_CCK_RATE_1MB);
+                       b43_write_probe_resp_template(dev, 0x268, 0x4A,
+                                                     B43_CCK_RATE_11MB);
+                       wl->beacon0_uploaded = 1;
+               }
+               cmd |= B43_MACCMD_BEACON0_VALID;
        }
-       if (!(status & 0x2)) {
-               b43_write_beacon_template(dev, 0x468, 0x1A, B43_CCK_RATE_1MB);
-               status |= 0x2;
-               b43_write32(dev, B43_MMIO_STATUS2_BITFIELD, status);
+       if (!(cmd & B43_MACCMD_BEACON1_VALID)) {
+               if (!wl->beacon1_uploaded) {
+                       b43_write_beacon_template(dev, 0x468, 0x1A,
+                                                 B43_CCK_RATE_1MB);
+                       wl->beacon1_uploaded = 1;
+               }
+               cmd |= B43_MACCMD_BEACON1_VALID;
        }
+       b43_write32(dev, B43_MMIO_MACCMD, cmd);
 }
 
 static void handle_irq_ucode_debug(struct b43_wldev *dev)
@@ -1512,54 +1558,73 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
        return ret;
 }
 
+static void do_release_fw(struct b43_firmware_file *fw)
+{
+       release_firmware(fw->data);
+       fw->data = NULL;
+       fw->filename = NULL;
+}
+
 static void b43_release_firmware(struct b43_wldev *dev)
 {
-       release_firmware(dev->fw.ucode);
-       dev->fw.ucode = NULL;
-       release_firmware(dev->fw.pcm);
-       dev->fw.pcm = NULL;
-       release_firmware(dev->fw.initvals);
-       dev->fw.initvals = NULL;
-       release_firmware(dev->fw.initvals_band);
-       dev->fw.initvals_band = NULL;
+       do_release_fw(&dev->fw.ucode);
+       do_release_fw(&dev->fw.pcm);
+       do_release_fw(&dev->fw.initvals);
+       do_release_fw(&dev->fw.initvals_band);
 }
 
-static void b43_print_fw_helptext(struct b43_wl *wl)
+static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
 {
-       b43err(wl, "You must go to "
+       const char *text;
+
+       text = "You must go to "
               "http://linuxwireless.org/en/users/Drivers/b43#devicefirmware "
-              "and download the correct firmware (version 4).\n");
+              "and download the latest firmware (version 4).\n";
+       if (error)
+               b43err(wl, text);
+       else
+               b43warn(wl, text);
 }
 
 static int do_request_fw(struct b43_wldev *dev,
                         const char *name,
-                        const struct firmware **fw)
+                        struct b43_firmware_file *fw)
 {
        char path[sizeof(modparam_fwpostfix) + 32];
+       const struct firmware *blob;
        struct b43_fw_header *hdr;
        u32 size;
        int err;
 
-       if (!name)
+       if (!name) {
+               /* Don't fetch anything. Free possibly cached firmware. */
+               do_release_fw(fw);
                return 0;
+       }
+       if (fw->filename) {
+               if (strcmp(fw->filename, name) == 0)
+                       return 0; /* Already have this fw. */
+               /* Free the cached firmware first. */
+               do_release_fw(fw);
+       }
 
        snprintf(path, ARRAY_SIZE(path),
                 "b43%s/%s.fw",
                 modparam_fwpostfix, name);
-       err = request_firmware(fw, path, dev->dev->dev);
+       err = request_firmware(&blob, path, dev->dev->dev);
        if (err) {
                b43err(dev->wl, "Firmware file \"%s\" not found "
                       "or load failed.\n", path);
                return err;
        }
-       if ((*fw)->size < sizeof(struct b43_fw_header))
+       if (blob->size < sizeof(struct b43_fw_header))
                goto err_format;
-       hdr = (struct b43_fw_header *)((*fw)->data);
+       hdr = (struct b43_fw_header *)(blob->data);
        switch (hdr->type) {
        case B43_FW_TYPE_UCODE:
        case B43_FW_TYPE_PCM:
                size = be32_to_cpu(hdr->size);
-               if (size != (*fw)->size - sizeof(struct b43_fw_header))
+               if (size != blob->size - sizeof(struct b43_fw_header))
                        goto err_format;
                /* fallthrough */
        case B43_FW_TYPE_IV:
@@ -1570,10 +1635,15 @@ static int do_request_fw(struct b43_wldev *dev,
                goto err_format;
        }
 
-       return err;
+       fw->data = blob;
+       fw->filename = name;
+
+       return 0;
 
 err_format:
        b43err(dev->wl, "Firmware file \"%s\" format error.\n", path);
+       release_firmware(blob);
+
        return -EPROTO;
 }
 
@@ -1585,90 +1655,101 @@ static int b43_request_firmware(struct b43_wldev *dev)
        u32 tmshigh;
        int err;
 
+       /* Get microcode */
        tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH);
-       if (!fw->ucode) {
+       if ((rev >= 5) && (rev <= 10))
+               filename = "ucode5";
+       else if ((rev >= 11) && (rev <= 12))
+               filename = "ucode11";
+       else if (rev >= 13)
+               filename = "ucode13";
+       else
+               goto err_no_ucode;
+       err = do_request_fw(dev, filename, &fw->ucode);
+       if (err)
+               goto err_load;
+
+       /* Get PCM code */
+       if ((rev >= 5) && (rev <= 10))
+               filename = "pcm5";
+       else if (rev >= 11)
+               filename = NULL;
+       else
+               goto err_no_pcm;
+       err = do_request_fw(dev, filename, &fw->pcm);
+       if (err)
+               goto err_load;
+
+       /* Get initvals */
+       switch (dev->phy.type) {
+       case B43_PHYTYPE_A:
+               if ((rev >= 5) && (rev <= 10)) {
+                       if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
+                               filename = "a0g1initvals5";
+                       else
+                               filename = "a0g0initvals5";
+               } else
+                       goto err_no_initvals;
+               break;
+       case B43_PHYTYPE_G:
                if ((rev >= 5) && (rev <= 10))
-                       filename = "ucode5";
-               else if ((rev >= 11) && (rev <= 12))
-                       filename = "ucode11";
+                       filename = "b0g0initvals5";
                else if (rev >= 13)
-                       filename = "ucode13";
+                       filename = "lp0initvals13";
                else
-                       goto err_no_ucode;
-               err = do_request_fw(dev, filename, &fw->ucode);
-               if (err)
-                       goto err_load;
+                       goto err_no_initvals;
+               break;
+       case B43_PHYTYPE_N:
+               if ((rev >= 11) && (rev <= 12))
+                       filename = "n0initvals11";
+               else
+                       goto err_no_initvals;
+               break;
+       default:
+               goto err_no_initvals;
        }
-       if (!fw->pcm) {
+       err = do_request_fw(dev, filename, &fw->initvals);
+       if (err)
+               goto err_load;
+
+       /* Get bandswitch initvals */
+       switch (dev->phy.type) {
+       case B43_PHYTYPE_A:
+               if ((rev >= 5) && (rev <= 10)) {
+                       if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
+                               filename = "a0g1bsinitvals5";
+                       else
+                               filename = "a0g0bsinitvals5";
+               } else if (rev >= 11)
+                       filename = NULL;
+               else
+                       goto err_no_initvals;
+               break;
+       case B43_PHYTYPE_G:
                if ((rev >= 5) && (rev <= 10))
-                       filename = "pcm5";
+                       filename = "b0g0bsinitvals5";
                else if (rev >= 11)
                        filename = NULL;
                else
-                       goto err_no_pcm;
-               err = do_request_fw(dev, filename, &fw->pcm);
-               if (err)
-                       goto err_load;
-       }
-       if (!fw->initvals) {
-               switch (dev->phy.type) {
-               case B43_PHYTYPE_A:
-                       if ((rev >= 5) && (rev <= 10)) {
-                               if (tmshigh & B43_TMSHIGH_GPHY)
-                                       filename = "a0g1initvals5";
-                               else
-                                       filename = "a0g0initvals5";
-                       } else
-                               goto err_no_initvals;
-                       break;
-               case B43_PHYTYPE_G:
-                       if ((rev >= 5) && (rev <= 10))
-                               filename = "b0g0initvals5";
-                       else if (rev >= 13)
-                               filename = "lp0initvals13";
-                       else
-                               goto err_no_initvals;
-                       break;
-               default:
                        goto err_no_initvals;
-               }
-               err = do_request_fw(dev, filename, &fw->initvals);
-               if (err)
-                       goto err_load;
-       }
-       if (!fw->initvals_band) {
-               switch (dev->phy.type) {
-               case B43_PHYTYPE_A:
-                       if ((rev >= 5) && (rev <= 10)) {
-                               if (tmshigh & B43_TMSHIGH_GPHY)
-                                       filename = "a0g1bsinitvals5";
-                               else
-                                       filename = "a0g0bsinitvals5";
-                       } else if (rev >= 11)
-                               filename = NULL;
-                       else
-                               goto err_no_initvals;
-                       break;
-               case B43_PHYTYPE_G:
-                       if ((rev >= 5) && (rev <= 10))
-                               filename = "b0g0bsinitvals5";
-                       else if (rev >= 11)
-                               filename = NULL;
-                       else
-                               goto err_no_initvals;
-                       break;
-               default:
+               break;
+       case B43_PHYTYPE_N:
+               if ((rev >= 11) && (rev <= 12))
+                       filename = "n0bsinitvals11";
+               else
                        goto err_no_initvals;
-               }
-               err = do_request_fw(dev, filename, &fw->initvals_band);
-               if (err)
-                       goto err_load;
+               break;
+       default:
+               goto err_no_initvals;
        }
+       err = do_request_fw(dev, filename, &fw->initvals_band);
+       if (err)
+               goto err_load;
 
        return 0;
 
 err_load:
-       b43_print_fw_helptext(dev->wl);
+       b43_print_fw_helptext(dev->wl, 1);
        goto error;
 
 err_no_ucode:
@@ -1698,22 +1779,33 @@ static int b43_upload_microcode(struct b43_wldev *dev)
        const __be32 *data;
        unsigned int i, len;
        u16 fwrev, fwpatch, fwdate, fwtime;
-       u32 tmp;
+       u32 tmp, macctl;
        int err = 0;
 
+       /* Jump the microcode PSM to offset 0 */
+       macctl = b43_read32(dev, B43_MMIO_MACCTL);
+       B43_WARN_ON(macctl & B43_MACCTL_PSM_RUN);
+       macctl |= B43_MACCTL_PSM_JMP0;
+       b43_write32(dev, B43_MMIO_MACCTL, macctl);
+       /* Zero out all microcode PSM registers and shared memory. */
+       for (i = 0; i < 64; i++)
+               b43_shm_write16(dev, B43_SHM_SCRATCH, i, 0);
+       for (i = 0; i < 4096; i += 2)
+               b43_shm_write16(dev, B43_SHM_SHARED, i, 0);
+
        /* Upload Microcode. */
-       data = (__be32 *) (dev->fw.ucode->data + hdr_len);
-       len = (dev->fw.ucode->size - hdr_len) / sizeof(__be32);
+       data = (__be32 *) (dev->fw.ucode.data->data + hdr_len);
+       len = (dev->fw.ucode.data->size - hdr_len) / sizeof(__be32);
        b43_shm_control_word(dev, B43_SHM_UCODE | B43_SHM_AUTOINC_W, 0x0000);
        for (i = 0; i < len; i++) {
                b43_write32(dev, B43_MMIO_SHM_DATA, be32_to_cpu(data[i]));
                udelay(10);
        }
 
-       if (dev->fw.pcm) {
+       if (dev->fw.pcm.data) {
                /* Upload PCM data. */
-               data = (__be32 *) (dev->fw.pcm->data + hdr_len);
-               len = (dev->fw.pcm->size - hdr_len) / sizeof(__be32);
+               data = (__be32 *) (dev->fw.pcm.data->data + hdr_len);
+               len = (dev->fw.pcm.data->size - hdr_len) / sizeof(__be32);
                b43_shm_control_word(dev, B43_SHM_HW, 0x01EA);
                b43_write32(dev, B43_MMIO_SHM_DATA, 0x00004000);
                /* No need for autoinc bit in SHM_HW */
@@ -1725,9 +1817,12 @@ static int b43_upload_microcode(struct b43_wldev *dev)
        }
 
        b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_ALL);
-       b43_write32(dev, B43_MMIO_MACCTL,
-                   B43_MACCTL_PSM_RUN |
-                   B43_MACCTL_IHR_ENABLED | B43_MACCTL_INFRA);
+
+       /* Start the microcode PSM */
+       macctl = b43_read32(dev, B43_MMIO_MACCTL);
+       macctl &= ~B43_MACCTL_PSM_JMP0;
+       macctl |= B43_MACCTL_PSM_RUN;
+       b43_write32(dev, B43_MMIO_MACCTL, macctl);
 
        /* Wait for the microcode to load and respond */
        i = 0;
@@ -1736,13 +1831,17 @@ static int b43_upload_microcode(struct b43_wldev *dev)
                if (tmp == B43_IRQ_MAC_SUSPENDED)
                        break;
                i++;
-               if (i >= 50) {
+               if (i >= 20) {
                        b43err(dev->wl, "Microcode not responding\n");
-                       b43_print_fw_helptext(dev->wl);
+                       b43_print_fw_helptext(dev->wl, 1);
                        err = -ENODEV;
-                       goto out;
+                       goto error;
+               }
+               msleep_interruptible(50);
+               if (signal_pending(current)) {
+                       err = -EINTR;
+                       goto error;
                }
-               udelay(10);
        }
        b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);       /* dummy read */
 
@@ -1756,10 +1855,9 @@ static int b43_upload_microcode(struct b43_wldev *dev)
                b43err(dev->wl, "YOUR FIRMWARE IS TOO OLD. Firmware from "
                       "binary drivers older than version 4.x is unsupported. "
                       "You must upgrade your firmware files.\n");
-               b43_print_fw_helptext(dev->wl);
-               b43_write32(dev, B43_MMIO_MACCTL, 0);
+               b43_print_fw_helptext(dev->wl, 1);
                err = -EOPNOTSUPP;
-               goto out;
+               goto error;
        }
        b43dbg(dev->wl, "Loading firmware version %u.%u "
               "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n",
@@ -1770,7 +1868,20 @@ static int b43_upload_microcode(struct b43_wldev *dev)
        dev->fw.rev = fwrev;
        dev->fw.patch = fwpatch;
 
-      out:
+       if (b43_is_old_txhdr_format(dev)) {
+               b43warn(dev->wl, "You are using an old firmware image. "
+                       "Support for old firmware will be removed in July 2008.\n");
+               b43_print_fw_helptext(dev->wl, 0);
+       }
+
+       return 0;
+
+error:
+       macctl = b43_read32(dev, B43_MMIO_MACCTL);
+       macctl &= ~B43_MACCTL_PSM_RUN;
+       macctl |= B43_MACCTL_PSM_JMP0;
+       b43_write32(dev, B43_MMIO_MACCTL, macctl);
+
        return err;
 }
 
@@ -1830,7 +1941,7 @@ static int b43_write_initvals(struct b43_wldev *dev,
 
 err_format:
        b43err(dev->wl, "Initial Values Firmware file-format error.\n");
-       b43_print_fw_helptext(dev->wl);
+       b43_print_fw_helptext(dev->wl, 1);
 
        return -EPROTO;
 }
@@ -1844,19 +1955,19 @@ static int b43_upload_initvals(struct b43_wldev *dev)
        size_t count;
        int err;
 
-       hdr = (const struct b43_fw_header *)(fw->initvals->data);
-       ivals = (const struct b43_iv *)(fw->initvals->data + hdr_len);
+       hdr = (const struct b43_fw_header *)(fw->initvals.data->data);
+       ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len);
        count = be32_to_cpu(hdr->size);
        err = b43_write_initvals(dev, ivals, count,
-                                fw->initvals->size - hdr_len);
+                                fw->initvals.data->size - hdr_len);
        if (err)
                goto out;
-       if (fw->initvals_band) {
-               hdr = (const struct b43_fw_header *)(fw->initvals_band->data);
-               ivals = (const struct b43_iv *)(fw->initvals_band->data + hdr_len);
+       if (fw->initvals_band.data) {
+               hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data);
+               ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len);
                count = be32_to_cpu(hdr->size);
                err = b43_write_initvals(dev, ivals, count,
-                                        fw->initvals_band->size - hdr_len);
+                                        fw->initvals_band.data->size - hdr_len);
                if (err)
                        goto out;
        }
@@ -2063,6 +2174,7 @@ static void b43_rate_memory_init(struct b43_wldev *dev)
        switch (dev->phy.type) {
        case B43_PHYTYPE_A:
        case B43_PHYTYPE_G:
+       case B43_PHYTYPE_N:
                b43_rate_memory_write(dev, B43_OFDM_RATE_6MB, 1);
                b43_rate_memory_write(dev, B43_OFDM_RATE_12MB, 1);
                b43_rate_memory_write(dev, B43_OFDM_RATE_18MB, 1);
@@ -2092,13 +2204,19 @@ static void b43_mgmtframe_txantenna(struct b43_wldev *dev, int antenna)
 
        switch (antenna) {
        case B43_ANTENNA0:
-               ant |= B43_TX4_PHY_ANT0;
+               ant |= B43_TXH_PHY_ANT0;
                break;
        case B43_ANTENNA1:
-               ant |= B43_TX4_PHY_ANT1;
+               ant |= B43_TXH_PHY_ANT1;
+               break;
+       case B43_ANTENNA2:
+               ant |= B43_TXH_PHY_ANT2;
+               break;
+       case B43_ANTENNA3:
+               ant |= B43_TXH_PHY_ANT3;
                break;
        case B43_ANTENNA_AUTO:
-               ant |= B43_TX4_PHY_ANTLAST;
+               ant |= B43_TXH_PHY_ANT01AUTO;
                break;
        default:
                B43_WARN_ON(1);
@@ -2108,15 +2226,15 @@ static void b43_mgmtframe_txantenna(struct b43_wldev *dev, int antenna)
 
        /* For Beacons */
        tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL);
-       tmp = (tmp & ~B43_TX4_PHY_ANT) | ant;
+       tmp = (tmp & ~B43_TXH_PHY_ANT) | ant;
        b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, tmp);
        /* For ACK/CTS */
        tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL);
-       tmp = (tmp & ~B43_TX4_PHY_ANT) | ant;
+       tmp = (tmp & ~B43_TXH_PHY_ANT) | ant;
        b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, tmp);
        /* For Probe Resposes */
        tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL);
-       tmp = (tmp & ~B43_TX4_PHY_ANT) | ant;
+       tmp = (tmp & ~B43_TXH_PHY_ANT) | ant;
        b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, tmp);
 }
 
@@ -2135,11 +2253,15 @@ static int b43_chip_init(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
        int err, tmp;
-       u32 value32;
+       u32 value32, macctl;
        u16 value16;
 
-       b43_write32(dev, B43_MMIO_MACCTL,
-                   B43_MACCTL_PSM_JMP0 | B43_MACCTL_IHR_ENABLED);
+       /* Initialize the MAC control */
+       macctl = B43_MACCTL_IHR_ENABLED | B43_MACCTL_SHM_ENABLED;
+       if (dev->phy.gmode)
+               macctl |= B43_MACCTL_GMODE;
+       macctl |= B43_MACCTL_INFRA;
+       b43_write32(dev, B43_MMIO_MACCTL, macctl);
 
        err = b43_request_firmware(dev);
        if (err)
@@ -2245,6 +2367,8 @@ static void b43_periodic_every60sec(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
 
+       if (phy->type != B43_PHYTYPE_G)
+               return;
        if (!b43_has_hardware_pctl(phy))
                b43_lo_g_ctl_mark_all_unused(dev);
        if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_RSSI) {
@@ -2431,10 +2555,10 @@ static int b43_rng_read(struct hwrng *rng, u32 * data)
        return (sizeof(u16));
 }
 
-static void b43_rng_exit(struct b43_wl *wl)
+static void b43_rng_exit(struct b43_wl *wl, bool suspended)
 {
        if (wl->rng_initialized)
-               hwrng_unregister(&wl->rng);
+               __hwrng_unregister(&wl->rng, suspended);
 }
 
 static int b43_rng_init(struct b43_wl *wl)
@@ -2681,6 +2805,10 @@ static int b43_antenna_from_ieee80211(struct b43_wldev *dev, u8 antenna)
                return B43_ANTENNA0;
        case 2:         /* Antenna 1 */
                return B43_ANTENNA1;
+       case 3:         /* Antenna 2 */
+               return B43_ANTENNA2;
+       case 4:         /* Antenna 3 */
+               return B43_ANTENNA3;
        default:
                return B43_ANTENNA_DEFAULT;
        }
@@ -2927,7 +3055,7 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw,
 }
 
 static int b43_op_config_interface(struct ieee80211_hw *hw,
-                                  int if_id,
+                                  struct ieee80211_vif *vif,
                                   struct ieee80211_if_conf *conf)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
@@ -2938,7 +3066,7 @@ static int b43_op_config_interface(struct ieee80211_hw *hw,
                return -ENODEV;
        mutex_lock(&wl->mutex);
        spin_lock_irqsave(&wl->irq_lock, flags);
-       B43_WARN_ON(wl->if_id != if_id);
+       B43_WARN_ON(wl->vif != vif);
        if (conf->bssid)
                memcpy(wl->bssid, conf->bssid, ETH_ALEN);
        else
@@ -2948,7 +3076,7 @@ static int b43_op_config_interface(struct ieee80211_hw *hw,
                        B43_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP);
                        b43_set_ssid(dev, conf->ssid, conf->ssid_len);
                        if (conf->beacon)
-                               b43_refresh_templates(dev, conf->beacon);
+                               b43_update_templates(wl, conf->beacon);
                }
                b43_write_mac_bssid_templates(dev);
        }
@@ -3054,6 +3182,12 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                if (phy_rev > 9)
                        unsupported = 1;
                break;
+#ifdef CONFIG_B43_NPHY
+       case B43_PHYTYPE_N:
+               if (phy_rev > 1)
+                       unsupported = 1;
+               break;
+#endif
        default:
                unsupported = 1;
        };
@@ -3076,14 +3210,15 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                        tmp = 0x5205017F;
        } else {
                b43_write16(dev, B43_MMIO_RADIO_CONTROL, B43_RADIOCTL_ID);
-               tmp = b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH);
-               tmp <<= 16;
+               tmp = b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
                b43_write16(dev, B43_MMIO_RADIO_CONTROL, B43_RADIOCTL_ID);
-               tmp |= b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
+               tmp |= (u32)b43_read16(dev, B43_MMIO_RADIO_DATA_HIGH) << 16;
        }
        radio_manuf = (tmp & 0x00000FFF);
        radio_ver = (tmp & 0x0FFFF000) >> 12;
        radio_rev = (tmp & 0xF0000000) >> 28;
+       if (radio_manuf != 0x17F /* Broadcom */)
+               unsupported = 1;
        switch (phy_type) {
        case B43_PHYTYPE_A:
                if (radio_ver != 0x2060)
@@ -3101,6 +3236,10 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                if (radio_ver != 0x2050)
                        unsupported = 1;
                break;
+       case B43_PHYTYPE_N:
+               if (radio_ver != 0x2055)
+                       unsupported = 1;
+               break;
        default:
                B43_WARN_ON(1);
        }
@@ -3133,9 +3272,6 @@ static void setup_struct_phy_for_init(struct b43_wldev *dev,
        memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig));
        memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos));
 
-       /* Flags */
-       phy->locked = 0;
-
        phy->aci_enable = 0;
        phy->aci_wlan_automatic = 0;
        phy->aci_hw_rssi = 0;
@@ -3162,7 +3298,6 @@ static void setup_struct_phy_for_init(struct b43_wldev *dev,
        phy->lofcal = 0xFFFF;
        phy->initval = 0xFFFF;
 
-       spin_lock_init(&phy->lock);
        phy->interfmode = B43_INTERFMODE_NONE;
        phy->channel = 0xFF;
 
@@ -3177,8 +3312,8 @@ static void setup_struct_phy_for_init(struct b43_wldev *dev,
 
 static void setup_struct_wldev_for_init(struct b43_wldev *dev)
 {
-       /* Flags */
-       dev->reg124_set_0x4 = 0;
+       dev->dfq_valid = 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;
@@ -3270,14 +3405,23 @@ static void b43_set_retry_limits(struct b43_wldev *dev,
 static void b43_wireless_core_exit(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
+       u32 macctl;
 
        B43_WARN_ON(b43_status(dev) > B43_STAT_INITIALIZED);
        if (b43_status(dev) != B43_STAT_INITIALIZED)
                return;
        b43_set_status(dev, B43_STAT_UNINIT);
 
-       b43_leds_exit(dev);
-       b43_rng_exit(dev->wl);
+       /* Stop the microcode PSM. */
+       macctl = b43_read32(dev, B43_MMIO_MACCTL);
+       macctl &= ~B43_MACCTL_PSM_RUN;
+       macctl |= B43_MACCTL_PSM_JMP0;
+       b43_write32(dev, B43_MMIO_MACCTL, macctl);
+
+       if (!dev->suspend_in_progress) {
+               b43_leds_exit(dev);
+               b43_rng_exit(dev->wl, false);
+       }
        b43_dma_free(dev);
        b43_chip_exit(dev);
        b43_radio_turn_off(dev, 1);
@@ -3286,6 +3430,11 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
                kfree(phy->tssi2dbm);
        kfree(phy->lo_control);
        phy->lo_control = NULL;
+       if (dev->wl->current_beacon) {
+               dev_kfree_skb_any(dev->wl->current_beacon);
+               dev->wl->current_beacon = NULL;
+       }
+
        ssb_device_disable(dev->dev, 0);
        ssb_bus_may_powerdown(dev->dev->bus);
 }
@@ -3386,15 +3535,15 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
        b43_bluetooth_coext_enable(dev);
 
        ssb_bus_powerup(bus, 1);        /* Enable dynamic PCTL */
-       memset(wl->bssid, 0, ETH_ALEN);
-       memset(wl->mac_addr, 0, ETH_ALEN);
        b43_upload_card_macaddress(dev);
        b43_security_init(dev);
-       b43_rng_init(wl);
+       if (!dev->suspend_in_progress)
+               b43_rng_init(wl);
 
        b43_set_status(dev, B43_STAT_INITIALIZED);
 
-       b43_leds_init(dev);
+       if (!dev->suspend_in_progress)
+               b43_leds_init(dev);
 out:
        return err;
 
@@ -3436,7 +3585,7 @@ static int b43_op_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);
 
@@ -3464,7 +3613,8 @@ static void b43_op_remove_interface(struct ieee80211_hw *hw,
        mutex_lock(&wl->mutex);
 
        B43_WARN_ON(!wl->operating);
-       B43_WARN_ON(wl->if_id != conf->if_id);
+       B43_WARN_ON(wl->vif != conf->vif);
+       wl->vif = NULL;
 
        wl->operating = 0;
 
@@ -3483,6 +3633,15 @@ static int b43_op_start(struct ieee80211_hw *hw)
        struct b43_wldev *dev = wl->current_dev;
        int did_init = 0;
        int err = 0;
+       bool do_rfkill_exit = 0;
+
+       /* Kill all old instance specific information to make sure
+        * the card won't use it in the short timeframe between start
+        * and mac80211 reconfiguring it. */
+       memset(wl->bssid, 0, ETH_ALEN);
+       memset(wl->mac_addr, 0, ETH_ALEN);
+       wl->filter_flags = 0;
+       wl->radiotap_enabled = 0;
 
        /* First register RFkill.
         * LEDs that are registered later depend on it. */
@@ -3492,8 +3651,10 @@ static int b43_op_start(struct ieee80211_hw *hw)
 
        if (b43_status(dev) < B43_STAT_INITIALIZED) {
                err = b43_wireless_core_init(dev);
-               if (err)
+               if (err) {
+                       do_rfkill_exit = 1;
                        goto out_mutex_unlock;
+               }
                did_init = 1;
        }
 
@@ -3502,6 +3663,7 @@ static int b43_op_start(struct ieee80211_hw *hw)
                if (err) {
                        if (did_init)
                                b43_wireless_core_exit(dev);
+                       do_rfkill_exit = 1;
                        goto out_mutex_unlock;
                }
        }
@@ -3509,6 +3671,9 @@ static int b43_op_start(struct ieee80211_hw *hw)
  out_mutex_unlock:
        mutex_unlock(&wl->mutex);
 
+       if (do_rfkill_exit)
+               b43_rfkill_exit(dev);
+
        return err;
 }
 
@@ -3546,6 +3711,40 @@ out_unlock:
        return err;
 }
 
+static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, int aid, int set)
+{
+       struct b43_wl *wl = hw_to_b43_wl(hw);
+       struct sk_buff *beacon;
+       unsigned long flags;
+
+       /* We could modify the existing beacon and set the aid bit in
+        * the TIM field, but that would probably require resizing and
+        * moving of data within the beacon template.
+        * Simply request a new beacon and let mac80211 do the hard work. */
+       beacon = ieee80211_beacon_get(hw, wl->vif, NULL);
+       if (unlikely(!beacon))
+               return -ENOMEM;
+       spin_lock_irqsave(&wl->irq_lock, flags);
+       b43_update_templates(wl, beacon);
+       spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+       return 0;
+}
+
+static int b43_op_ibss_beacon_update(struct ieee80211_hw *hw,
+                                    struct sk_buff *beacon,
+                                    struct ieee80211_tx_control *ctl)
+{
+       struct b43_wl *wl = hw_to_b43_wl(hw);
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->irq_lock, flags);
+       b43_update_templates(wl, beacon);
+       spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+       return 0;
+}
+
 static const struct ieee80211_ops b43_hw_ops = {
        .tx                     = b43_op_tx,
        .conf_tx                = b43_op_conf_tx,
@@ -3560,6 +3759,8 @@ static const struct ieee80211_ops b43_hw_ops = {
        .start                  = b43_op_start,
        .stop                   = b43_op_stop,
        .set_retry_limit        = b43_op_set_retry_limit,
+       .set_tim                = b43_op_beacon_set_tim,
+       .beacon_update          = b43_op_ibss_beacon_update,
 };
 
 /* Hard-reset the chip. Do not call this directly.
@@ -3604,72 +3805,30 @@ static void b43_chip_reset(struct work_struct *work)
 }
 
 static int b43_setup_modes(struct b43_wldev *dev,
-                          int have_aphy, int have_bphy, int have_gphy)
+                          bool have_2ghz_phy, bool have_5ghz_phy)
 {
        struct ieee80211_hw *hw = dev->wl->hw;
        struct ieee80211_hw_mode *mode;
        struct b43_phy *phy = &dev->phy;
-       int cnt = 0;
        int err;
 
-/*FIXME: Don't tell ieee80211 about an A-PHY, because we currently don't support A-PHY. */
-       have_aphy = 0;
-
-       phy->possible_phymodes = 0;
-       for (; 1; cnt++) {
-               if (have_aphy) {
-                       B43_WARN_ON(cnt >= B43_MAX_PHYHWMODES);
-                       mode = &phy->hwmodes[cnt];
-
-                       mode->mode = MODE_IEEE80211A;
-                       mode->num_channels = b43_a_chantable_size;
-                       mode->channels = b43_a_chantable;
-                       mode->num_rates = b43_a_ratetable_size;
-                       mode->rates = b43_a_ratetable;
-                       err = ieee80211_register_hwmode(hw, mode);
-                       if (err)
-                               return err;
-
-                       phy->possible_phymodes |= B43_PHYMODE_A;
-                       have_aphy = 0;
-                       continue;
-               }
-               if (have_bphy) {
-                       B43_WARN_ON(cnt >= B43_MAX_PHYHWMODES);
-                       mode = &phy->hwmodes[cnt];
-
-                       mode->mode = MODE_IEEE80211B;
-                       mode->num_channels = b43_bg_chantable_size;
-                       mode->channels = b43_bg_chantable;
-                       mode->num_rates = b43_b_ratetable_size;
-                       mode->rates = b43_b_ratetable;
-                       err = ieee80211_register_hwmode(hw, mode);
-                       if (err)
-                               return err;
-
-                       phy->possible_phymodes |= B43_PHYMODE_B;
-                       have_bphy = 0;
-                       continue;
-               }
-               if (have_gphy) {
-                       B43_WARN_ON(cnt >= B43_MAX_PHYHWMODES);
-                       mode = &phy->hwmodes[cnt];
-
-                       mode->mode = MODE_IEEE80211G;
-                       mode->num_channels = b43_bg_chantable_size;
-                       mode->channels = b43_bg_chantable;
-                       mode->num_rates = b43_g_ratetable_size;
-                       mode->rates = b43_g_ratetable;
-                       err = ieee80211_register_hwmode(hw, mode);
-                       if (err)
-                               return err;
-
-                       phy->possible_phymodes |= B43_PHYMODE_G;
-                       have_gphy = 0;
-                       continue;
-               }
-               break;
-       }
+       /* XXX: This function will go away soon, when mac80211
+        *      band stuff is rewritten. So this is just a hack.
+        *      For now we always claim GPHY mode, as there is no
+        *      support for NPHY and APHY in the device, yet.
+        *      This assumption is OK, as any B, N or A PHY will already
+        *      have died a horrible sanity check death earlier. */
+
+       mode = &phy->hwmodes[0];
+       mode->mode = MODE_IEEE80211G;
+       mode->num_channels = b43_2ghz_chantable_size;
+       mode->channels = b43_2ghz_chantable;
+       mode->num_rates = b43_g_ratetable_size;
+       mode->rates = b43_g_ratetable;
+       err = ieee80211_register_hwmode(hw, mode);
+       if (err)
+               return err;
+       phy->possible_phymodes |= B43_PHYMODE_G;
 
        return 0;
 }
@@ -3687,7 +3846,7 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
        struct ssb_bus *bus = dev->dev->bus;
        struct pci_dev *pdev = bus->host_pci;
        int err;
-       int have_aphy = 0, have_bphy = 0, have_gphy = 0;
+       bool have_2ghz_phy = 0, have_5ghz_phy = 0;
        u32 tmp;
 
        /* Do NOT do any device initialization here.
@@ -3707,17 +3866,12 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
                u32 tmshigh;
 
                tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH);
-               have_aphy = !!(tmshigh & B43_TMSHIGH_APHY);
-               have_gphy = !!(tmshigh & B43_TMSHIGH_GPHY);
-               if (!have_aphy && !have_gphy)
-                       have_bphy = 1;
-       } else if (dev->dev->id.revision == 4) {
-               have_gphy = 1;
-               have_aphy = 1;
+               have_2ghz_phy = !!(tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY);
+               have_5ghz_phy = !!(tmshigh & B43_TMSHIGH_HAVE_5GHZ_PHY);
        } else
-               have_bphy = 1;
+               B43_WARN_ON(1);
 
-       dev->phy.gmode = (have_gphy || have_bphy);
+       dev->phy.gmode = have_2ghz_phy;
        tmp = dev->phy.gmode ? B43_TMSLOW_GMODE : 0;
        b43_wireless_core_reset(dev, tmp);
 
@@ -3729,31 +3883,34 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
            (pdev->device != 0x4312 &&
             pdev->device != 0x4319 && pdev->device != 0x4324)) {
                /* No multiband support. */
-               have_aphy = 0;
-               have_bphy = 0;
-               have_gphy = 0;
+               have_2ghz_phy = 0;
+               have_5ghz_phy = 0;
                switch (dev->phy.type) {
                case B43_PHYTYPE_A:
-                       have_aphy = 1;
-                       break;
-               case B43_PHYTYPE_B:
-                       have_bphy = 1;
+                       have_5ghz_phy = 1;
                        break;
                case B43_PHYTYPE_G:
-                       have_gphy = 1;
+               case B43_PHYTYPE_N:
+                       have_2ghz_phy = 1;
                        break;
                default:
                        B43_WARN_ON(1);
                }
        }
-       dev->phy.gmode = (have_gphy || have_bphy);
+       if (dev->phy.type == B43_PHYTYPE_A) {
+               /* FIXME */
+               b43err(wl, "IEEE 802.11a devices are unsupported\n");
+               err = -EOPNOTSUPP;
+               goto err_powerdown;
+       }
+       dev->phy.gmode = have_2ghz_phy;
        tmp = dev->phy.gmode ? B43_TMSLOW_GMODE : 0;
        b43_wireless_core_reset(dev, tmp);
 
        err = b43_validate_chipaccess(dev);
        if (err)
                goto err_powerdown;
-       err = b43_setup_modes(dev, have_aphy, have_bphy, have_gphy);
+       err = b43_setup_modes(dev, have_2ghz_phy, have_5ghz_phy);
        if (err)
                goto err_powerdown;
 
@@ -3896,6 +4053,7 @@ static int b43_wireless_init(struct ssb_device *dev)
        wl->hw = hw;
        spin_lock_init(&wl->irq_lock);
        spin_lock_init(&wl->leds_lock);
+       spin_lock_init(&wl->shm_lock);
        mutex_init(&wl->mutex);
        INIT_LIST_HEAD(&wl->devlist);
 
@@ -3982,6 +4140,7 @@ static int b43_suspend(struct ssb_device *dev, pm_message_t state)
        b43dbg(wl, "Suspending...\n");
 
        mutex_lock(&wl->mutex);
+       wldev->suspend_in_progress = true;
        wldev->suspend_init_status = b43_status(wldev);
        if (wldev->suspend_init_status >= B43_STAT_STARTED)
                b43_wireless_core_stop(wldev);
@@ -4013,15 +4172,17 @@ static int b43_resume(struct ssb_device *dev)
        if (wldev->suspend_init_status >= B43_STAT_STARTED) {
                err = b43_wireless_core_start(wldev);
                if (err) {
+                       b43_leds_exit(wldev);
+                       b43_rng_exit(wldev->wl, true);
                        b43_wireless_core_exit(wldev);
                        b43err(wl, "Resume failed at core start\n");
                        goto out;
                }
        }
-       mutex_unlock(&wl->mutex);
-
        b43dbg(wl, "Device resumed.\n");
-      out:
+ out:
+       wldev->suspend_in_progress = false;
+       mutex_unlock(&wl->mutex);
        return err;
 }