Update b43 from compat-wireless-2008-05-26 codebase
[openwrt/svn-archive/archive.git] / package / b43 / src / main.c
index afb109447a45d4e7341d413a1a381277b781d0c4..cbb317bb34840e91532c19ac6ba27cf700574458 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>
 
@@ -45,7 +46,9 @@
 #include "main.h"
 #include "debugfs.h"
 #include "phy.h"
+#include "nphy.h"
 #include "dma.h"
+#include "pio.h"
 #include "sysfs.h"
 #include "xmit.h"
 #include "lo.h"
@@ -57,6 +60,8 @@ MODULE_AUTHOR("Stefano Brivio");
 MODULE_AUTHOR("Michael Buesch");
 MODULE_LICENSE("GPL");
 
+MODULE_FIRMWARE(B43_SUPPORTED_FIRMWARE_ID);
+
 
 static int modparam_bad_frames_preempt;
 module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444);
@@ -75,6 +80,15 @@ static int modparam_nohwcrypt;
 module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
 MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
 
+int b43_modparam_qos = 1;
+module_param_named(qos, b43_modparam_qos, int, 0444);
+MODULE_PARM_DESC(qos, "Enable QOS support (default on)");
+
+static int modparam_btcoex = 1;
+module_param_named(btcoex, modparam_btcoex, int, 0444);
+MODULE_PARM_DESC(btcoex, "Enable Bluetooth coexistance (default on)");
+
+
 static const struct ssb_device_id b43_ssb_tbl[] = {
        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 5),
        SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 6),
@@ -125,58 +139,143 @@ static struct ieee80211_rate __b43_ratetable[] = {
 #define b43_g_ratetable                (__b43_ratetable + 0)
 #define b43_g_ratetable_size   12
 
-#define CHANTAB_ENT(_chanid, _freq) \
-       {                                                       \
-               .center_freq    = (_freq),                      \
-               .hw_value       = (_chanid),                    \
-       }
+#define CHAN4G(_channel, _freq, _flags) {                      \
+       .band                   = IEEE80211_BAND_2GHZ,          \
+       .center_freq            = (_freq),                      \
+       .hw_value               = (_channel),                   \
+       .flags                  = (_flags),                     \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
 static struct ieee80211_channel b43_2ghz_chantable[] = {
-       CHANTAB_ENT(1, 2412),
-       CHANTAB_ENT(2, 2417),
-       CHANTAB_ENT(3, 2422),
-       CHANTAB_ENT(4, 2427),
-       CHANTAB_ENT(5, 2432),
-       CHANTAB_ENT(6, 2437),
-       CHANTAB_ENT(7, 2442),
-       CHANTAB_ENT(8, 2447),
-       CHANTAB_ENT(9, 2452),
-       CHANTAB_ENT(10, 2457),
-       CHANTAB_ENT(11, 2462),
-       CHANTAB_ENT(12, 2467),
-       CHANTAB_ENT(13, 2472),
-       CHANTAB_ENT(14, 2484),
+       CHAN4G(1, 2412, 0),
+       CHAN4G(2, 2417, 0),
+       CHAN4G(3, 2422, 0),
+       CHAN4G(4, 2427, 0),
+       CHAN4G(5, 2432, 0),
+       CHAN4G(6, 2437, 0),
+       CHAN4G(7, 2442, 0),
+       CHAN4G(8, 2447, 0),
+       CHAN4G(9, 2452, 0),
+       CHAN4G(10, 2457, 0),
+       CHAN4G(11, 2462, 0),
+       CHAN4G(12, 2467, 0),
+       CHAN4G(13, 2472, 0),
+       CHAN4G(14, 2484, 0),
+};
+#undef CHAN4G
+
+#define CHAN5G(_channel, _flags) {                             \
+       .band                   = IEEE80211_BAND_5GHZ,          \
+       .center_freq            = 5000 + (5 * (_channel)),      \
+       .hw_value               = (_channel),                   \
+       .flags                  = (_flags),                     \
+       .max_antenna_gain       = 0,                            \
+       .max_power              = 30,                           \
+}
+static struct ieee80211_channel b43_5ghz_nphy_chantable[] = {
+       CHAN5G(32, 0),          CHAN5G(34, 0),
+       CHAN5G(36, 0),          CHAN5G(38, 0),
+       CHAN5G(40, 0),          CHAN5G(42, 0),
+       CHAN5G(44, 0),          CHAN5G(46, 0),
+       CHAN5G(48, 0),          CHAN5G(50, 0),
+       CHAN5G(52, 0),          CHAN5G(54, 0),
+       CHAN5G(56, 0),          CHAN5G(58, 0),
+       CHAN5G(60, 0),          CHAN5G(62, 0),
+       CHAN5G(64, 0),          CHAN5G(66, 0),
+       CHAN5G(68, 0),          CHAN5G(70, 0),
+       CHAN5G(72, 0),          CHAN5G(74, 0),
+       CHAN5G(76, 0),          CHAN5G(78, 0),
+       CHAN5G(80, 0),          CHAN5G(82, 0),
+       CHAN5G(84, 0),          CHAN5G(86, 0),
+       CHAN5G(88, 0),          CHAN5G(90, 0),
+       CHAN5G(92, 0),          CHAN5G(94, 0),
+       CHAN5G(96, 0),          CHAN5G(98, 0),
+       CHAN5G(100, 0),         CHAN5G(102, 0),
+       CHAN5G(104, 0),         CHAN5G(106, 0),
+       CHAN5G(108, 0),         CHAN5G(110, 0),
+       CHAN5G(112, 0),         CHAN5G(114, 0),
+       CHAN5G(116, 0),         CHAN5G(118, 0),
+       CHAN5G(120, 0),         CHAN5G(122, 0),
+       CHAN5G(124, 0),         CHAN5G(126, 0),
+       CHAN5G(128, 0),         CHAN5G(130, 0),
+       CHAN5G(132, 0),         CHAN5G(134, 0),
+       CHAN5G(136, 0),         CHAN5G(138, 0),
+       CHAN5G(140, 0),         CHAN5G(142, 0),
+       CHAN5G(144, 0),         CHAN5G(145, 0),
+       CHAN5G(146, 0),         CHAN5G(147, 0),
+       CHAN5G(148, 0),         CHAN5G(149, 0),
+       CHAN5G(150, 0),         CHAN5G(151, 0),
+       CHAN5G(152, 0),         CHAN5G(153, 0),
+       CHAN5G(154, 0),         CHAN5G(155, 0),
+       CHAN5G(156, 0),         CHAN5G(157, 0),
+       CHAN5G(158, 0),         CHAN5G(159, 0),
+       CHAN5G(160, 0),         CHAN5G(161, 0),
+       CHAN5G(162, 0),         CHAN5G(163, 0),
+       CHAN5G(164, 0),         CHAN5G(165, 0),
+       CHAN5G(166, 0),         CHAN5G(168, 0),
+       CHAN5G(170, 0),         CHAN5G(172, 0),
+       CHAN5G(174, 0),         CHAN5G(176, 0),
+       CHAN5G(178, 0),         CHAN5G(180, 0),
+       CHAN5G(182, 0),         CHAN5G(184, 0),
+       CHAN5G(186, 0),         CHAN5G(188, 0),
+       CHAN5G(190, 0),         CHAN5G(192, 0),
+       CHAN5G(194, 0),         CHAN5G(196, 0),
+       CHAN5G(198, 0),         CHAN5G(200, 0),
+       CHAN5G(202, 0),         CHAN5G(204, 0),
+       CHAN5G(206, 0),         CHAN5G(208, 0),
+       CHAN5G(210, 0),         CHAN5G(212, 0),
+       CHAN5G(214, 0),         CHAN5G(216, 0),
+       CHAN5G(218, 0),         CHAN5G(220, 0),
+       CHAN5G(222, 0),         CHAN5G(224, 0),
+       CHAN5G(226, 0),         CHAN5G(228, 0),
 };
 
