Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6.git] / drivers / net / wireless / b43legacy / main.c
index 0f7a6e7..5f533b9 100644 (file)
@@ -95,28 +95,29 @@ MODULE_DEVICE_TABLE(ssb, b43legacy_ssb_tbl);
  * data in there. This data is the same for all devices, so we don't
  * get concurrency issues */
 #define RATETAB_ENT(_rateid, _flags) \
-       {                                                       \
-               .rate   = B43legacy_RATE_TO_100KBPS(_rateid),   \
-               .val    = (_rateid),                            \
-               .val2   = (_rateid),                            \
-               .flags  = (_flags),                             \
+       {                                                               \
+               .bitrate        = B43legacy_RATE_TO_100KBPS(_rateid),   \
+               .hw_value       = (_rateid),                            \
+               .flags          = (_flags),                             \
        }
+/*
+ * NOTE: When changing this, sync with xmit.c's
+ *      b43legacy_plcp_get_bitrate_idx_* functions!
+ */
 static struct ieee80211_rate __b43legacy_ratetable[] = {
-       RATETAB_ENT(B43legacy_CCK_RATE_1MB, IEEE80211_RATE_CCK),
-       RATETAB_ENT(B43legacy_CCK_RATE_2MB, IEEE80211_RATE_CCK_2),
-       RATETAB_ENT(B43legacy_CCK_RATE_5MB, IEEE80211_RATE_CCK_2),
-       RATETAB_ENT(B43legacy_CCK_RATE_11MB, IEEE80211_RATE_CCK_2),
-       RATETAB_ENT(B43legacy_OFDM_RATE_6MB, IEEE80211_RATE_OFDM),
-       RATETAB_ENT(B43legacy_OFDM_RATE_9MB, IEEE80211_RATE_OFDM),
-       RATETAB_ENT(B43legacy_OFDM_RATE_12MB, IEEE80211_RATE_OFDM),
-       RATETAB_ENT(B43legacy_OFDM_RATE_18MB, IEEE80211_RATE_OFDM),
-       RATETAB_ENT(B43legacy_OFDM_RATE_24MB, IEEE80211_RATE_OFDM),
-       RATETAB_ENT(B43legacy_OFDM_RATE_36MB, IEEE80211_RATE_OFDM),
-       RATETAB_ENT(B43legacy_OFDM_RATE_48MB, IEEE80211_RATE_OFDM),
-       RATETAB_ENT(B43legacy_OFDM_RATE_54MB, IEEE80211_RATE_OFDM),
+       RATETAB_ENT(B43legacy_CCK_RATE_1MB, 0),
+       RATETAB_ENT(B43legacy_CCK_RATE_2MB, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATETAB_ENT(B43legacy_CCK_RATE_5MB, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATETAB_ENT(B43legacy_CCK_RATE_11MB, IEEE80211_RATE_SHORT_PREAMBLE),
+       RATETAB_ENT(B43legacy_OFDM_RATE_6MB, 0),
+       RATETAB_ENT(B43legacy_OFDM_RATE_9MB, 0),
+       RATETAB_ENT(B43legacy_OFDM_RATE_12MB, 0),
+       RATETAB_ENT(B43legacy_OFDM_RATE_18MB, 0),
+       RATETAB_ENT(B43legacy_OFDM_RATE_24MB, 0),
+       RATETAB_ENT(B43legacy_OFDM_RATE_36MB, 0),
+       RATETAB_ENT(B43legacy_OFDM_RATE_48MB, 0),
+       RATETAB_ENT(B43legacy_OFDM_RATE_54MB, 0),
 };
-#define b43legacy_a_ratetable          (__b43legacy_ratetable + 4)
-#define b43legacy_a_ratetable_size     8
 #define b43legacy_b_ratetable          (__b43legacy_ratetable + 0)
 #define b43legacy_b_ratetable_size     4
 #define b43legacy_g_ratetable          (__b43legacy_ratetable + 0)
@@ -124,14 +125,8 @@ static struct ieee80211_rate __b43legacy_ratetable[] = {
 
 #define CHANTAB_ENT(_chanid, _freq) \
        {                                                       \
-               .chan   = (_chanid),                            \
-               .freq   = (_freq),                              \
-               .val    = (_chanid),                            \
-               .flag   = IEEE80211_CHAN_W_SCAN |               \
-                         IEEE80211_CHAN_W_ACTIVE_SCAN |        \
-                         IEEE80211_CHAN_W_IBSS,                \
-               .power_level    = 0x0A,                         \
-               .antenna_max    = 0xFF,                         \
+               .center_freq    = (_freq),                      \
+               .hw_value       = (_chanid),                    \
        }
 static struct ieee80211_channel b43legacy_bg_chantable[] = {
        CHANTAB_ENT(1, 2412),
@@ -149,7 +144,20 @@ static struct ieee80211_channel b43legacy_bg_chantable[] = {
        CHANTAB_ENT(13, 2472),
        CHANTAB_ENT(14, 2484),
 };
-#define b43legacy_bg_chantable_size    ARRAY_SIZE(b43legacy_bg_chantable)
+
+static struct ieee80211_supported_band b43legacy_band_2GHz_BPHY = {
+       .channels = b43legacy_bg_chantable,
+       .n_channels = ARRAY_SIZE(b43legacy_bg_chantable),
+       .bitrates = b43legacy_b_ratetable,
+       .n_bitrates = b43legacy_b_ratetable_size,
+};
+
+static struct ieee80211_supported_band b43legacy_band_2GHz_GPHY = {
+       .channels = b43legacy_bg_chantable,
+       .n_channels = ARRAY_SIZE(b43legacy_bg_chantable),
+       .bitrates = b43legacy_g_ratetable,
+       .n_bitrates = b43legacy_g_ratetable_size,
+};
 
 static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev);
 static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev);
@@ -797,9 +805,8 @@ static void b43legacy_generate_noise_sample(struct b43legacy_wldev *dev)
 {
        b43legacy_jssi_write(dev, 0x7F7F7F7F);
        b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
-                         b43legacy_read32(dev,
-                         B43legacy_MMIO_MACCMD)
-                         | (1 << 4));
+                         b43legacy_read32(dev, B43legacy_MMIO_MACCMD)
+                         | B43legacy_MACCMD_BGNOISE);
        B43legacy_WARN_ON(dev->noisecalc.channel_at_start !=
                            dev->phy.channel);
 }
@@ -839,10 +846,10 @@ static void handle_irq_noise(struct b43legacy_wldev *dev)
        /* Get the noise samples. */
        B43legacy_WARN_ON(dev->noisecalc.nr_samples >= 8);
        i = dev->noisecalc.nr_samples;
-       noise[0] = limit_value(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
-       noise[1] = limit_value(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
-       noise[2] = limit_value(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
-       noise[3] = limit_value(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
+       noise[0] = clamp_val(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
+       noise[1] = clamp_val(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
+       noise[2] = clamp_val(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
+       noise[3] = clamp_val(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1);
        dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]];
        dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]];
        dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]];
@@ -888,18 +895,18 @@ static void handle_irq_tbtt_indication(struct b43legacy_wldev *dev)
                if (1/*FIXME: the last PSpoll frame was sent successfully */)
                        b43legacy_power_saving_ctl_bits(dev, -1, -1);
        }
-       dev->reg124_set_0x4 = 0;
        if (b43legacy_is_mode(dev->wl, IEEE80211_IF_TYPE_IBSS))
-               dev->reg124_set_0x4 = 1;
+               dev->dfq_valid = 1;
 }
 
 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_MACCMD,
