b43: Add simple firmware watchdog
[pandora-kernel.git] / drivers / net / wireless / b43 / main.c
index 9445a60..c14d522 100644 (file)
@@ -373,13 +373,10 @@ static inline void b43_shm_control_word(struct b43_wldev *dev,
        b43_write32(dev, B43_MMIO_SHM_CONTROL, control);
 }
 
-u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset)
+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) {
@@ -397,18 +394,26 @@ u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset)
        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)
+u32 b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset)
 {
        struct b43_wl *wl = dev->wl;
        unsigned long flags;
-       u16 ret;
+       u32 ret;
 
        spin_lock_irqsave(&wl->shm_lock, flags);
+       ret = __b43_shm_read32(dev, routing, offset);
+       spin_unlock_irqrestore(&wl->shm_lock, flags);
+
+       return ret;
+}
+
+u16 __b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset)
+{
+       u16 ret;
+
        if (routing == B43_SHM_SHARED) {
                B43_WARN_ON(offset & 0x0001);
                if (offset & 0x0003) {
@@ -423,17 +428,24 @@ u16 b43_shm_read16(struct b43_wldev * dev, u16 routing, u16 offset)
        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)
+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);
+       ret = __b43_shm_read16(dev, routing, offset);
+       spin_unlock_irqrestore(&wl->shm_lock, flags);
+
+       return ret;
+}
+
+void __b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value)
+{
        if (routing == B43_SHM_SHARED) {
                B43_WARN_ON(offset & 0x0001);
                if (offset & 0x0003) {
@@ -443,35 +455,47 @@ void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value)
                                    (value >> 16) & 0xffff);
                        b43_shm_control_word(dev, routing, (offset >> 2) + 1);
                        b43_write16(dev, B43_MMIO_SHM_DATA, value & 0xffff);
-                       goto out;
+                       return;
                }
                offset >>= 2;
        }
        b43_shm_control_word(dev, routing, offset);
        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)
+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);
+       __b43_shm_write32(dev, routing, offset, value);
+       spin_unlock_irqrestore(&wl->shm_lock, flags);
+}
+
+void __b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value)
+{
        if (routing == B43_SHM_SHARED) {
                B43_WARN_ON(offset & 0x0001);
                if (offset & 0x0003) {
                        /* Unaligned access */
                        b43_shm_control_word(dev, routing, offset >> 2);
                        b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, value);
-                       goto out;
+                       return;
                }
                offset >>= 2;
        }
        b43_shm_control_word(dev, routing, offset);
        b43_write16(dev, B43_MMIO_SHM_DATA, value);
-out:
+}
+
+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);
+       __b43_shm_write16(dev, routing, offset, value);
        spin_unlock_irqrestore(&wl->shm_lock, flags);
 }
 
@@ -1145,7 +1169,6 @@ static void b43_generate_noise_sample(struct b43_wldev *dev)
        b43_jssi_write(dev, 0x7F7F7F7F);
        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);
 }
 
 static void b43_calculate_link_quality(struct b43_wldev *dev)
@@ -1154,7 +1177,6 @@ static void b43_calculate_link_quality(struct b43_wldev *dev)
 
        if (dev->noisecalc.calculation_running)
                return;
-       dev->noisecalc.channel_at_start = dev->phy.channel;
        dev->noisecalc.calculation_running = 1;
        dev->noisecalc.nr_samples = 0;
 
@@ -1171,9 +1193,16 @@ static void handle_irq_noise(struct b43_wldev *dev)
 
        /* Bottom half of Link Quality calculation. */
 
+       /* Possible race condition: It might be possible that the user
+        * changed to a different channel in the meantime since we
+        * started the calculation. We ignore that fact, since it's
+        * not really that much of a problem. The background noise is
+        * an estimation only anyway. Slightly wrong results will get damped
+        * by the averaging of the 8 sample rounds. Additionally the
+        * value is shortlived. So it will be replaced by the next noise
+        * calculation round soon. */
+
        B43_WARN_ON(!dev->noisecalc.calculation_running);
-       if (dev->noisecalc.channel_at_start != phy->channel)
-               goto drop_calculation;
        *((__le32 *)noise) = cpu_to_le32(b43_jssi_read(dev));
        if (noise[0] == 0x7F || noise[1] == 0x7F ||
            noise[2] == 0x7F || noise[3] == 0x7F)
@@ -1214,11 +1243,10 @@ static void handle_irq_noise(struct b43_wldev *dev)
                        average -= 48;
 
                dev->stats.link_noise = average;