-#ifdef NOTYET
-static struct ieee80211_channel b43_5ghz_chantable[] = {
-       CHANTAB_ENT(36, 5180),
-       CHANTAB_ENT(40, 5200),
-       CHANTAB_ENT(44, 5220),
-       CHANTAB_ENT(48, 5240),
-       CHANTAB_ENT(52, 5260),
-       CHANTAB_ENT(56, 5280),
-       CHANTAB_ENT(60, 5300),
-       CHANTAB_ENT(64, 5320),
-       CHANTAB_ENT(149, 5745),
-       CHANTAB_ENT(153, 5765),
-       CHANTAB_ENT(157, 5785),
-       CHANTAB_ENT(161, 5805),
-       CHANTAB_ENT(165, 5825),
+static struct ieee80211_channel b43_5ghz_aphy_chantable[] = {
+       CHAN5G(34, 0),          CHAN5G(36, 0),
+       CHAN5G(38, 0),          CHAN5G(40, 0),
+       CHAN5G(42, 0),          CHAN5G(44, 0),
+       CHAN5G(46, 0),          CHAN5G(48, 0),
+       CHAN5G(52, 0),          CHAN5G(56, 0),
+       CHAN5G(60, 0),          CHAN5G(64, 0),
+       CHAN5G(100, 0),         CHAN5G(104, 0),
+       CHAN5G(108, 0),         CHAN5G(112, 0),
+       CHAN5G(116, 0),         CHAN5G(120, 0),
+       CHAN5G(124, 0),         CHAN5G(128, 0),
+       CHAN5G(132, 0),         CHAN5G(136, 0),
+       CHAN5G(140, 0),         CHAN5G(149, 0),
+       CHAN5G(153, 0),         CHAN5G(157, 0),
+       CHAN5G(161, 0),         CHAN5G(165, 0),
+       CHAN5G(184, 0),         CHAN5G(188, 0),
+       CHAN5G(192, 0),         CHAN5G(196, 0),
+       CHAN5G(200, 0),         CHAN5G(204, 0),
+       CHAN5G(208, 0),         CHAN5G(212, 0),
+       CHAN5G(216, 0),
+};
+#undef CHAN5G
+
+static struct ieee80211_supported_band b43_band_5GHz_nphy = {
+       .band           = IEEE80211_BAND_5GHZ,
+       .channels       = b43_5ghz_nphy_chantable,
+       .n_channels     = ARRAY_SIZE(b43_5ghz_nphy_chantable),
+       .bitrates       = b43_a_ratetable,
+       .n_bitrates     = b43_a_ratetable_size,
 };
 
-static struct ieee80211_supported_band b43_band_5GHz = {
-       .channels = b43_5ghz_chantable,
-       .n_channels = ARRAY_SIZE(b43_5ghz_chantable),
-       .bitrates = b43_a_ratetable,
-       .n_bitrates = b43_a_ratetable_size,
+static struct ieee80211_supported_band b43_band_5GHz_aphy = {
+       .band           = IEEE80211_BAND_5GHZ,
+       .channels       = b43_5ghz_aphy_chantable,
+       .n_channels     = ARRAY_SIZE(b43_5ghz_aphy_chantable),
+       .bitrates       = b43_a_ratetable,
+       .n_bitrates     = b43_a_ratetable_size,
 };
-#endif
 
 static struct ieee80211_supported_band b43_band_2GHz = {
-       .channels = b43_2ghz_chantable,
-       .n_channels = ARRAY_SIZE(b43_2ghz_chantable),
-       .bitrates = b43_g_ratetable,
-       .n_bitrates = b43_g_ratetable_size,
+       .band           = IEEE80211_BAND_2GHZ,
+       .channels       = b43_2ghz_chantable,
+       .n_channels     = ARRAY_SIZE(b43_2ghz_chantable),
+       .bitrates       = b43_g_ratetable,
+       .n_bitrates     = b43_g_ratetable_size,
 };
 
 static void b43_wireless_core_exit(struct b43_wldev *dev);
@@ -377,24 +476,30 @@ out:
 }
 
 /* Read HostFlags */
-u32 b43_hf_read(struct b43_wldev * dev)
+u64 b43_hf_read(struct b43_wldev * dev)
 {
-       u32 ret;
+       u64 ret;
 
        ret = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFHI);
        ret <<= 16;
+       ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFMI);
+       ret <<= 16;
        ret |= b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFLO);
 
        return ret;
 }
 
 /* Write HostFlags */
-void b43_hf_write(struct b43_wldev *dev, u32 value)
+void b43_hf_write(struct b43_wldev *dev, u64 value)
 {
-       b43_shm_write16(dev, B43_SHM_SHARED,
-                       B43_SHM_SH_HOSTFLO, (value & 0x0000FFFF));
-       b43_shm_write16(dev, B43_SHM_SHARED,
-                       B43_SHM_SH_HOSTFHI, ((value & 0xFFFF0000) >> 16));
+       u16 lo, mi, hi;
+
+       lo = (value & 0x00000000FFFFULL);
+       mi = (value & 0x0000FFFF0000ULL) >> 16;
+       hi = (value & 0xFFFF00000000ULL) >> 32;
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFLO, lo);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFMI, mi);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFHI, hi);
 }
 
 void b43_tsf_read(struct b43_wldev *dev, u64 * tsf)
@@ -624,6 +729,7 @@ static void b43_synchronize_irq(struct b43_wldev *dev)
  */
 void b43_dummy_transmission(struct b43_wldev *dev)
 {
+       struct b43_wl *wl = dev->wl;
        struct b43_phy *phy = &dev->phy;
        unsigned int i, max_loop;
        u16 value;
@@ -650,6 +756,9 @@ void b43_dummy_transmission(struct b43_wldev *dev)
                return;
        }
 
+       spin_lock_irq(&wl->irq_lock);
+       write_lock(&wl->tx_lock);
+
        for (i = 0; i < 5; i++)
                b43_ram_write(dev, i * 4, buffer[i]);
 
@@ -690,6 +799,9 @@ void b43_dummy_transmission(struct b43_wldev *dev)
        }
        if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5)
                b43_radio_write16(dev, 0x0051, 0x0037);
+
+       write_unlock(&wl->tx_lock);
+       spin_unlock_irq(&wl->irq_lock);
 }
 
 static void key_write(struct b43_wldev *dev,
@@ -919,7 +1031,18 @@ void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags)
 /* Turn the Analog ON/OFF */
 static void b43_switch_analog(struct b43_wldev *dev, int on)
 {
-       b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4);
+       switch (dev->phy.type) {
+       case B43_PHYTYPE_A:
+       case B43_PHYTYPE_G:
+               b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4);
+               break;
+       case B43_PHYTYPE_N:
+               b43_phy_write(dev, B43_NPHY_AFECTL_OVER,
+                             on ? 0 : 0x7FFF);
+               break;
+       default:
+               B43_WARN_ON(1);
+       }
 }
 
 void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags)
@@ -1059,10 +1182,10 @@ static void handle_irq_noise(struct b43_wldev *dev)
        /* Get the noise samples. */
        B43_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]];
@@ -1169,22 +1292,107 @@ static void b43_write_template_common(struct b43_wldev *dev,
                        size + sizeof(struct b43_plcp_hdr6));
 }
 