-                         b43legacy_read32(dev, B43legacy_MMIO_MACCMD)
-                         | 0x4);
+       if (dev->dfq_valid) {
+               b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
+                                 b43legacy_read32(dev, B43legacy_MMIO_MACCMD)
+                                 | B43legacy_MACCMD_DFQ_VALID);
+               dev->dfq_valid = 0;
+       }
 }
 
 static void handle_irq_pmq(struct b43legacy_wldev *dev)
@@ -955,32 +962,77 @@ static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev,
                                            u16 ram_offset,
                                            u16 shm_size_offset, u8 rate)
 {
-       int len;
-       const u8 *data;
 
-       B43legacy_WARN_ON(!dev->cached_beacon);
-       len = min((size_t)dev->cached_beacon->len,
+       unsigned int i, len, variable_len;
+       const struct ieee80211_mgmt *bcn;
+       const u8 *ie;
+       bool tim_found = 0;
+
+       bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
+       len = min((size_t)dev->wl->current_beacon->len,
                  0x200 - sizeof(struct b43legacy_plcp_hdr6));
-       data = (const u8 *)(dev->cached_beacon->data);
-       b43legacy_write_template_common(dev, data,
-                                       len, ram_offset,
+
+       b43legacy_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 b43legacy_plcp_hdr6);
+                       tim_position += offsetof(struct ieee80211_mgmt,
+                                                u.beacon.variable);
+                       tim_position += i;
+
+                       dtim_period = ie[i + 3];
+
+                       b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
+                                       B43legacy_SHM_SH_TIMPOS, tim_position);
+                       b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
+                                       B43legacy_SHM_SH_DTIMP, dtim_period);
+                       break;
+               }
+               i += ie_len + 2;
+       }
+       if (!tim_found) {
+               b43legacywarn(dev->wl, "Did not find a valid TIM IE in the "
+                             "beacon template packet. AP or IBSS operation "
+                             "may be broken.\n");
+       }
 }
 
 static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev,
                                            u16 shm_offset, u16 size,