-             drop_calculation:
                dev->noisecalc.calculation_running = 0;
                return;
        }
-      generate_new:
+generate_new:
        b43_generate_noise_sample(dev);
 }
 
@@ -1368,18 +1396,18 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
        unsigned int rate;
        u16 ctl;
        int antenna;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon);
 
        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));
-       rate = dev->wl->beacon_txctl.tx_rate->hw_value;
+       rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value;
 
        b43_write_template_common(dev, (const u8 *)bcn,
                                  len, ram_offset, shm_size_offset, rate);
 
        /* Write the PHY TX control parameters. */
-       antenna = b43_antenna_from_ieee80211(dev,
-                       dev->wl->beacon_txctl.antenna_sel_tx);
+       antenna = b43_antenna_from_ieee80211(dev, info->antenna_sel_tx);
        antenna = b43_antenna_to_phyctl(antenna);
        ctl = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL);
        /* We can't send beacons with short preamble. Would get PHY errors. */
@@ -1430,11 +1458,17 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
                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");
-       } else
-               b43dbg(dev->wl, "Updated beacon template\n");
+               /*
+                * If ucode wants to modify TIM do it behind the beacon, this
+                * will happen, for example, when doing mesh networking.
+                */
+               b43_shm_write16(dev, B43_SHM_SHARED,
+                               B43_SHM_SH_TIMBPOS,
+                               len + sizeof(struct b43_plcp_hdr6));
+               b43_shm_write16(dev, B43_SHM_SHARED,
+                               B43_SHM_SH_DTIMPER, 0);
+       }
+       b43dbg(dev->wl, "Updated beacon template at 0x%x\n", ram_offset);
 }
 
 static void b43_write_probe_resp_plcp(struct b43_wldev *dev,
@@ -1544,12 +1578,37 @@ static void b43_write_probe_resp_template(struct b43_wldev *dev,
        kfree(probe_resp_data);
 }
 
+static void b43_upload_beacon0(struct b43_wldev *dev)
+{
+       struct b43_wl *wl = dev->wl;
+
+       if (wl->beacon0_uploaded)
+               return;
+       b43_write_beacon_template(dev, 0x68, 0x18);
+       /* FIXME: Probe resp upload doesn't really belong here,
+        *        but we don't use that feature anyway. */
+       b43_write_probe_resp_template(dev, 0x268, 0x4A,
+                                     &__b43_ratetable[3]);
+       wl->beacon0_uploaded = 1;
+}
+
+static void b43_upload_beacon1(struct b43_wldev *dev)
+{
+       struct b43_wl *wl = dev->wl;
+
+       if (wl->beacon1_uploaded)
+               return;
+       b43_write_beacon_template(dev, 0x468, 0x1A);
+       wl->beacon1_uploaded = 1;
+}
+
 static void handle_irq_beacon(struct b43_wldev *dev)
 {
        struct b43_wl *wl = dev->wl;
        u32 cmd, beacon0_valid, beacon1_valid;
 
-       if (!b43_is_mode(wl, IEEE80211_IF_TYPE_AP))
+       if (!b43_is_mode(wl, IEEE80211_IF_TYPE_AP) &&
+           !b43_is_mode(wl, IEEE80211_IF_TYPE_MESH_POINT))
                return;
 
        /* This is the bottom half of the asynchronous beacon update. */
@@ -1568,24 +1627,27 @@ static void handle_irq_beacon(struct b43_wldev *dev)
                return;
        }
 
-       if (!beacon0_valid) {
-               if (!wl->beacon0_uploaded) {
-                       b43_write_beacon_template(dev, 0x68, 0x18);
-                       b43_write_probe_resp_template(dev, 0x268, 0x4A,
-                                                     &__b43_ratetable[3]);
-                       wl->beacon0_uploaded = 1;
-               }
+       if (unlikely(wl->beacon_templates_virgin)) {
+               /* We never uploaded a beacon before.
+                * Upload both templates now, but only mark one valid. */
+               wl->beacon_templates_virgin = 0;
+               b43_upload_beacon0(dev);
+               b43_upload_beacon1(dev);
                cmd = b43_read32(dev, B43_MMIO_MACCMD);
                cmd |= B43_MACCMD_BEACON0_VALID;
                b43_write32(dev, B43_MMIO_MACCMD, cmd);
-       } else if (!beacon1_valid) {
-               if (!wl->beacon1_uploaded) {
-                       b43_write_beacon_template(dev, 0x468, 0x1A);
-                       wl->beacon1_uploaded = 1;
+       } else {
+               if (!beacon0_valid) {
+                       b43_upload_beacon0(dev);
+                       cmd = b43_read32(dev, B43_MMIO_MACCMD);
+                       cmd |= B43_MACCMD_BEACON0_VALID;
+                       b43_write32(dev, B43_MMIO_MACCMD, cmd);
+               } else if (!beacon1_valid) {
+                       b43_upload_beacon1(dev);
+                       cmd = b43_read32(dev, B43_MMIO_MACCMD);
+                       cmd |= B43_MACCMD_BEACON1_VALID;
+                       b43_write32(dev, B43_MMIO_MACCMD, cmd);
                }
-               cmd = b43_read32(dev, B43_MMIO_MACCMD);
-               cmd |= B43_MACCMD_BEACON1_VALID;
-               b43_write32(dev, B43_MMIO_MACCMD, cmd);
        }
 }
 
@@ -1613,8 +1675,7 @@ static void b43_beacon_update_trigger_work(struct work_struct *work)
 
 /* 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,
-                                const struct ieee80211_tx_control *txctl)
+static void b43_update_templates(struct b43_wl *wl, struct sk_buff *beacon)
 {
        /* This is the top half of the ansynchronous beacon update.
         * The bottom half is the beacon IRQ.
@@ -1625,7 +1686,6 @@ static void b43_update_templates(struct b43_wl *wl, struct sk_buff *beacon,
        if (wl->current_beacon)
                dev_kfree_skb_any(wl->current_beacon);
        wl->current_beacon = beacon;
-       memcpy(&wl->beacon_txctl, txctl, sizeof(wl->beacon_txctl));
        wl->beacon0_uploaded = 0;
        wl->beacon1_uploaded = 0;
        queue_work(wl->hw->workqueue, &wl->beacon_update_trigger);
@@ -1664,9 +1724,100 @@ static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int)
        b43dbg(dev->wl, "Set beacon interval to %u\n", beacon_int);
 }
 
+static void b43_handle_firmware_panic(struct b43_wldev *dev)
+{
+       u16 reason;
+
+       /* Read the register that contains the reason code for the panic. */
+       reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_FWPANIC_REASON_REG);
+       b43err(dev->wl, "Whoopsy, firmware panic! Reason: %u\n", reason);
+
+       switch (reason) {
+       default:
+               b43dbg(dev->wl, "The panic reason is unknown.\n");
+               /* fallthrough */
+       case B43_FWPANIC_DIE:
+               /* Do not restart the controller or firmware.
+                * The device is nonfunctional from now on.
+                * Restarting would result in this panic to trigger again,
+                * so we avoid that recursion. */
+               break;
+       case B43_FWPANIC_RESTART:
+               b43_controller_restart(dev, "Microcode panic");
+               break;
+       }
+}
+
 static void handle_irq_ucode_debug(struct b43_wldev *dev)
 {
-       //TODO
+       unsigned int i, cnt;
+       u16 reason, marker_id, marker_line;
+       __le16 *buf;
+
+       /* The proprietary firmware doesn't have this IRQ. */
+       if (!dev->fw.opensource)
+               return;
+
+       /* Read the register that contains the reason code for this IRQ. */
+       reason = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_DEBUGIRQ_REASON_REG);
+
+       switch (reason) {
+       case B43_DEBUGIRQ_PANIC:
+               b43_handle_firmware_panic(dev);
+               break;
+       case B43_DEBUGIRQ_DUMP_SHM:
+               if (!B43_DEBUG)
+                       break; /* Only with driver debugging enabled. */
+               buf = kmalloc(4096, GFP_ATOMIC);
+               if (!buf) {
+                       b43dbg(dev->wl, "SHM-dump: Failed to allocate memory\n");
+                       goto out;
+               }
+               for (i = 0; i < 4096; i += 2) {
+                       u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, i);
+                       buf[i / 2] = cpu_to_le16(tmp);
+               }
+               b43info(dev->wl, "Shared memory dump:\n");
+               print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET,
+                              16, 2, buf, 4096, 1);
+               kfree(buf);
+               break;
+       case B43_DEBUGIRQ_DUMP_REGS:
+               if (!B43_DEBUG)
+                       break; /* Only with driver debugging enabled. */
+               b43info(dev->wl, "Microcode register dump:\n");
+               for (i = 0, cnt = 0; i < 64; i++) {
+                       u16 tmp = b43_shm_read16(dev, B43_SHM_SCRATCH, i);
+                       if (cnt == 0)
+                               printk(KERN_INFO);
+                       printk("r%02u: 0x%04X  ", i, tmp);
+                       cnt++;
+                       if (cnt == 6) {
+                               printk("\n");
+                               cnt = 0;
+                       }
+               }
+               printk("\n");
+               break;
+       case B43_DEBUGIRQ_MARKER:
+               if (!B43_DEBUG)
+                       break; /* Only with driver debugging enabled. */
+               marker_id = b43_shm_read16(dev, B43_SHM_SCRATCH,
+                                          B43_MARKER_ID_REG);
+               marker_line = b43_shm_read16(dev, B43_SHM_SCRATCH,
+                                            B43_MARKER_LINE_REG);
+               b43info(dev->wl, "The firmware just executed the MARKER(%u) "
+                       "at line number %u\n",
+                       marker_id, marker_line);
+               break;
+       default:
+               b43dbg(dev->wl, "Debug-IRQ triggered for unknown reason: %u\n",
+                      reason);
+       }
+out:
+       /* Acknowledge the debug-IRQ, so the firmware can continue. */
+       b43_shm_write16(dev, B43_SHM_SCRATCH,
+                       B43_DEBUGIRQ_REASON_REG, B43_DEBUGIRQ_ACK);
 }
 
 /* Interrupt handler bottom-half */