+/* Check if the use of the antenna that ieee80211 told us to
+ * use is possible. This will fall back to DEFAULT.
+ * "antenna_nr" is the antenna identifier we got from ieee80211. */
+u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev,
+                                 u8 antenna_nr)
+{
+       u8 antenna_mask;
+
+       if (antenna_nr == 0) {
+               /* Zero means "use default antenna". That's always OK. */
+               return 0;
+       }
+
+       /* Get the mask of available antennas. */
+       if (dev->phy.gmode)
+               antenna_mask = dev->dev->bus->sprom.ant_available_bg;
+       else
+               antenna_mask = dev->dev->bus->sprom.ant_available_a;
+
+       if (!(antenna_mask & (1 << (antenna_nr - 1)))) {
+               /* This antenna is not available. Fall back to default. */
+               return 0;
+       }
+
+       return antenna_nr;
+}
+
+static int b43_antenna_from_ieee80211(struct b43_wldev *dev, u8 antenna)
+{
+       antenna = b43_ieee80211_antenna_sanitize(dev, antenna);
+       switch (antenna) {
+       case 0:         /* default/diversity */
+               return B43_ANTENNA_DEFAULT;
+       case 1:         /* Antenna 0 */
+               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;
+       }
+}
+
+/* Convert a b43 antenna number value to the PHY TX control value. */
+static u16 b43_antenna_to_phyctl(int antenna)
+{
+       switch (antenna) {
+       case B43_ANTENNA0:
+               return B43_TXH_PHY_ANT0;
+       case B43_ANTENNA1:
+               return B43_TXH_PHY_ANT1;
+       case B43_ANTENNA2:
+               return B43_TXH_PHY_ANT2;
+       case B43_ANTENNA3:
+               return B43_TXH_PHY_ANT3;
+       case B43_ANTENNA_AUTO:
+               return B43_TXH_PHY_ANT01AUTO;
+       }
+       B43_WARN_ON(1);
+       return 0;
+}
+
 static void b43_write_beacon_template(struct b43_wldev *dev,
                                      u16 ram_offset,
-                                     u16 shm_size_offset, u8 rate)
+                                     u16 shm_size_offset)
 {
        unsigned int i, len, variable_len;
        const struct ieee80211_mgmt *bcn;
        const u8 *ie;
        bool tim_found = 0;
+       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 = 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, 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. */
+       ctl &= ~B43_TXH_PHY_SHORTPRMBL;
+       ctl &= ~B43_TXH_PHY_ANT;
+       ctl &= ~B43_TXH_PHY_ENC;
+       ctl |= antenna;
+       if (b43_is_cck_rate(rate))
+               ctl |= B43_TXH_PHY_ENC_CCK;
+       else
+               ctl |= B43_TXH_PHY_ENC_OFDM;
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl);
+
        /* Find the position of the TIM and the DTIM_period value
         * and write them to SHM. */
        ie = bcn->u.beacon.variable;
@@ -1225,7 +1433,8 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
                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");
 }
 
 static void b43_write_probe_resp_plcp(struct b43_wldev *dev,
@@ -1335,6 +1544,73 @@ static void b43_write_probe_resp_template(struct b43_wldev *dev,
        kfree(probe_resp_data);
 }
 
+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))
+               return;
+
+       /* This is the bottom half of the asynchronous beacon update. */
+
+       /* Ignore interrupt in the future. */
+       dev->irq_savedstate &= ~B43_IRQ_BEACON;
+
+       cmd = b43_read32(dev, B43_MMIO_MACCMD);
+       beacon0_valid = (cmd & B43_MACCMD_BEACON0_VALID);
+       beacon1_valid = (cmd & B43_MACCMD_BEACON1_VALID);
+
+       /* Schedule interrupt manually, if busy. */
+       if (beacon0_valid && beacon1_valid) {
+               b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_BEACON);
+               dev->irq_savedstate |= B43_IRQ_BEACON;
+               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;
+               }
+               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;
+               }
+               cmd = b43_read32(dev, B43_MMIO_MACCMD);
+               cmd |= B43_MACCMD_BEACON1_VALID;
+               b43_write32(dev, B43_MMIO_MACCMD, cmd);
+       }
+}
+
+static void b43_beacon_update_trigger_work(struct work_struct *work)
+{
+       struct b43_wl *wl = container_of(work, struct b43_wl,
+                                        beacon_update_trigger);
+       struct b43_wldev *dev;
+
+       mutex_lock(&wl->mutex);
+       dev = wl->current_dev;
+       if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) {
+               spin_lock_irq(&wl->irq_lock);
+               /* update beacon right away or defer to irq */
+               dev->irq_savedstate = b43_read32(dev, B43_MMIO_GEN_IRQ_MASK);
+               handle_irq_beacon(dev);
+               /* The handler might have updated the IRQ mask. */
+               b43_write32(dev, B43_MMIO_GEN_IRQ_MASK,
+                           dev->irq_savedstate);
+               mmiowb();
+               spin_unlock_irq(&wl->irq_lock);
+       }
+       mutex_unlock(&wl->mutex);
+}
+
 /* 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)
@@ -1350,6 +1626,7 @@ static void b43_update_templates(struct b43_wl *wl, struct sk_buff *beacon)
        wl->current_beacon = beacon;
        wl->beacon0_uploaded = 0;
        wl->beacon1_uploaded = 0;
+       queue_work(wl->hw->workqueue, &wl->beacon_update_trigger);
 }
 
 static void b43_set_ssid(struct b43_wldev *dev, const u8 * ssid, u8 ssid_len)
@@ -1375,49 +1652,110 @@ static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int)
 {
        b43_time_lock(dev);
        if (dev->dev->id.revision >= 3) {
-               b43_write32(dev, 0x188, (beacon_int << 16));
+               b43_write32(dev, B43_MMIO_TSF_CFP_REP, (beacon_int << 16));
+               b43_write32(dev, B43_MMIO_TSF_CFP_START, (beacon_int << 10));
        } else {
                b43_write16(dev, 0x606, (beacon_int >> 6));
                b43_write16(dev, 0x610, beacon_int);
        }
        b43_time_unlock(dev);
+       b43dbg(dev->wl, "Set beacon interval to %u\n", beacon_int);
 }
 
-static void handle_irq_beacon(struct b43_wldev *dev)
+static void b43_handle_firmware_panic(struct b43_wldev *dev)
 {
-       struct b43_wl *wl = dev->wl;
-       u32 cmd;
+       u16 reason;
 
-       if (!b43_is_mode(wl, IEEE80211_IF_TYPE_AP))
-               return;
+       /* 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);
 
-       /* This is the bottom half of the asynchronous beacon update. */
-
-       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_ratetable[3]);
-                       wl->beacon0_uploaded = 1;
-               }
-               cmd |= B43_MACCMD_BEACON0_VALID;
-       }
-       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;
+       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;
        }
-       b43_write32(dev, B43_MMIO_MACCMD, cmd);
 }
 
 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 */
@@ -1494,12 +1832,15 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev)
                handle_irq_noise(dev);
 
        /* Check the DMA reason registers for received data. */
-       if (dma_reason[0] & B43_DMAIRQ_RX_DONE)
-               b43_dma_rx(dev->dma.rx_ring0);
-       if (dma_reason[3] & B43_DMAIRQ_RX_DONE)
-               b43_dma_rx(dev->dma.rx_ring3);
+       if (dma_reason[0] & B43_DMAIRQ_RX_DONE) {
+               if (b43_using_pio_transfers(dev))
+                       b43_pio_rx(dev->pio.rx_queue);
+               else
+                       b43_dma_rx(dev->dma.rx_ring);
+       }
        B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE);
        B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE);