-                                           u8 rate)
+                                           struct ieee80211_rate *rate)
 {
        struct b43legacy_plcp_hdr4 plcp;
        u32 tmp;
        __le16 dur;
 
        plcp.data = 0;
-       b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate);
+       b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate->bitrate);
        dur = ieee80211_generic_frame_duration(dev->wl->hw,
                                               dev->wl->vif,
                                               size,
-                                              B43legacy_RATE_TO_100KBPS(rate));
+                                              rate);
        /* Write PLCP in two parts and timing for packet transfer */
        tmp = le32_to_cpu(plcp.data);
        b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, shm_offset,
@@ -997,45 +1049,44 @@ static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev,
  * 2) Patching duration field
  * 3) Stripping TIM
  */
-static u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
-                                        u16 *dest_size, u8 rate)
+static const u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
+                                              u16 *dest_size,
+                                              struct ieee80211_rate *rate)
 {
        const u8 *src_data;
        u8 *dest_data;
-       u16 src_size;
-       u16 elem_size;
-       u16 src_pos;
-       u16 dest_pos;
+       u16 src_size, elem_size, src_pos, dest_pos;
        __le16 dur;
        struct ieee80211_hdr *hdr;
+       size_t ie_start;
 
-       B43legacy_WARN_ON(!dev->cached_beacon);
-       src_size = dev->cached_beacon->len;
-       src_data = (const u8 *)dev->cached_beacon->data;
+       src_size = dev->wl->current_beacon->len;
+       src_data = (const u8 *)dev->wl->current_beacon->data;
 
-       if (unlikely(src_size < 0x24)) {
-               b43legacydbg(dev->wl, "b43legacy_generate_probe_resp: "
-                      "invalid beacon\n");
+       /* Get the start offset of the variable IEs in the packet. */
+       ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+       B43legacy_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt,
+                                              u.beacon.variable));
+
+       if (B43legacy_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 = 0x24;
-       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;
@@ -1046,7 +1097,7 @@ static u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
        dur = ieee80211_generic_frame_duration(dev->wl->hw,
                                               dev->wl->vif,
                                               *dest_size,
-                                              B43legacy_RATE_TO_100KBPS(rate));
+                                              rate);
        hdr->duration_id = dur;
 
        return dest_data;
