iwlwifi: Fix built-in compilation of iwlcore
[safe/jmp/linux-2.6] / drivers / net / wireless / b43legacy / main.c
index 9db949e..ef829ee 100644 (file)
@@ -962,16 +962,61 @@ 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,
@@ -1004,46 +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,
-                                        struct ieee80211_rate *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;
+
+       src_size = dev->wl->current_beacon->len;
+       src_data = (const u8 *)dev->wl->current_beacon->data;
 
-       B43legacy_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);
+       B43legacy_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt,
+                                              u.beacon.variable));
 
-       if (unlikely(src_size < 0x24)) {
-               b43legacydbg(dev->wl, "b43legacy_generate_probe_resp: "
-                      "invalid beacon\n");
+       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;
@@ -1065,11 +1108,10 @@ static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev,
                                                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;
@@ -1094,43 +1136,21 @@ static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev,
        kfree(probe_resp_data);
 }
 
-static int b43legacy_refresh_cached_beacon(struct b43legacy_wldev *dev,
-                                          struct sk_buff *beacon)
-{
-       if (dev->cached_beacon)
-               kfree_skb(dev->cached_beacon);
-       dev->cached_beacon = beacon;
-
-       return 0;
-}
-
-static void b43legacy_update_templates(struct b43legacy_wldev *dev)
-{
-       u32 cmd;
-
-       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_b_ratetable[0]);
-
-       cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
-       cmd |= B43legacy_MACCMD_BEACON0_VALID | B43legacy_MACCMD_BEACON1_VALID;
-       b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd);
-}
-
-static void b43legacy_refresh_templates(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)
 {
-       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 = 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,
@@ -1171,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);
+       /* This is the bottom half of the asynchronous beacon update. */
 
-       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);
+       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)
@@ -1497,6 +1516,7 @@ static int b43legacy_request_firmware(struct b43legacy_wldev *dev)
        }
        if (!fw->initvals) {
                switch (dev->phy.type) {
+               case B43legacy_PHYTYPE_B:
                case B43legacy_PHYTYPE_G:
                        if ((rev >= 5) && (rev <= 10))
                                filename = "b0g0initvals5";
@@ -1514,6 +1534,7 @@ static int b43legacy_request_firmware(struct b43legacy_wldev *dev)
        }
        if (!fw->initvals_band) {
                switch (dev->phy.type) {
+               case B43legacy_PHYTYPE_B:
                case B43legacy_PHYTYPE_G:
                        if ((rev >= 5) && (rev <= 10))
                                filename = "b0g0bsinitvals5";
@@ -2709,7 +2730,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);
        }
@@ -2986,6 +3007,34 @@ 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)
@@ -3022,6 +3071,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);
 }
@@ -3167,9 +3221,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);
@@ -3225,6 +3277,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);
 
@@ -3346,6 +3400,41 @@ 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, NULL);
+       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 ieee80211_tx_control *ctl)
+{
+       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,
@@ -3359,6 +3448,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.
@@ -3809,7 +3900,7 @@ static void b43legacy_print_driverinfo(void)
 #ifdef CONFIG_B43LEGACY_DMA
        feat_dma = "D";
 #endif
-       printk(KERN_INFO "Broadcom 43xx driver loaded "
+       printk(KERN_INFO "Broadcom 43xx-legacy driver loaded "
               "[ Features: %s%s%s%s%s, Firmware-ID: "
               B43legacy_SUPPORTED_FIRMWARE_ID " ]\n",
               feat_pci, feat_leds, feat_rfkill, feat_pio, feat_dma);