+       B43_WARN_ON(dma_reason[3] & B43_DMAIRQ_RX_DONE);
        B43_WARN_ON(dma_reason[4] & B43_DMAIRQ_RX_DONE);
        B43_WARN_ON(dma_reason[5] & B43_DMAIRQ_RX_DONE);
 
@@ -1601,7 +1942,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;
@@ -1625,9 +1967,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))
@@ -1678,7 +2026,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;
 
@@ -1689,8 +2037,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 */
@@ -1708,7 +2061,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;
@@ -1721,7 +2074,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;
 
@@ -1755,7 +2108,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;
 
@@ -1872,14 +2225,28 @@ static int b43_upload_microcode(struct b43_wldev *dev)
                err = -EOPNOTSUPP;
                goto error;
        }
-       b43dbg(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. "
@@ -1926,7 +2293,7 @@ static int b43_write_initvals(struct b43_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);
                        b43_write32(dev, offset, value);
 
                        iv = (const struct b43_iv *)((const uint8_t *)iv +
@@ -2060,7 +2427,6 @@ void b43_mac_enable(struct b43_wldev *dev)
 {
        dev->mac_suspended--;
        B43_WARN_ON(dev->mac_suspended < 0);
-       B43_WARN_ON(irqs_disabled());
        if (dev->mac_suspended == 0) {
                b43_write32(dev, B43_MMIO_MACCTL,
                            b43_read32(dev, B43_MMIO_MACCTL)
@@ -2071,11 +2437,6 @@ void b43_mac_enable(struct b43_wldev *dev)
                b43_read32(dev, B43_MMIO_MACCTL);
                b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);
                b43_power_saving_ctl_bits(dev, 0);
-
-               /* Re-enable IRQs. */
-               spin_lock_irq(&dev->wl->irq_lock);
-               b43_interrupt_enable(dev, dev->irq_savedstate);
-               spin_unlock_irq(&dev->wl->irq_lock);
        }
 }
 
@@ -2086,24 +2447,22 @@ void b43_mac_suspend(struct b43_wldev *dev)
        u32 tmp;
 
        might_sleep();
-       B43_WARN_ON(irqs_disabled());
        B43_WARN_ON(dev->mac_suspended < 0);
 
        if (dev->mac_suspended == 0) {
-               /* Mask IRQs before suspending MAC. Otherwise
-                * the MAC stays busy and won't suspend. */
-               spin_lock_irq(&dev->wl->irq_lock);
-               tmp = b43_interrupt_disable(dev, B43_IRQ_ALL);
-               spin_unlock_irq(&dev->wl->irq_lock);
-               b43_synchronize_irq(dev);
-               dev->irq_savedstate = tmp;
-
                b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
                b43_write32(dev, B43_MMIO_MACCTL,
                            b43_read32(dev, B43_MMIO_MACCTL)
                            & ~B43_MACCTL_ENABLED);
                /* force pci to flush the write */
                b43_read32(dev, B43_MMIO_MACCTL);
+               for (i = 35; i; i--) {
+                       tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);
+                       if (tmp & B43_IRQ_MAC_SUSPENDED)
+                               goto out;
+                       udelay(10);
+               }
+               /* Hm, it seems this will take some time. Use msleep(). */
                for (i = 40; i; i--) {
                        tmp = b43_read32(dev, B43_MMIO_GEN_IRQ_REASON);
                        if (tmp & B43_IRQ_MAC_SUSPENDED)
@@ -2209,38 +2568,28 @@ static void b43_rate_memory_init(struct b43_wldev *dev)
        }
 }
 
+/* Set the default values for the PHY TX Control Words. */
+static void b43_set_phytxctl_defaults(struct b43_wldev *dev)
+{
+       u16 ctl = 0;
+
+       ctl |= B43_TXH_PHY_ENC_CCK;
+       ctl |= B43_TXH_PHY_ANT01AUTO;
+       ctl |= B43_TXH_PHY_TXPWR;
+
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL, ctl);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_ACKCTSPHYCTL, ctl);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRPHYCTL, ctl);
+}
+
 /* Set the TX-Antenna for management frames sent by firmware. */
 static void b43_mgmtframe_txantenna(struct b43_wldev *dev, int antenna)
 {
-       u16 ant = 0;
+       u16 ant;
        u16 tmp;
 
-       switch (antenna) {
-       case B43_ANTENNA0:
-               ant |= B43_TXH_PHY_ANT0;
-               break;
-       case B43_ANTENNA1:
-               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_TXH_PHY_ANT01AUTO;
-               break;
-       default:
-               B43_WARN_ON(1);
-       }
-
-       /* FIXME We also need to set the other flags of the PHY control field somewhere. */
+       ant = b43_antenna_to_phyctl(antenna);
 
-       /* For Beacons */
-       tmp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_BEACPHYCTL);
-       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_TXH_PHY_ANT) | ant;
@@ -2256,6 +2605,7 @@ static void b43_chip_exit(struct b43_wldev *dev)
 {
        b43_radio_turn_off(dev, 1);
        b43_gpio_cleanup(dev);
+       b43_lo_g_cleanup(dev);
        /* firmware is released later */
 }
 
@@ -2362,28 +2712,12 @@ err_gpio_clean:
        return err;
 }
 
-static void b43_periodic_every120sec(struct b43_wldev *dev)
-{
-       struct b43_phy *phy = &dev->phy;
-
-       if (phy->type != B43_PHYTYPE_G || phy->rev < 2)
-               return;
-
-       b43_mac_suspend(dev);
-       b43_lo_g_measure(dev);
-       b43_mac_enable(dev);
-       if (b43_has_hardware_pctl(phy))
-               b43_lo_g_ctl_mark_all_unused(dev);
-}
-
 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) {
                b43_mac_suspend(dev);
                b43_calc_nrssi_slope(dev);
@@ -2435,6 +2769,7 @@ static void b43_periodic_every15sec(struct b43_wldev *dev)
                }
        }
        b43_phy_xmitpower(dev); //FIXME: unless scanning?
+       b43_lo_g_maintanance_work(dev);
        //TODO for APHY (temperature?)
 
        atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT);
@@ -2446,8 +2781,6 @@ static void do_periodic_work(struct b43_wldev *dev)
        unsigned int state;
 
        state = dev->periodic_state;
-       if (state % 8 == 0)
-               b43_periodic_every120sec(dev);
        if (state % 4 == 0)
                b43_periodic_every60sec(dev);
        if (state % 2 == 0)
@@ -2595,29 +2928,211 @@ 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;
-       int err = -ENODEV;
+       unsigned long flags;
+       int err;
 
+       if (unlikely(skb->len < 2 + 2 + 6)) {
+               /* Too short, this can't be a valid frame. */
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
+       B43_WARN_ON(skb_shinfo(skb)->nr_frags);
        if (unlikely(!dev))
-               goto out;
-       if (unlikely(b43_status(dev) < B43_STAT_STARTED))
-               goto out;
-       /* DMA-TX is done without a global lock. */
-       err = b43_dma_tx(dev, skb, ctl);
-out:
+               return NETDEV_TX_BUSY;
+
+       /* Transmissions on seperate queues can run concurrently. */
+       read_lock_irqsave(&wl->tx_lock, flags);
+
+       err = -ENODEV;
+       if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
+               if (b43_using_pio_transfers(dev))
+                       err = b43_pio_tx(dev, skb);
+               else
+                       err = b43_dma_tx(dev, skb);
+       }
+
+       read_unlock_irqrestore(&wl->tx_lock, flags);
+
        if (unlikely(err))
                return NETDEV_TX_BUSY;
        return NETDEV_TX_OK;
 }
 