@@ -1054,13 +1105,13 @@ static u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
 
 static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev,
                                                u16 ram_offset,
-                                               u16 shm_size_offset, u8 rate)
+                                               u16 shm_size_offset,
+                                               struct ieee80211_rate *rate)
 {
-       u8 *probe_resp_data;
+       const u8 *probe_resp_data;
        u16 size;
 
-       B43legacy_WARN_ON(!dev->cached_beacon);
-       size = dev->cached_beacon->len;
+       size = dev->wl->current_beacon->len;
        probe_resp_data = b43legacy_generate_probe_resp(dev, &size, rate);
        if (unlikely(!probe_resp_data))
                return;
@@ -1069,59 +1120,37 @@ static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev,
         * all possible basic rates
         */
        b43legacy_write_probe_resp_plcp(dev, 0x31A, size,
-                                       B43legacy_CCK_RATE_1MB);
+                                       &b43legacy_b_ratetable[0]);
        b43legacy_write_probe_resp_plcp(dev, 0x32C, size,
-                                       B43legacy_CCK_RATE_2MB);
+                                       &b43legacy_b_ratetable[1]);
        b43legacy_write_probe_resp_plcp(dev, 0x33E, size,
-                                       B43legacy_CCK_RATE_5MB);
+                                       &b43legacy_b_ratetable[2]);
        b43legacy_write_probe_resp_plcp(dev, 0x350, size,
-                                       B43legacy_CCK_RATE_11MB);
+                                       &b43legacy_b_ratetable[3]);
 
        size = min((size_t)size,
                   0x200 - sizeof(struct b43legacy_plcp_hdr6));
        b43legacy_write_template_common(dev, probe_resp_data,
                                        size, ram_offset,
-                                       shm_size_offset, rate);
+                                       shm_size_offset, rate->bitrate);
        kfree(probe_resp_data);
 }
 