@@ -1853,7 +2004,8 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
 
 static int do_request_fw(struct b43_wldev *dev,
                         const char *name,
-                        struct b43_firmware_file *fw)
+                        struct b43_firmware_file *fw,
+                        bool silent)
 {
        char path[sizeof(modparam_fwpostfix) + 32];
        const struct firmware *blob;
@@ -1877,9 +2029,15 @@ static int do_request_fw(struct b43_wldev *dev,
                 "b43%s/%s.fw",
                 modparam_fwpostfix, name);
        err = request_firmware(&blob, path, dev->dev->dev);
-       if (err) {
-               b43err(dev->wl, "Firmware file \"%s\" not found "
-                      "or load failed.\n", path);
+       if (err == -ENOENT) {
+               if (!silent) {
+                       b43err(dev->wl, "Firmware file \"%s\" not found\n",
+                              path);
+               }
+               return err;
+       } else if (err) {
+               b43err(dev->wl, "Firmware file \"%s\" request failed (err=%d)\n",
+                      path, err);
                return err;
        }
        if (blob->size < sizeof(struct b43_fw_header))
@@ -1930,7 +2088,7 @@ static int b43_request_firmware(struct b43_wldev *dev)
                filename = "ucode13";
        else
                goto err_no_ucode;
-       err = do_request_fw(dev, filename, &fw->ucode);
+       err = do_request_fw(dev, filename, &fw->ucode, 0);
        if (err)
                goto err_load;
 
@@ -1941,8 +2099,13 @@ static int b43_request_firmware(struct b43_wldev *dev)
                filename = NULL;
        else
                goto err_no_pcm;
-       err = do_request_fw(dev, filename, &fw->pcm);
-       if (err)
+       fw->pcm_request_failed = 0;
+       err = do_request_fw(dev, filename, &fw->pcm, 1);
+       if (err == -ENOENT) {
+               /* We did not find a PCM file? Not fatal, but
+                * core rev <= 10 must do without hwcrypto then. */
+               fw->pcm_request_failed = 1;
+       } else if (err)
                goto err_load;
 
        /* Get initvals */
@@ -1960,7 +2123,7 @@ static int b43_request_firmware(struct b43_wldev *dev)
                if ((rev >= 5) && (rev <= 10))
                        filename = "b0g0initvals5";
                else if (rev >= 13)
-                       filename = "lp0initvals13";
+                       filename = "b0g0initvals13";
                else
                        goto err_no_initvals;
                break;
@@ -1973,7 +2136,7 @@ static int b43_request_firmware(struct b43_wldev *dev)
        default:
                goto err_no_initvals;
        }
-       err = do_request_fw(dev, filename, &fw->initvals);
+       err = do_request_fw(dev, filename, &fw->initvals, 0);
        if (err)
                goto err_load;
 
@@ -2007,7 +2170,7 @@ static int b43_request_firmware(struct b43_wldev *dev)
        default:
                goto err_no_initvals;
        }