-static int b43_op_conf_tx(struct ieee80211_hw *hw,
-                         int queue,
+/* Locking: wl->irq_lock */
+static void b43_qos_params_upload(struct b43_wldev *dev,
+                                 const struct ieee80211_tx_queue_params *p,
+                                 u16 shm_offset)
+{
+       u16 params[B43_NR_QOSPARAMS];
+       int cw_min, cw_max, aifs, bslots, tmp;
+       unsigned int i;
+
+       const u16 aCWmin = 0x0001;
+       const u16 aCWmax = 0x03FF;
+
+       /* Calculate the default values for the parameters, if needed. */
+       switch (shm_offset) {
+       case B43_QOS_VOICE:
+               aifs = (p->aifs == -1) ? 2 : p->aifs;
+               cw_min = (p->cw_min == 0) ? ((aCWmin + 1) / 4 - 1) : p->cw_min;
+               cw_max = (p->cw_max == 0) ? ((aCWmin + 1) / 2 - 1) : p->cw_max;
+               break;
+       case B43_QOS_VIDEO:
+               aifs = (p->aifs == -1) ? 2 : p->aifs;
+               cw_min = (p->cw_min == 0) ? ((aCWmin + 1) / 2 - 1) : p->cw_min;
+               cw_max = (p->cw_max == 0) ? aCWmin : p->cw_max;
+               break;
+       case B43_QOS_BESTEFFORT:
+               aifs = (p->aifs == -1) ? 3 : p->aifs;
+               cw_min = (p->cw_min == 0) ? aCWmin : p->cw_min;
+               cw_max = (p->cw_max == 0) ? aCWmax : p->cw_max;
+               break;
+       case B43_QOS_BACKGROUND:
+               aifs = (p->aifs == -1) ? 7 : p->aifs;
+               cw_min = (p->cw_min == 0) ? aCWmin : p->cw_min;
+               cw_max = (p->cw_max == 0) ? aCWmax : p->cw_max;
+               break;
+       default:
+               B43_WARN_ON(1);
+               return;
+       }
+       if (cw_min <= 0)
+               cw_min = aCWmin;
+       if (cw_max <= 0)
+               cw_max = aCWmin;
+       bslots = b43_read16(dev, B43_MMIO_RNG) % cw_min;
+
+       memset(&params, 0, sizeof(params));
+
+       params[B43_QOSPARAM_TXOP] = p->txop * 32;
+       params[B43_QOSPARAM_CWMIN] = cw_min;
+       params[B43_QOSPARAM_CWMAX] = cw_max;
+       params[B43_QOSPARAM_CWCUR] = cw_min;
+       params[B43_QOSPARAM_AIFS] = aifs;
+       params[B43_QOSPARAM_BSLOTS] = bslots;
+       params[B43_QOSPARAM_REGGAP] = bslots + aifs;
+
+       for (i = 0; i < ARRAY_SIZE(params); i++) {
+               if (i == B43_QOSPARAM_STATUS) {
+                       tmp = b43_shm_read16(dev, B43_SHM_SHARED,
+                                            shm_offset + (i * 2));
+                       /* Mark the parameters as updated. */
+                       tmp |= 0x100;
+                       b43_shm_write16(dev, B43_SHM_SHARED,
+                                       shm_offset + (i * 2),
+                                       tmp);
+               } else {
+                       b43_shm_write16(dev, B43_SHM_SHARED,
+                                       shm_offset + (i * 2),
+                                       params[i]);
+               }
+       }
+}
+
+/* Update the QOS parameters in hardware. */
+static void b43_qos_update(struct b43_wldev *dev)
+{
+       struct b43_wl *wl = dev->wl;
+       struct b43_qos_params *params;
+       unsigned long flags;
+       unsigned int i;
+
+       /* Mapping of mac80211 queues to b43 SHM offsets. */
+       static const u16 qos_shm_offsets[] = {
+               [0] = B43_QOS_VOICE,
+               [1] = B43_QOS_VIDEO,
+               [2] = B43_QOS_BESTEFFORT,
+               [3] = B43_QOS_BACKGROUND,
+       };
+       BUILD_BUG_ON(ARRAY_SIZE(qos_shm_offsets) != ARRAY_SIZE(wl->qos_params));
+
+       b43_mac_suspend(dev);
+       spin_lock_irqsave(&wl->irq_lock, flags);
+
+       for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
+               params = &(wl->qos_params[i]);
+               if (params->need_hw_update) {
+                       b43_qos_params_upload(dev, &(params->p),
+                                             qos_shm_offsets[i]);
+                       params->need_hw_update = 0;
+               }
+       }
+
+       spin_unlock_irqrestore(&wl->irq_lock, flags);
+       b43_mac_enable(dev);
+}
+
+static void b43_qos_clear(struct b43_wl *wl)
+{
+       struct b43_qos_params *params;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++) {
+               params = &(wl->qos_params[i]);
+
+               memset(&(params->p), 0, sizeof(params->p));
+               params->p.aifs = -1;
+               params->need_hw_update = 1;
+       }
+}
+
+/* Initialize the core's QOS capabilities */
+static void b43_qos_init(struct b43_wldev *dev)
+{
+       struct b43_wl *wl = dev->wl;
+       unsigned int i;
+
+       /* Upload the current QOS parameters. */
+       for (i = 0; i < ARRAY_SIZE(wl->qos_params); i++)
+               wl->qos_params[i].need_hw_update = 1;
+       b43_qos_update(dev);
+
+       /* Enable QOS support. */
+       b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
+       b43_write16(dev, B43_MMIO_IFSCTL,
+                   b43_read16(dev, B43_MMIO_IFSCTL)
+                   | B43_MMIO_IFSCTL_USE_EDCF);
+}
+
+static void b43_qos_update_work(struct work_struct *work)
+{
+       struct b43_wl *wl = container_of(work, struct b43_wl, qos_update_work);
+       struct b43_wldev *dev;
+
+       mutex_lock(&wl->mutex);
+       dev = wl->current_dev;
+       if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED)))
+               b43_qos_update(dev);
+       mutex_unlock(&wl->mutex);
+}
+
+static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
                          const struct ieee80211_tx_queue_params *params)
 {
+       struct b43_wl *wl = hw_to_b43_wl(hw);
+       unsigned long flags;
+       unsigned int queue = (unsigned int)_queue;
+       struct b43_qos_params *p;
+
+       if (queue >= ARRAY_SIZE(wl->qos_params)) {
+               /* Queue not available or don't support setting
+                * params on this queue. Return success to not
+                * confuse mac80211. */
+               return 0;
+       }
+
+       spin_lock_irqsave(&wl->irq_lock, flags);
+       p = &(wl->qos_params[queue]);
+       memcpy(&(p->p), params, sizeof(p->p));
+       p->need_hw_update = 1;
+       spin_unlock_irqrestore(&wl->irq_lock, flags);
+
+       queue_work(hw->workqueue, &wl->qos_update_work);
+
        return 0;
 }
 
@@ -2633,7 +3148,10 @@ static int b43_op_get_tx_stats(struct ieee80211_hw *hw,
                goto out;
        spin_lock_irqsave(&wl->irq_lock, flags);
        if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
-               b43_dma_get_tx_stats(dev, stats);
+               if (b43_using_pio_transfers(dev))
+                       b43_pio_get_tx_stats(dev, stats);
+               else
+                       b43_dma_get_tx_stats(dev, stats);
                err = 0;
        }
        spin_unlock_irqrestore(&wl->irq_lock, flags);