-static int b43legacy_refresh_cached_beacon(struct b43legacy_wldev *dev,
-                                          struct sk_buff *beacon)
+/* Asynchronously update the packet templates in template RAM.
+ * Locking: Requires wl->irq_lock to be locked. */
+static void b43legacy_update_templates(struct b43legacy_wl *wl,
+                                      struct sk_buff *beacon)
 {
-       if (dev->cached_beacon)
-               kfree_skb(dev->cached_beacon);
-       dev->cached_beacon = beacon;
+       /* 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. */
 
-       return 0;
-}
-
-static void b43legacy_update_templates(struct b43legacy_wldev *dev)
-{
-       u32 status;
-
-       B43legacy_WARN_ON(!dev->cached_beacon);
-
-       b43legacy_write_beacon_template(dev, 0x68, 0x18,
-                                       B43legacy_CCK_RATE_1MB);
-       b43legacy_write_beacon_template(dev, 0x468, 0x1A,
-                                       B43legacy_CCK_RATE_1MB);
-       b43legacy_write_probe_resp_template(dev, 0x268, 0x4A,
-                                           B43legacy_CCK_RATE_11MB);
-
-       status = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
-       status |= 0x03;
-       b43legacy_write32(dev, B43legacy_MMIO_MACCMD, status);
-}
-
-static void b43legacy_refresh_templates(struct b43legacy_wldev *dev,
-                                       struct sk_buff *beacon)
-{
-       int err;
-
-       err = b43legacy_refresh_cached_beacon(dev, beacon);
-       if (unlikely(err))
-               return;
-       b43legacy_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 b43legacy_set_ssid(struct b43legacy_wldev *dev,
@@ -1162,38 +1191,37 @@ static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev,
 
 static void handle_irq_beacon(struct b43legacy_wldev *dev)
 {
-       u32 status;
+       struct b43legacy_wl *wl = dev->wl;
+       u32 cmd;
 
-       if (!b43legacy_is_mode(dev->wl, IEEE80211_IF_TYPE_AP))
+       if (!b43legacy_is_mode(wl, IEEE80211_IF_TYPE_AP))
                return;
 
-       dev->irq_savedstate &= ~B43legacy_IRQ_BEACON;
-       status = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
-
-       if (!dev->cached_beacon || ((status & 0x1) && (status & 0x2))) {
-               /* ACK beacon IRQ. */
-               b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON,
-                                 B43legacy_IRQ_BEACON);
-               dev->irq_savedstate |= B43legacy_IRQ_BEACON;
-               if (dev->cached_beacon)
-                       kfree_skb(dev->cached_beacon);
-               dev->cached_beacon = NULL;
-               return;
-       }
-       if (!(status & 0x1)) {
-               b43legacy_write_beacon_template(dev, 0x68, 0x18,
-                                               B43legacy_CCK_RATE_1MB);
-               status |= 0x1;
-               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_MACCMD,
-                                 status);
+       /* This is the bottom half of the asynchronous beacon update. */
+
+       cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
+       if (!(cmd & B43legacy_MACCMD_BEACON0_VALID)) {
+               if (!wl->beacon0_uploaded) {
+                       b43legacy_write_beacon_template(dev, 0x68,
+                                                       B43legacy_SHM_SH_BTL0,
+                                                       B43legacy_CCK_RATE_1MB);
+                       b43legacy_write_probe_resp_template(dev, 0x268,
+                                                           B43legacy_SHM_SH_PRTLEN,
+                                                           &__b43legacy_ratetable[3]);
+                       wl->beacon0_uploaded = 1;
+               }
+               cmd |= B43legacy_MACCMD_BEACON0_VALID;
+       }
+       if (!(cmd & B43legacy_MACCMD_BEACON1_VALID)) {
+               if (!wl->beacon1_uploaded) {
+                       b43legacy_write_beacon_template(dev, 0x468,
+                                                       B43legacy_SHM_SH_BTL1,
+                                                       B43legacy_CCK_RATE_1MB);
+                       wl->beacon1_uploaded = 1;
+               }
+               cmd |= B43legacy_MACCMD_BEACON1_VALID;
        }
+       b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd);
 }
 
 static void handle_irq_ucode_debug(struct b43legacy_wldev *dev)