-       err = do_request_fw(dev, filename, &fw->initvals_band);
+       err = do_request_fw(dev, filename, &fw->initvals_band, 0);
        if (err)
                goto err_load;
 
@@ -2124,14 +2287,28 @@ static int b43_upload_microcode(struct b43_wldev *dev)
                err = -EOPNOTSUPP;
                goto error;
        }
-       b43info(dev->wl, "Loading firmware version %u.%u "
-               "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n",
-               fwrev, fwpatch,
-               (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF,
-               (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F);
-
        dev->fw.rev = fwrev;
        dev->fw.patch = fwpatch;
+       dev->fw.opensource = (fwdate == 0xFFFF);
+
+       if (dev->fw.opensource) {
+               /* Patchlevel info is encoded in the "time" field. */
+               dev->fw.patch = fwtime;
+               b43info(dev->wl, "Loading OpenSource firmware version %u.%u%s\n",
+                       dev->fw.rev, dev->fw.patch,
+                       dev->fw.pcm_request_failed ? " (Hardware crypto not supported)" : "");
+       } else {
+               b43info(dev->wl, "Loading firmware version %u.%u "
+                       "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n",
+                       fwrev, fwpatch,
+                       (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF,
+                       (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F);
+               if (dev->fw.pcm_request_failed) {
+                       b43warn(dev->wl, "No \"pcm5.fw\" firmware file found. "
+                               "Hardware accelerated cryptography is disabled.\n");
+                       b43_print_fw_helptext(dev->wl, 0);
+               }
+       }
 
        if (b43_is_old_txhdr_format(dev)) {
                b43warn(dev->wl, "You are using an old firmware image. "
@@ -2376,7 +2553,8 @@ static void b43_adjust_opmode(struct b43_wldev *dev)
        ctl &= ~B43_MACCTL_BEACPROMISC;
        ctl |= B43_MACCTL_INFRA;
 
-       if (b43_is_mode(wl, IEEE80211_IF_TYPE_AP))
+       if (b43_is_mode(wl, IEEE80211_IF_TYPE_AP) ||
+           b43_is_mode(wl, IEEE80211_IF_TYPE_MESH_POINT))
                ctl |= B43_MACCTL_AP;
        else if (b43_is_mode(wl, IEEE80211_IF_TYPE_IBSS))
                ctl &= ~B43_MACCTL_INFRA;
@@ -2629,6 +2807,21 @@ static void b43_periodic_every30sec(struct b43_wldev *dev)
 static void b43_periodic_every15sec(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
+       u16 wdr;
+
+       if (dev->fw.opensource) {
+               /* Check if the firmware is still alive.
+                * It will reset the watchdog counter to 0 in its idle loop. */
+               wdr = b43_shm_read16(dev, B43_SHM_SCRATCH, B43_WATCHDOG_REG);
+               if (unlikely(wdr)) {
+                       b43err(dev->wl, "Firmware watchdog: The firmware died!\n");
+                       b43_controller_restart(dev, "Firmware watchdog");
+                       return;
+               } else {
+                       b43_shm_write16(dev, B43_SHM_SCRATCH,
+                                       B43_WATCHDOG_REG, 1);
+               }
+       }
 
        if (phy->type == B43_PHYTYPE_G) {
                //TODO: update_aci_moving_average
@@ -2813,8 +3006,7 @@ static int b43_rng_init(struct b43_wl *wl)
 }
 
 static int b43_op_tx(struct ieee80211_hw *hw,
-                    struct sk_buff *skb,
-                    struct ieee80211_tx_control *ctl)
+                    struct sk_buff *skb)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
        struct b43_wldev *dev = wl->current_dev;
@@ -2836,9 +3028,9 @@ static int b43_op_tx(struct ieee80211_hw *hw,
        err = -ENODEV;
        if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
                if (b43_using_pio_transfers(dev))
-                       err = b43_pio_tx(dev, skb, ctl);
+                       err = b43_pio_tx(dev, skb);
                else
-                       err = b43_dma_tx(dev, skb, ctl);
+                       err = b43_dma_tx(dev, skb);
        }
 
        read_unlock_irqrestore(&wl->tx_lock, flags);
@@ -3244,8 +3436,9 @@ static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
        antenna = b43_antenna_from_ieee80211(dev, conf->antenna_sel_rx);
        b43_set_rx_antenna(dev, antenna);
 
-       /* Update templates for AP mode. */
-       if (b43_is_mode(wl, IEEE80211_IF_TYPE_AP))
+       /* Update templates for AP/mesh mode. */
+       if (b43_is_mode(wl, IEEE80211_IF_TYPE_AP) ||
+           b43_is_mode(wl, IEEE80211_IF_TYPE_MESH_POINT))
                b43_set_beacon_int(dev, conf->beacon_int);
 
        if (!!conf->radio_enabled != phy->radio_on) {
@@ -3296,6 +3489,13 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        if (!dev || b43_status(dev) < B43_STAT_INITIALIZED)
                goto out_unlock;
 
+       if (dev->fw.pcm_request_failed) {
+               /* We don't have firmware for the crypto engine.
+                * Must use software-crypto. */
+               err = -EOPNOTSUPP;
+               goto out_unlock;
+       }
+
        err = -EINVAL;
        switch (key->alg) {
        case ALG_WEP:
@@ -3426,13 +3626,12 @@ static int b43_op_config_interface(struct ieee80211_hw *hw,
        else
                memset(wl->bssid, 0, ETH_ALEN);
        if (b43_status(dev) >= B43_STAT_INITIALIZED) {
-               if (b43_is_mode(wl, IEEE80211_IF_TYPE_AP)) {
-                       B43_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP);
+               if (b43_is_mode(wl, IEEE80211_IF_TYPE_AP) ||
+                   b43_is_mode(wl, IEEE80211_IF_TYPE_MESH_POINT)) {
+                       B43_WARN_ON(conf->type != wl->if_type);
                        b43_set_ssid(dev, conf->ssid, conf->ssid_len);
-                       if (conf->beacon) {
-                               b43_update_templates(wl, conf->beacon,
-                                                    conf->beacon_control);
-                       }
+                       if (conf->beacon)
+                               b43_update_templates(wl, conf->beacon);
                }
                b43_write_mac_bssid_templates(dev);
        }
@@ -3969,6 +4168,7 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
        /* TODO: allow WDS/AP devices to coexist */
 
        if (conf->type != IEEE80211_IF_TYPE_AP &&
+           conf->type != IEEE80211_IF_TYPE_MESH_POINT &&
            conf->type != IEEE80211_IF_TYPE_STA &&
            conf->type != IEEE80211_IF_TYPE_WDS &&
            conf->type != IEEE80211_IF_TYPE_IBSS)
@@ -4042,6 +4242,9 @@ static int b43_op_start(struct ieee80211_hw *hw)
        wl->filter_flags = 0;
        wl->radiotap_enabled = 0;
        b43_qos_clear(wl);
+       wl->beacon0_uploaded = 0;
+       wl->beacon1_uploaded = 0;
+       wl->beacon_templates_virgin = 1;
 
        /* First register RFkill.
         * LEDs that are registered later depend on it. */
@@ -4118,31 +4321,29 @@ 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;
-       struct ieee80211_tx_control txctl;
 
        /* 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, &txctl);
+       beacon = ieee80211_beacon_get(hw, wl->vif);
        if (unlikely(!beacon))
                return -ENOMEM;
        spin_lock_irqsave(&wl->irq_lock, flags);
-       b43_update_templates(wl, beacon, &txctl);
+       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 sk_buff *beacon)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
        unsigned long flags;
 
        spin_lock_irqsave(&wl->irq_lock, flags);
-       b43_update_templates(wl, beacon, ctl);
+       b43_update_templates(wl, beacon);
        spin_unlock_irqrestore(&wl->irq_lock, flags);
 
        return 0;
@@ -4210,7 +4411,9 @@ static void b43_chip_reset(struct work_struct *work)
                        goto out;
                }
        }
-      out:
+out:
+       if (err)
+               wl->current_dev = NULL; /* Failed to init the dev. */
        mutex_unlock(&wl->mutex);
        if (err)
                b43err(wl, "Controller restart FAILED\n");
@@ -4351,9 +4554,11 @@ static void b43_one_core_detach(struct ssb_device *dev)
        struct b43_wldev *wldev;
        struct b43_wl *wl;
 
+       /* Do not cancel ieee80211-workqueue based work here.
+        * See comment in b43_remove(). */
+
        wldev = ssb_get_drvdata(dev);
        wl = wldev->wl;
-       cancel_work_sync(&wldev->restart_work);
        b43_debugfs_remove_device(wldev);
        b43_wireless_core_detach(wldev);
        list_del(&wldev->list);
@@ -4538,6 +4743,10 @@ static void b43_remove(struct ssb_device *dev)
        struct b43_wl *wl = ssb_get_devtypedata(dev);
        struct b43_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);
+
        B43_WARN_ON(!wl);
        if (wl->current_dev == wldev)
                ieee80211_unregister_hw(wl->hw);