@@ -2654,45 +3172,6 @@ static int b43_op_get_stats(struct ieee80211_hw *hw,
        return 0;
 }
 
-static const char *phymode_to_string(unsigned int phymode)
-{
-       switch (phymode) {
-       case B43_PHYMODE_A:
-               return "A";
-       case B43_PHYMODE_B:
-               return "B";
-       case B43_PHYMODE_G:
-               return "G";
-       default:
-               B43_WARN_ON(1);
-       }
-       return "";
-}
-
-static int find_wldev_for_phymode(struct b43_wl *wl,
-                                 unsigned int phymode,
-                                 struct b43_wldev **dev, bool * gmode)
-{
-       struct b43_wldev *d;
-
-       list_for_each_entry(d, &wl->devlist, list) {
-               if (d->phy.possible_phymodes & phymode) {
-                       /* Ok, this device supports the PHY-mode.
-                        * Now figure out how the gmode bit has to be
-                        * set to support it. */
-                       if (phymode == B43_PHYMODE_A)
-                               *gmode = 0;
-                       else
-                               *gmode = 1;
-                       *dev = d;
-
-                       return 0;
-               }
-       }
-
-       return -ESRCH;
-}
-
 static void b43_put_phy_into_reset(struct b43_wldev *dev)
 {
        struct ssb_device *sdev = dev->dev;
@@ -2712,28 +3191,64 @@ static void b43_put_phy_into_reset(struct b43_wldev *dev)
        msleep(1);
 }
 
+static const char * band_to_string(enum ieee80211_band band)
+{
+       switch (band) {
+       case IEEE80211_BAND_5GHZ:
+               return "5";
+       case IEEE80211_BAND_2GHZ:
+               return "2.4";
+       default:
+               break;
+       }
+       B43_WARN_ON(1);
+       return "";
+}
+
 /* Expects wl->mutex locked */
-static int b43_switch_phymode(struct b43_wl *wl, unsigned int new_mode)
+static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan)
 {
-       struct b43_wldev *up_dev;
+       struct b43_wldev *up_dev = NULL;
        struct b43_wldev *down_dev;
+       struct b43_wldev *d;
        int err;
-       bool gmode = 0;
+       bool gmode;
        int prev_status;
 
-       err = find_wldev_for_phymode(wl, new_mode, &up_dev, &gmode);
-       if (err) {
-               b43err(wl, "Could not find a device for %s-PHY mode\n",
-                      phymode_to_string(new_mode));
-               return err;
+       /* Find a device and PHY which supports the band. */
+       list_for_each_entry(d, &wl->devlist, list) {
+               switch (chan->band) {
+               case IEEE80211_BAND_5GHZ:
+                       if (d->phy.supports_5ghz) {
+                               up_dev = d;
+                               gmode = 0;
+                       }
+                       break;
+               case IEEE80211_BAND_2GHZ:
+                       if (d->phy.supports_2ghz) {
+                               up_dev = d;
+                               gmode = 1;
+                       }
+                       break;
+               default:
+                       B43_WARN_ON(1);
+                       return -EINVAL;
+               }
+               if (up_dev)
+                       break;
+       }
+       if (!up_dev) {
+               b43err(wl, "Could not find a device for %s-GHz band operation\n",
+                      band_to_string(chan->band));
+               return -ENODEV;
        }
        if ((up_dev == wl->current_dev) &&
            (!!wl->current_dev->phy.gmode == !!gmode)) {
                /* This device is already running. */
                return 0;
        }
-       b43dbg(wl, "Reconfiguring PHYmode to %s-PHY\n",
-              phymode_to_string(new_mode));
+       b43dbg(wl, "Switching to %s-GHz band\n",
+              band_to_string(chan->band));
        down_dev = wl->current_dev;
 
        prev_status = b43_status(down_dev);
@@ -2755,8 +3270,8 @@ static int b43_switch_phymode(struct b43_wl *wl, unsigned int new_mode)
                err = b43_wireless_core_init(up_dev);
                if (err) {
                        b43err(wl, "Fatal: Could not initialize device for "
-                              "newly selected %s-PHY mode\n",
-                              phymode_to_string(new_mode));
+                              "selected %s-GHz band\n",
+                              band_to_string(chan->band));
                        goto init_failure;
                }
        }
@@ -2764,8 +3279,8 @@ static int b43_switch_phymode(struct b43_wl *wl, unsigned int new_mode)
                err = b43_wireless_core_start(up_dev);
                if (err) {
                        b43err(wl, "Fatal: Coult not start device for "
-                              "newly selected %s-PHY mode\n",
-                              phymode_to_string(new_mode));
+                              "selected %s-GHz band\n",
+                              band_to_string(chan->band));
                        b43_wireless_core_exit(up_dev);
                        goto init_failure;
                }
@@ -2775,83 +3290,26 @@ static int b43_switch_phymode(struct b43_wl *wl, unsigned int new_mode)
        wl->current_dev = up_dev;
 
        return 0;
-      init_failure:
+init_failure:
        /* Whoops, failed to init the new core. No core is operating now. */
        wl->current_dev = NULL;
        return err;
 }
 
-/* Check if the use of the antenna that ieee80211 told us to
- * use is possible. This will fall back to DEFAULT.
- * "antenna_nr" is the antenna identifier we got from ieee80211. */
-u8 b43_ieee80211_antenna_sanitize(struct b43_wldev *dev,
-                                 u8 antenna_nr)
-{
-       u8 antenna_mask;
-
-       if (antenna_nr == 0) {
-               /* Zero means "use default antenna". That's always OK. */
-               return 0;
-       }
-
-       /* Get the mask of available antennas. */
-       if (dev->phy.gmode)
-               antenna_mask = dev->dev->bus->sprom.ant_available_bg;
-       else
-               antenna_mask = dev->dev->bus->sprom.ant_available_a;
-
-       if (!(antenna_mask & (1 << (antenna_nr - 1)))) {
-               /* This antenna is not available. Fall back to default. */
-               return 0;
-       }
-
-       return antenna_nr;
-}
-
-static int b43_antenna_from_ieee80211(struct b43_wldev *dev, u8 antenna)
-{
-       antenna = b43_ieee80211_antenna_sanitize(dev, antenna);
-       switch (antenna) {
-       case 0:         /* default/diversity */
-               return B43_ANTENNA_DEFAULT;
-       case 1:         /* Antenna 0 */
-               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;
-       }
-}
-
 static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
        struct b43_wldev *dev;
        struct b43_phy *phy;
        unsigned long flags;
-       unsigned int new_phymode = 0xFFFF;
        int antenna;
        int err = 0;
        u32 savedirqs;
 
        mutex_lock(&wl->mutex);
 
-       /* Switch the PHY mode (if necessary). */
-       switch (conf->channel->band) {
-       case IEEE80211_BAND_5GHZ:
-               new_phymode = B43_PHYMODE_A;
-               break;
-       case IEEE80211_BAND_2GHZ:
-               new_phymode = B43_PHYMODE_G;
-               break;
-       default:
-               B43_WARN_ON(1);
-       }
-       err = b43_switch_phymode(wl, new_phymode);
+       /* Switch the band (if necessary). This might change the active core. */
+       err = b43_switch_band(wl, conf->channel);
        if (err)
                goto out_unlock_mutex;
        dev = wl->current_dev;
@@ -2952,6 +3410,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:
@@ -3114,16 +3579,17 @@ static void b43_wireless_core_stop(struct b43_wldev *dev)
        spin_unlock_irqrestore(&wl->irq_lock, flags);
        b43_synchronize_irq(dev);
 