@@ -1692,7 +1720,7 @@ static int b43legacy_write_initvals(struct b43legacy_wldev *dev,
                                goto err_format;
                        array_size -= sizeof(iv->data.d32);
 
-                       value = be32_to_cpu(get_unaligned(&iv->data.d32));
+                       value = get_unaligned_be32(&iv->data.d32);
                        b43legacy_write32(dev, offset, value);
 
                        iv = (const struct b43legacy_iv *)((const uint8_t *)iv +
@@ -2330,8 +2358,7 @@ static int b43legacy_rng_init(struct b43legacy_wl *wl)
 }
 
 static int b43legacy_op_tx(struct ieee80211_hw *hw,
-                          struct sk_buff *skb,
-                          struct ieee80211_tx_control *ctl)
+                          struct sk_buff *skb)
 {
        struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
        struct b43legacy_wldev *dev = wl->current_dev;
@@ -2345,18 +2372,17 @@ static int b43legacy_op_tx(struct ieee80211_hw *hw,
        /* DMA-TX is done without a global lock. */
        if (b43legacy_using_pio(dev)) {
                spin_lock_irqsave(&wl->irq_lock, flags);
-               err = b43legacy_pio_tx(dev, skb, ctl);
+               err = b43legacy_pio_tx(dev, skb);
                spin_unlock_irqrestore(&wl->irq_lock, flags);
        } else
-               err = b43legacy_dma_tx(dev, skb, ctl);
+               err = b43legacy_dma_tx(dev, skb);
 out:
        if (unlikely(err))
                return NETDEV_TX_BUSY;
        return NETDEV_TX_OK;
 }
 
-static int b43legacy_op_conf_tx(struct ieee80211_hw *hw,
-                               int queue,
+static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
                                const struct ieee80211_tx_queue_params *params)
 {
        return 0;
@@ -2552,14 +2578,16 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
        antenna_rx = b43legacy_antenna_from_ieee80211(conf->antenna_sel_rx);
 
        mutex_lock(&wl->mutex);
+       dev = wl->current_dev;
+       phy = &dev->phy;
 
        /* Switch the PHY mode (if necessary). */
-       switch (conf->phymode) {
-       case MODE_IEEE80211B:
-               new_phymode = B43legacy_PHYMODE_B;
-               break;
-       case MODE_IEEE80211G:
-               new_phymode = B43legacy_PHYMODE_G;
+       switch (conf->channel->band) {
+       case IEEE80211_BAND_2GHZ:
+               if (phy->type == B43legacy_PHYTYPE_B)
+                       new_phymode = B43legacy_PHYMODE_B;
+               else
+                       new_phymode = B43legacy_PHYMODE_G;
                break;
        default:
                B43legacy_WARN_ON(1);
@@ -2567,8 +2595,6 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
        err = b43legacy_switch_phymode(wl, new_phymode);
        if (err)
                goto out_unlock_mutex;
-       dev = wl->current_dev;
-       phy = &dev->phy;
 
        /* Disable IRQs while reconfiguring the device.
         * This makes it possible to drop the spinlock throughout
@@ -2584,8 +2610,8 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
 
        /* Switch to the requested channel.
         * The firmware takes care of races with the TX handler. */
-       if (conf->channel_val != phy->channel)
-               b43legacy_radio_selectchannel(dev, conf->channel_val, 0);
+       if (conf->channel->hw_value != phy->channel)
+               b43legacy_radio_selectchannel(dev, conf->channel->hw_value, 0);
 
        /* Enable/Disable ShortSlot timing. */
        if ((!!(conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME))
@@ -2702,7 +2728,7 @@ static int b43legacy_op_config_interface(struct ieee80211_hw *hw,
                        B43legacy_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP);
                        b43legacy_set_ssid(dev, conf->ssid, conf->ssid_len);
                        if (conf->beacon)
-                               b43legacy_refresh_templates(dev, conf->beacon);
+                               b43legacy_update_templates(wl, conf->beacon);
                }
                b43legacy_write_mac_bssid_templates(dev);
        }
@@ -2767,7 +2793,6 @@ static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev)
        /* Start data flow (TX/RX) */
        b43legacy_mac_enable(dev);
        b43legacy_interrupt_enable(dev, dev->irq_savedstate);
-       ieee80211_start_queues(dev->wl->hw);
 
        /* Start maintenance work */
        b43legacy_periodic_tasks_setup(dev);
@@ -2920,7 +2945,7 @@ static void setup_struct_phy_for_init(struct b43legacy_wldev *dev,
 static void setup_struct_wldev_for_init(struct b43legacy_wldev *dev)
 {
        /* Flags */
-       dev->reg124_set_0x4 = 0;
+       dev->dfq_valid = 0;
 
        /* Stats */
        memset(&dev->stats, 0, sizeof(dev->stats));
@@ -2979,11 +3004,38 @@ static void b43legacy_set_retry_limits(struct b43legacy_wldev *dev,
        b43legacy_shm_write16(dev, B43legacy_SHM_WIRELESS, 0x0007, long_retry);
 }
 
+static void b43legacy_set_synth_pu_delay(struct b43legacy_wldev *dev,
+                                         bool idle) {
+       u16 pu_delay = 1050;
+
+       if (b43legacy_is_mode(dev->wl, IEEE80211_IF_TYPE_IBSS) || idle)
+               pu_delay = 500;
+       if ((dev->phy.radio_ver == 0x2050) && (dev->phy.radio_rev == 8))
+               pu_delay = max(pu_delay, (u16)2400);
+
+       b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
+                             B43legacy_SHM_SH_SPUWKUP, pu_delay);
+}
+
+/* Set the TSF CFP pre-TargetBeaconTransmissionTime. */
+static void b43legacy_set_pretbtt(struct b43legacy_wldev *dev)
+{
+       u16 pretbtt;
+
+       /* The time value is in microseconds. */
+       if (b43legacy_is_mode(dev->wl, IEEE80211_IF_TYPE_IBSS))
+               pretbtt = 2;
+       else
+               pretbtt = 250;
+       b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
+                             B43legacy_SHM_SH_PRETBTT, pretbtt);
+       b43legacy_write16(dev, B43legacy_MMIO_TSF_CFP_PRETBTT, pretbtt);
+}
+
 /* 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;
 
@@ -2998,12 +3050,6 @@ static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev)
        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);
@@ -3015,6 +3061,11 @@ static void b43legacy_wireless_core_exit(struct b43legacy_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);
 }
@@ -3160,9 +3211,7 @@ static int b43legacy_wireless_core_init(struct b43legacy_wldev *dev)
        if (err)
                goto err_chip_exit;
 
-       b43legacy_write16(dev, 0x0612, 0x0050);
-       b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0416, 0x0050);
-       b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0414, 0x01F4);
+       b43legacy_set_synth_pu_delay(dev, 1);
 
        ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */
        b43legacy_upload_card_macaddress(dev);
@@ -3218,6 +3267,8 @@ static int b43legacy_op_add_interface(struct ieee80211_hw *hw,
 
        spin_lock_irqsave(&wl->irq_lock, flags);
        b43legacy_adjust_opmode(dev);
+       b43legacy_set_pretbtt(dev);
+       b43legacy_set_synth_pu_delay(dev, 0);
        b43legacy_upload_card_macaddress(dev);
        spin_unlock_irqrestore(&wl->irq_lock, flags);
 
@@ -3339,6 +3390,40 @@ out_unlock:
        return err;
 }
 
+static int b43legacy_op_beacon_set_tim(struct ieee80211_hw *hw,
+                                      int aid, int set)
+{
+       struct b43legacy_wl *wl = hw_to_b43legacy_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);
+       if (unlikely(!beacon))
+               return -ENOMEM;
+       spin_lock_irqsave(&wl->irq_lock, flags);
+       b43legacy_update_templates(wl, beacon);
+       spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+       return 0;
+}
+
+static int b43legacy_op_ibss_beacon_update(struct ieee80211_hw *hw,
+                                          struct sk_buff *beacon)
+{
+       struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->irq_lock, flags);
+       b43legacy_update_templates(wl, beacon);
+       spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+       return 0;
+}
+
 static const struct ieee80211_ops b43legacy_hw_ops = {
        .tx                     = b43legacy_op_tx,
        .conf_tx                = b43legacy_op_conf_tx,
@@ -3352,6 +3437,8 @@ static const struct ieee80211_ops b43legacy_hw_ops = {
        .start                  = b43legacy_op_start,
        .stop                   = b43legacy_op_stop,
        .set_retry_limit        = b43legacy_op_set_retry_limit,
+       .set_tim                = b43legacy_op_beacon_set_tim,
+       .beacon_update          = b43legacy_op_ibss_beacon_update,
 };
 
 /* Hard-reset the chip. Do not call this directly.
@@ -3388,6 +3475,8 @@ static void b43legacy_chip_reset(struct work_struct *work)
                }
        }
 out:
+       if (err)
+               wl->current_dev = NULL; /* Failed to init the dev. */
        mutex_unlock(&wl->mutex);
        if (err)
                b43legacyerr(wl, "Controller restart FAILED\n");
@@ -3400,48 +3489,19 @@ static int b43legacy_setup_modes(struct b43legacy_wldev *dev,
                                 int have_gphy)
 {
        struct ieee80211_hw *hw = dev->wl->hw;
-       struct ieee80211_hw_mode *mode;
        struct b43legacy_phy *phy = &dev->phy;
-       int cnt = 0;
-       int err;
 
        phy->possible_phymodes = 0;
-       for (; 1; cnt++) {
-               if (have_bphy) {
-                       B43legacy_WARN_ON(cnt >= B43legacy_MAX_PHYHWMODES);
-                       mode = &phy->hwmodes[cnt];
-
-                       mode->mode = MODE_IEEE80211B;
-                       mode->num_channels = b43legacy_bg_chantable_size;
-                       mode->channels = b43legacy_bg_chantable;
-                       mode->num_rates = b43legacy_b_ratetable_size;
-                       mode->rates = b43legacy_b_ratetable;
-                       err = ieee80211_register_hwmode(hw, mode);
-                       if (err)
-                               return err;
-
-                       phy->possible_phymodes |= B43legacy_PHYMODE_B;
-                       have_bphy = 0;
-                       continue;
-               }
-               if (have_gphy) {
-                       B43legacy_WARN_ON(cnt >= B43legacy_MAX_PHYHWMODES);
-                       mode = &phy->hwmodes[cnt];
-
-                       mode->mode = MODE_IEEE80211G;
-                       mode->num_channels = b43legacy_bg_chantable_size;
-                       mode->channels = b43legacy_bg_chantable;
-                       mode->num_rates = b43legacy_g_ratetable_size;
-                       mode->rates = b43legacy_g_ratetable;
-                       err = ieee80211_register_hwmode(hw, mode);
-                       if (err)
-                               return err;
-
-                       phy->possible_phymodes |= B43legacy_PHYMODE_G;
-                       have_gphy = 0;
-                       continue;
-               }
-               break;
+       if (have_bphy) {
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+                       &b43legacy_band_2GHz_BPHY;
+               phy->possible_phymodes |= B43legacy_PHYMODE_B;
+       }
+
+       if (have_gphy) {
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+                       &b43legacy_band_2GHz_GPHY;
+               phy->possible_phymodes |= B43legacy_PHYMODE_G;
        }
 
        return 0;
@@ -3549,9 +3609,11 @@ static void b43legacy_one_core_detach(struct ssb_device *dev)
        struct b43legacy_wldev *wldev;
        struct b43legacy_wl *wl;
 
+       /* Do not cancel ieee80211-workqueue based work here.
+        * See comment in b43legacy_remove(). */
+
        wldev = ssb_get_drvdata(dev);
        wl = wldev->wl;
-       cancel_work_sync(&wldev->restart_work);
        b43legacy_debugfs_remove_device(wldev);
        b43legacy_wireless_core_detach(wldev);
        list_del(&wldev->list);
@@ -3650,10 +3712,9 @@ static int b43legacy_wireless_init(struct ssb_device *dev)
 
        /* fill hw info */
        hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE |
-                   IEEE80211_HW_RX_INCLUDES_FCS;
-       hw->max_signal = 100;
-       hw->max_rssi = -110;
-       hw->max_noise = -110;
+                   IEEE80211_HW_RX_INCLUDES_FCS |
+                   IEEE80211_HW_SIGNAL_DBM |
+                   IEEE80211_HW_NOISE_DBM;
        hw->queues = 1; /* FIXME: hardware has more queues */
        SET_IEEE80211_DEV(hw, dev->dev);
        if (is_valid_ether_addr(sprom->et1mac))
@@ -3720,6 +3781,10 @@ static void b43legacy_remove(struct ssb_device *dev)
        struct b43legacy_wl *wl = ssb_get_devtypedata(dev);
        struct b43legacy_wldev *wldev = ssb_get_drvdata(dev);
 
+       /* We must cancel any work here before unregistering from ieee80211,
+        * as the ieee80211 unreg will destroy the workqueue. */
+       cancel_work_sync(&wldev->restart_work);
+
        B43legacy_WARN_ON(!wl);
        if (wl->current_dev == wldev)
                ieee80211_unregister_hw(wl->hw);