+       write_lock_irqsave(&wl->tx_lock, flags);
        b43_set_status(dev, B43_STAT_INITIALIZED);
+       write_unlock_irqrestore(&wl->tx_lock, flags);
 
+       b43_pio_stop(dev);
        mutex_unlock(&wl->mutex);
        /* Must unlock as it would otherwise deadlock. No races here.
         * Cancel the possibly running self-rearming periodic work. */
        cancel_delayed_work_sync(&dev->periodic_work);
        mutex_lock(&wl->mutex);
 
-       ieee80211_stop_queues(wl->hw);  //FIXME this could cause a deadlock, as mac80211 seems buggy.
-
        b43_mac_suspend(dev);
        free_irq(dev->dev->irq, dev);
        b43dbg(wl, "Wireless interface stopped\n");
@@ -3150,7 +3616,6 @@ static int b43_wireless_core_start(struct b43_wldev *dev)
        /* Start data flow (TX/RX). */
        b43_mac_enable(dev);
        b43_interrupt_enable(dev, dev->irq_savedstate);
-       ieee80211_start_queues(dev->wl->hw);
 
        /* Start maintainance work */
        b43_periodic_tasks_setup(dev);
@@ -3291,8 +3756,8 @@ static void setup_struct_phy_for_init(struct b43_wldev *dev,
        lo = phy->lo_control;
        if (lo) {
                memset(lo, 0, sizeof(*(phy->lo_control)));
-               lo->rebuild = 1;
                lo->tx_bias = 0xFF;
+               INIT_LIST_HEAD(&lo->calib_list);
        }
        phy->max_lb_gain = 0;
        phy->trsw_rx_gain = 0;
@@ -3347,8 +3812,10 @@ static void setup_struct_wldev_for_init(struct b43_wldev *dev)
 static void b43_bluetooth_coext_enable(struct b43_wldev *dev)
 {
        struct ssb_sprom *sprom = &dev->dev->bus->sprom;
-       u32 hf;
+       u64 hf;
 
+       if (!modparam_btcoex)
+               return;
        if (!(sprom->boardflags_lo & B43_BFL_BTCOEXIST))
                return;
        if (dev->phy.type != B43_PHYTYPE_B && !dev->phy.gmode)
@@ -3360,11 +3827,13 @@ static void b43_bluetooth_coext_enable(struct b43_wldev *dev)
        else
                hf |= B43_HF_BTCOEX;
        b43_hf_write(dev, hf);
-       //TODO
 }
 
 static void b43_bluetooth_coext_disable(struct b43_wldev *dev)
-{                              //TODO
+{
+       if (!modparam_btcoex)
+               return;
+       //TODO
 }
 
 static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev)
@@ -3410,6 +3879,41 @@ static void b43_set_retry_limits(struct b43_wldev *dev,
                        long_retry);
 }
 
+static void b43_set_synth_pu_delay(struct b43_wldev *dev, bool idle)
+{
+       u16 pu_delay;
+
+       /* The time value is in microseconds. */
+       if (dev->phy.type == B43_PHYTYPE_A)
+               pu_delay = 3700;
+       else
+               pu_delay = 1050;
+       if (b43_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);
+
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_SPUWKUP, pu_delay);
+}
+
+/* Set the TSF CFP pre-TargetBeaconTransmissionTime. */
+static void b43_set_pretbtt(struct b43_wldev *dev)
+{
+       u16 pretbtt;
+
+       /* The time value is in microseconds. */
+       if (b43_is_mode(dev->wl, IEEE80211_IF_TYPE_IBSS)) {
+               pretbtt = 2;
+       } else {
+               if (dev->phy.type == B43_PHYTYPE_A)
+                       pretbtt = 120;
+               else
+                       pretbtt = 250;
+       }
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRETBTT, pretbtt);
+       b43_write16(dev, B43_MMIO_TSF_CFP_PRETBTT, pretbtt);
+}
+
 /* Shutdown a wireless core */
 /* Locking: wl->mutex */
 static void b43_wireless_core_exit(struct b43_wldev *dev)
@@ -3428,9 +3932,12 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
        macctl |= B43_MACCTL_PSM_JMP0;
        b43_write32(dev, B43_MMIO_MACCTL, macctl);
 
-       b43_leds_exit(dev);
-       b43_rng_exit(dev->wl);
+       if (!dev->suspend_in_progress) {
+               b43_leds_exit(dev);
+               b43_rng_exit(dev->wl);
+       }
        b43_dma_free(dev);
+       b43_pio_free(dev);
        b43_chip_exit(dev);
        b43_radio_turn_off(dev, 1);
        b43_switch_analog(dev, 0);
@@ -3455,7 +3962,8 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
        struct ssb_sprom *sprom = &bus->sprom;
        struct b43_phy *phy = &dev->phy;
        int err;
-       u32 hf, tmp;
+       u64 hf;
+       u32 tmp;
 
        B43_WARN_ON(b43_status(dev) != B43_STAT_UNINIT);
 
@@ -3518,6 +4026,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
        b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_PRMAXTIME, 1);
 
        b43_rate_memory_init(dev);
+       b43_set_phytxctl_defaults(dev);
 
        /* Minimum Contention Window */
        if (phy->type == B43_PHYTYPE_B) {
@@ -3528,28 +4037,29 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
        /* Maximum Contention Window */
        b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
 
-       err = b43_dma_init(dev);
+       if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) {
+               dev->__using_pio_transfers = 1;
+               err = b43_pio_init(dev);
+       } else {
+               dev->__using_pio_transfers = 0;
+               err = b43_dma_init(dev);
+       }
        if (err)
                goto err_chip_exit;
        b43_qos_init(dev);
-
-//FIXME
-#if 1
-       b43_write16(dev, 0x0612, 0x0050);
-       b43_shm_write16(dev, B43_SHM_SHARED, 0x0416, 0x0050);
-       b43_shm_write16(dev, B43_SHM_SHARED, 0x0414, 0x01F4);
-#endif
-
+       b43_set_synth_pu_delay(dev, 1);
        b43_bluetooth_coext_enable(dev);
 
        ssb_bus_powerup(bus, 1);        /* Enable dynamic PCTL */
        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;
 
@@ -3597,6 +4107,8 @@ static int b43_op_add_interface(struct ieee80211_hw *hw,
 
        spin_lock_irqsave(&wl->irq_lock, flags);
        b43_adjust_opmode(dev);
+       b43_set_pretbtt(dev);
+       b43_set_synth_pu_delay(dev, 0);
        b43_upload_card_macaddress(dev);
        spin_unlock_irqrestore(&wl->irq_lock, flags);
 
@@ -3648,6 +4160,7 @@ static int b43_op_start(struct ieee80211_hw *hw)
        memset(wl->mac_addr, 0, ETH_ALEN);
        wl->filter_flags = 0;
        wl->radiotap_enabled = 0;
+       b43_qos_clear(wl);
 
        /* First register RFkill.
         * LEDs that are registered later depend on it. */
@@ -3689,6 +4202,8 @@ static void b43_op_stop(struct ieee80211_hw *hw)
        struct b43_wldev *dev = wl->current_dev;
 
        b43_rfkill_exit(dev);
+       cancel_work_sync(&(wl->qos_update_work));
+       cancel_work_sync(&(wl->beacon_update_trigger));
 
        mutex_lock(&wl->mutex);
        if (b43_status(dev) >= B43_STAT_STARTED)
@@ -3727,7 +4242,7 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, int aid, int set)
         * 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);
+       beacon = ieee80211_beacon_get(hw, wl->vif);
        if (unlikely(!beacon))
                return -ENOMEM;
        spin_lock_irqsave(&wl->irq_lock, flags);
@@ -3738,8 +4253,7 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, int aid, int set)
 }
 
 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;
@@ -3751,6 +4265,16 @@ static int b43_op_ibss_beacon_update(struct ieee80211_hw *hw,
        return 0;
 }
 
+static void b43_op_sta_notify(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             enum sta_notify_cmd notify_cmd,
+                             const u8 *addr)
+{
+       struct b43_wl *wl = hw_to_b43_wl(hw);
+
+       B43_WARN_ON(!vif || wl->vif != vif);
+}
+
 static const struct ieee80211_ops b43_hw_ops = {
        .tx                     = b43_op_tx,
        .conf_tx                = b43_op_conf_tx,
@@ -3767,6 +4291,7 @@ static const struct ieee80211_ops b43_hw_ops = {
        .set_retry_limit        = b43_op_set_retry_limit,
        .set_tim                = b43_op_beacon_set_tim,
        .beacon_update          = b43_op_ibss_beacon_update,
+       .sta_notify             = b43_op_sta_notify,
 };
 
 /* Hard-reset the chip. Do not call this directly.
@@ -3810,21 +4335,23 @@ static void b43_chip_reset(struct work_struct *work)
                b43info(wl, "Controller restarted\n");
 }
 
-static int b43_setup_modes(struct b43_wldev *dev,
+static int b43_setup_bands(struct b43_wldev *dev,
                           bool have_2ghz_phy, bool have_5ghz_phy)
 {
        struct ieee80211_hw *hw = dev->wl->hw;
-       struct b43_phy *phy = &dev->phy;
 
-       /* 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. */
+       if (have_2ghz_phy)
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43_band_2GHz;
+       if (dev->phy.type == B43_PHYTYPE_N) {
+               if (have_5ghz_phy)
+                       hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_nphy;
+       } else {
+               if (have_5ghz_phy)
+                       hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_aphy;
+       }
 
-       hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43_band_2GHz;
-       phy->possible_phymodes |= B43_PHYMODE_G;
+       dev->phy.supports_2ghz = have_2ghz_phy;
+       dev->phy.supports_5ghz = have_5ghz_phy;
 
        return 0;
 }
@@ -3899,6 +4426,14 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
                err = -EOPNOTSUPP;
                goto err_powerdown;
        }
+       if (1 /* disable A-PHY */) {
+               /* FIXME: For now we disable the A-PHY on multi-PHY devices. */
+               if (dev->phy.type != B43_PHYTYPE_N) {
+                       have_2ghz_phy = 1;
+                       have_5ghz_phy = 0;
+               }
+       }
+
        dev->phy.gmode = have_2ghz_phy;
        tmp = dev->phy.gmode ? B43_TMSLOW_GMODE : 0;
        b43_wireless_core_reset(dev, tmp);
@@ -3906,7 +4441,7 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
        err = b43_validate_chipaccess(dev);
        if (err)
                goto err_powerdown;
-       err = b43_setup_modes(dev, have_2ghz_phy, have_5ghz_phy);
+       err = b43_setup_bands(dev, have_2ghz_phy, have_5ghz_phy);
        if (err)
                goto err_powerdown;
 
@@ -3996,8 +4531,16 @@ static int b43_one_core_attach(struct ssb_device *dev, struct b43_wl *wl)
        return err;
 }
 
+#define IS_PDEV(pdev, _vendor, _device, _subvendor, _subdevice)                ( \
+       (pdev->vendor == PCI_VENDOR_ID_##_vendor) &&                    \
+       (pdev->device == _device) &&                                    \
+       (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) &&       \
+       (pdev->subsystem_device == _subdevice)                          )
+
 static void b43_sprom_fixup(struct ssb_bus *bus)
 {
+       struct pci_dev *pdev;
+
        /* boardflags workarounds */
        if (bus->boardinfo.vendor == SSB_BOARDVENDOR_DELL &&
            bus->chip_id == 0x4301 && bus->boardinfo.rev == 0x74)
@@ -4005,6 +4548,13 @@ static void b43_sprom_fixup(struct ssb_bus *bus)
        if (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE &&
            bus->boardinfo.type == 0x4E && bus->boardinfo.rev > 0x40)
                bus->sprom.boardflags_lo |= B43_BFL_PACTRL;
+       if (bus->bustype == SSB_BUSTYPE_PCI) {
+               pdev = bus->host_pci;
+               if (IS_PDEV(pdev, BROADCOM, 0x4318, ASUSTEK, 0x100F) ||
+                   IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0015) ||
+                   IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0013))
+                       bus->sprom.boardflags_lo &= ~B43_BFL_BTCOEXIST;
+       }
 }
 
 static void b43_wireless_exit(struct ssb_device *dev, struct b43_wl *wl)
@@ -4032,11 +4582,11 @@ static int b43_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;
-       hw->queues = 1;         /* FIXME: hardware has more queues */
+                   IEEE80211_HW_RX_INCLUDES_FCS |
+                   IEEE80211_HW_SIGNAL_DBM |
+                   IEEE80211_HW_NOISE_DBM;
+
+       hw->queues = b43_modparam_qos ? 4 : 1;
        SET_IEEE80211_DEV(hw, dev->dev);
        if (is_valid_ether_addr(sprom->et1mac))
                SET_IEEE80211_PERM_ADDR(hw, sprom->et1mac);
@@ -4048,10 +4598,13 @@ static int b43_wireless_init(struct ssb_device *dev)
        memset(wl, 0, sizeof(*wl));
        wl->hw = hw;
        spin_lock_init(&wl->irq_lock);
+       rwlock_init(&wl->tx_lock);
        spin_lock_init(&wl->leds_lock);
        spin_lock_init(&wl->shm_lock);
        mutex_init(&wl->mutex);
        INIT_LIST_HEAD(&wl->devlist);
+       INIT_WORK(&wl->qos_update_work, b43_qos_update_work);
+       INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
 
        ssb_set_devtypedata(dev, wl);
        b43info(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id);
@@ -4136,6 +4689,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);
@@ -4167,15 +4721,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);
                        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;
 }
 
@@ -4193,6 +4749,33 @@ static struct ssb_driver b43_ssb_driver = {
        .resume         = b43_resume,
 };
 
+static void b43_print_driverinfo(void)
+{
+       const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "",
+                  *feat_leds = "", *feat_rfkill = "";
+
+#ifdef CONFIG_B43_PCI_AUTOSELECT
+       feat_pci = "P";
+#endif
+#ifdef CONFIG_B43_PCMCIA
+       feat_pcmcia = "M";
+#endif
+#ifdef CONFIG_B43_NPHY
+       feat_nphy = "N";
+#endif
+#ifdef CONFIG_B43_LEDS
+       feat_leds = "L";
+#endif
+#ifdef CONFIG_B43_RFKILL
+       feat_rfkill = "R";
+#endif
+       printk(KERN_INFO "Broadcom 43xx driver loaded "
+              "[ Features: %s%s%s%s%s, Firmware-ID: "
+              B43_SUPPORTED_FIRMWARE_ID " ]\n",
+              feat_pci, feat_pcmcia, feat_nphy,
+              feat_leds, feat_rfkill);
+}
+
 static int __init b43_init(void)
 {
        int err;
@@ -4204,6 +4787,7 @@ static int __init b43_init(void)
        err = ssb_driver_register(&b43_ssb_driver);
        if (err)
                goto err_pcmcia_exit;
+       b43_print_driverinfo();
 
        return err;