Update b43 from compat-wireless-2008-05-26 codebase
authorPeter Denison <openwrt@marshadder.org>
Wed, 4 Jun 2008 19:47:48 +0000 (19:47 +0000)
committerPeter Denison <openwrt@marshadder.org>
Wed, 4 Jun 2008 19:47:48 +0000 (19:47 +0000)
SVN-Revision: 11358

22 files changed:
package/b43/Makefile
package/b43/src/Makefile
package/b43/src/b43.h
package/b43/src/debugfs.c
package/b43/src/debugfs.h
package/b43/src/dma.c
package/b43/src/dma.h
package/b43/src/leds.c
package/b43/src/lo.c
package/b43/src/lo.h
package/b43/src/main.c
package/b43/src/main.h
package/b43/src/nphy.c
package/b43/src/nphy.h
package/b43/src/pcmcia.c
package/b43/src/phy.c
package/b43/src/phy.h
package/b43/src/pio.c [new file with mode: 0644]
package/b43/src/pio.h [new file with mode: 0644]
package/b43/src/wa.c
package/b43/src/xmit.c
package/b43/src/xmit.h

index 3d5eb26..6616585 100644 (file)
@@ -44,6 +44,8 @@ endef
 EXTRA_KCONFIG:= \
        CONFIG_B43=m \
        CONFIG_B43_NPHY=y \
+       CONFIG_B43_DEBUG=y \
+       $(if $(CONFIG_RFKILL),CONFIG_B43_RFKILL=y) \
        $(if $(CONFIG_LEDS_TRIGGERS),CONFIG_B43_LEDS=y) \
 
 
index ac1329d..8c52b0b 100644 (file)
@@ -1,13 +1,14 @@
 b43-y                          += main.o
 b43-y                          += tables.o
-b43-y                          += tables_nphy.o
+b43-$(CONFIG_B43_NPHY)         += tables_nphy.o
 b43-y                          += phy.o
-b43-y                          += nphy.o
+b43-$(CONFIG_B43_NPHY)         += nphy.o
 b43-y                          += sysfs.o
 b43-y                          += xmit.o
 b43-y                          += lo.o
 b43-y                          += wa.o
 b43-y                          += dma.o
+b43-$(CONFIG_B43_PIO)          += pio.o
 b43-$(CONFIG_B43_RFKILL)       += rfkill.o
 b43-$(CONFIG_B43_LEDS)         += leds.o
 b43-$(CONFIG_B43_PCMCIA)       += pcmcia.o
index 0dc1aaf..e919189 100644 (file)
 #include "lo.h"
 #include "phy.h"
 
+
+/* The unique identifier of the firmware that's officially supported by
+ * this driver version. */
+#define B43_SUPPORTED_FIRMWARE_ID      "FW13"
+
+
 #ifdef CONFIG_B43_DEBUG
 # define B43_DEBUG     1
 #else
 #define B43_MMIO_DMA64_BASE4           0x300
 #define B43_MMIO_DMA64_BASE5           0x340
 
+/* PIO on core rev < 11 */
+#define B43_MMIO_PIO_BASE0             0x300
+#define B43_MMIO_PIO_BASE1             0x310
+#define B43_MMIO_PIO_BASE2             0x320
+#define B43_MMIO_PIO_BASE3             0x330
+#define B43_MMIO_PIO_BASE4             0x340
+#define B43_MMIO_PIO_BASE5             0x350
+#define B43_MMIO_PIO_BASE6             0x360
+#define B43_MMIO_PIO_BASE7             0x370
+/* PIO on core rev >= 11 */
+#define B43_MMIO_PIO11_BASE0           0x200
+#define B43_MMIO_PIO11_BASE1           0x240
+#define B43_MMIO_PIO11_BASE2           0x280
+#define B43_MMIO_PIO11_BASE3           0x2C0
+#define B43_MMIO_PIO11_BASE4           0x300
+#define B43_MMIO_PIO11_BASE5           0x340
+
 #define B43_MMIO_PHY_VER               0x3E0
 #define B43_MMIO_PHY_RADIO             0x3E2
 #define B43_MMIO_PHY0                  0x3E6
 #define B43_MMIO_GPIO_MASK             0x49E
 #define B43_MMIO_TSF_CFP_START_LOW     0x604
 #define B43_MMIO_TSF_CFP_START_HIGH    0x606
+#define B43_MMIO_TSF_CFP_PRETBTT       0x612
 #define B43_MMIO_TSF_0                 0x632   /* core rev < 3 only */
 #define B43_MMIO_TSF_1                 0x634   /* core rev < 3 only */
 #define B43_MMIO_TSF_2                 0x636   /* core rev < 3 only */
 #define B43_MMIO_TSF_3                 0x638   /* core rev < 3 only */
 #define B43_MMIO_RNG                   0x65A
+#define B43_MMIO_IFSCTL                        0x688 /* Interframe space control */
+#define  B43_MMIO_IFSCTL_USE_EDCF      0x0004
 #define B43_MMIO_POWERUP_DELAY         0x6A8
 
 /* SPROM boardflags_lo values */
@@ -138,7 +164,8 @@ enum {
 #define B43_SHM_SH_PHYTYPE             0x0052  /* PHY type */
 #define B43_SHM_SH_ANTSWAP             0x005C  /* Antenna swap threshold */
 #define B43_SHM_SH_HOSTFLO             0x005E  /* Hostflags for ucode options (low) */
-#define B43_SHM_SH_HOSTFHI             0x0060  /* Hostflags for ucode options (high) */
+#define B43_SHM_SH_HOSTFMI             0x0060  /* Hostflags for ucode options (middle) */
+#define B43_SHM_SH_HOSTFHI             0x0062  /* Hostflags for ucode options (high) */
 #define B43_SHM_SH_RFATT               0x0064  /* Current radio attenuation value */
 #define B43_SHM_SH_RADAR               0x0066  /* Radar register */
 #define B43_SHM_SH_PHYTXNOI            0x006E  /* PHY noise directly after TX (lower 8bit only) */
@@ -226,31 +253,41 @@ enum {
 #define B43_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4)
 
 /* HostFlags. See b43_hf_read/write() */
-#define B43_HF_ANTDIVHELP              0x00000001      /* ucode antenna div helper */
-#define B43_HF_SYMW                    0x00000002      /* G-PHY SYM workaround */
-#define B43_HF_RXPULLW                 0x00000004      /* RX pullup workaround */
-#define B43_HF_CCKBOOST                        0x00000008      /* 4dB CCK power boost (exclusive with OFDM boost) */
-#define B43_HF_BTCOEX                  0x00000010      /* Bluetooth coexistance */
-#define B43_HF_GDCW                    0x00000020      /* G-PHY DV canceller filter bw workaround */
-#define B43_HF_OFDMPABOOST             0x00000040      /* Enable PA gain boost for OFDM */
-#define B43_HF_ACPR                    0x00000080      /* Disable for Japan, channel 14 */
-#define B43_HF_EDCF                    0x00000100      /* on if WME and MAC suspended */
-#define B43_HF_TSSIRPSMW               0x00000200      /* TSSI reset PSM ucode workaround */
-#define B43_HF_DSCRQ                   0x00000400      /* Disable slow clock request in ucode */
-#define B43_HF_ACIW                    0x00000800      /* ACI workaround: shift bits by 2 on PHY CRS */
-#define B43_HF_2060W                   0x00001000      /* 2060 radio workaround */
-#define B43_HF_RADARW                  0x00002000      /* Radar workaround */
-#define B43_HF_USEDEFKEYS              0x00004000      /* Enable use of default keys */
-#define B43_HF_BT4PRIOCOEX             0x00010000      /* Bluetooth 2-priority coexistance */
-#define B43_HF_FWKUP                   0x00020000      /* Fast wake-up ucode */
-#define B43_HF_VCORECALC               0x00040000      /* Force VCO recalculation when powering up synthpu */
-#define B43_HF_PCISCW                  0x00080000      /* PCI slow clock workaround */
-#define B43_HF_4318TSSI                        0x00200000      /* 4318 TSSI */
-#define B43_HF_FBCMCFIFO               0x00400000      /* Flush bcast/mcast FIFO immediately */
-#define B43_HF_HWPCTL                  0x00800000      /* Enable hardwarre power control */
-#define B43_HF_BTCOEXALT               0x01000000      /* Bluetooth coexistance in alternate pins */
-#define B43_HF_TXBTCHECK               0x02000000      /* Bluetooth check during transmission */
-#define B43_HF_SKCFPUP                 0x04000000      /* Skip CFP update */
+#define B43_HF_ANTDIVHELP      0x000000000001ULL /* ucode antenna div helper */
+#define B43_HF_SYMW            0x000000000002ULL /* G-PHY SYM workaround */
+#define B43_HF_RXPULLW         0x000000000004ULL /* RX pullup workaround */
+#define B43_HF_CCKBOOST                0x000000000008ULL /* 4dB CCK power boost (exclusive with OFDM boost) */
+#define B43_HF_BTCOEX          0x000000000010ULL /* Bluetooth coexistance */
+#define B43_HF_GDCW            0x000000000020ULL /* G-PHY DC canceller filter bw workaround */
+#define B43_HF_OFDMPABOOST     0x000000000040ULL /* Enable PA gain boost for OFDM */
+#define B43_HF_ACPR            0x000000000080ULL /* Disable for Japan, channel 14 */
+#define B43_HF_EDCF            0x000000000100ULL /* on if WME and MAC suspended */
+#define B43_HF_TSSIRPSMW       0x000000000200ULL /* TSSI reset PSM ucode workaround */
+#define B43_HF_20IN40IQW       0x000000000200ULL /* 20 in 40 MHz I/Q workaround (rev >= 13 only) */
+#define B43_HF_DSCRQ           0x000000000400ULL /* Disable slow clock request in ucode */
+#define B43_HF_ACIW            0x000000000800ULL /* ACI workaround: shift bits by 2 on PHY CRS */
+#define B43_HF_2060W           0x000000001000ULL /* 2060 radio workaround */
+#define B43_HF_RADARW          0x000000002000ULL /* Radar workaround */
+#define B43_HF_USEDEFKEYS      0x000000004000ULL /* Enable use of default keys */
+#define B43_HF_AFTERBURNER     0x000000008000ULL /* Afterburner enabled */
+#define B43_HF_BT4PRIOCOEX     0x000000010000ULL /* Bluetooth 4-priority coexistance */
+#define B43_HF_FWKUP           0x000000020000ULL /* Fast wake-up ucode */
+#define B43_HF_VCORECALC       0x000000040000ULL /* Force VCO recalculation when powering up synthpu */
+#define B43_HF_PCISCW          0x000000080000ULL /* PCI slow clock workaround */
+#define B43_HF_4318TSSI                0x000000200000ULL /* 4318 TSSI */
+#define B43_HF_FBCMCFIFO       0x000000400000ULL /* Flush bcast/mcast FIFO immediately */
+#define B43_HF_HWPCTL          0x000000800000ULL /* Enable hardwarre power control */
+#define B43_HF_BTCOEXALT       0x000001000000ULL /* Bluetooth coexistance in alternate pins */
+#define B43_HF_TXBTCHECK       0x000002000000ULL /* Bluetooth check during transmission */
+#define B43_HF_SKCFPUP         0x000004000000ULL /* Skip CFP update */
+#define B43_HF_N40W            0x000008000000ULL /* N PHY 40 MHz workaround (rev >= 13 only) */
+#define B43_HF_ANTSEL          0x000020000000ULL /* Antenna selection (for testing antenna div.) */
+#define B43_HF_BT3COEXT                0x000020000000ULL /* Bluetooth 3-wire coexistence (rev >= 13 only) */
+#define B43_HF_BTCANT          0x000040000000ULL /* Bluetooth coexistence (antenna mode) (rev >= 13 only) */
+#define B43_HF_ANTSELEN                0x000100000000ULL /* Antenna selection enabled (rev >= 13 only) */
+#define B43_HF_ANTSELMODE      0x000200000000ULL /* Antenna selection mode (rev >= 13 only) */
+#define B43_HF_MLADVW          0x001000000000ULL /* N PHY ML ADV workaround (rev >= 13 only) */
+#define B43_HF_PR45960W                0x080000000000ULL /* PR 45960 workaround (rev >= 13 only) */
 
 /* MacFilter offsets. */
 #define B43_MACFILTER_SELF             0x0000
@@ -373,9 +410,7 @@ enum {
 #define B43_IRQ_TIMEOUT                        0x80000000
 
 #define B43_IRQ_ALL                    0xFFFFFFFF
-#define B43_IRQ_MASKTEMPLATE           (B43_IRQ_MAC_SUSPENDED | \
-                                        B43_IRQ_BEACON | \
-                                        B43_IRQ_TBTT_INDI | \
+#define B43_IRQ_MASKTEMPLATE           (B43_IRQ_TBTT_INDI | \
                                         B43_IRQ_ATIM_END | \
                                         B43_IRQ_PMQ | \
                                         B43_IRQ_MAC_TXERR | \
@@ -387,6 +422,26 @@ enum {
                                         B43_IRQ_RFKILL | \
                                         B43_IRQ_TX_OK)
 
+/* The firmware register to fetch the debug-IRQ reason from. */
+#define B43_DEBUGIRQ_REASON_REG                63
+/* Debug-IRQ reasons. */
+#define B43_DEBUGIRQ_PANIC             0       /* The firmware panic'ed */
+#define B43_DEBUGIRQ_DUMP_SHM          1       /* Dump shared SHM */
+#define B43_DEBUGIRQ_DUMP_REGS         2       /* Dump the microcode registers */
+#define B43_DEBUGIRQ_MARKER            3       /* A "marker" was thrown by the firmware. */
+#define B43_DEBUGIRQ_ACK               0xFFFF  /* The host writes that to ACK the IRQ */
+
+/* The firmware register that contains the "marker" line. */
+#define B43_MARKER_ID_REG              2
+#define B43_MARKER_LINE_REG            3
+
+/* The firmware register to fetch the panic reason from. */
+#define B43_FWPANIC_REASON_REG         3
+/* Firmware panic reason codes */
+#define B43_FWPANIC_DIE                        0 /* Firmware died. Don't auto-restart it. */
+#define B43_FWPANIC_RESTART            1 /* Firmware died. Schedule a controller reset. */
+
+
 /* Device specific rate values.
  * The actual values defined here are (rate_in_mbps * 2).
  * Some code depends on this. Don't change it. */
@@ -423,7 +478,6 @@ enum {
 };
 
 struct b43_dmaring;
-struct b43_pioqueue;
 
 /* The firmware file header */
 #define B43_FW_TYPE_UCODE      'u'
@@ -452,14 +506,11 @@ struct b43_iv {
 } __attribute__((__packed__));
 
 
-#define B43_PHYMODE(phytype)           (1 << (phytype))
-#define B43_PHYMODE_A                  B43_PHYMODE(B43_PHYTYPE_A)
-#define B43_PHYMODE_B                  B43_PHYMODE(B43_PHYTYPE_B)
-#define B43_PHYMODE_G                  B43_PHYMODE(B43_PHYTYPE_G)
-
 struct b43_phy {
-       /* Possible PHYMODEs on this PHY */
-       u8 possible_phymodes;
+       /* Band support flags. */
+       bool supports_2ghz;
+       bool supports_5ghz;
+
        /* GMODE bit enabled? */
        bool gmode;
 
@@ -573,15 +624,27 @@ struct b43_phy {
 
 /* Data structures for DMA transmission, per 80211 core. */
 struct b43_dma {
-       struct b43_dmaring *tx_ring0;
-       struct b43_dmaring *tx_ring1;
-       struct b43_dmaring *tx_ring2;
-       struct b43_dmaring *tx_ring3;
-       struct b43_dmaring *tx_ring4;
-       struct b43_dmaring *tx_ring5;
-
-       struct b43_dmaring *rx_ring0;
-       struct b43_dmaring *rx_ring3;   /* only available on core.rev < 5 */
+       struct b43_dmaring *tx_ring_AC_BK; /* Background */
+       struct b43_dmaring *tx_ring_AC_BE; /* Best Effort */
+       struct b43_dmaring *tx_ring_AC_VI; /* Video */
+       struct b43_dmaring *tx_ring_AC_VO; /* Voice */
+       struct b43_dmaring *tx_ring_mcast; /* Multicast */
+
+       struct b43_dmaring *rx_ring;
+};
+
+struct b43_pio_txqueue;
+struct b43_pio_rxqueue;
+
+/* Data structures for PIO transmission, per 80211 core. */
+struct b43_pio {
+       struct b43_pio_txqueue *tx_queue_AC_BK; /* Background */
+       struct b43_pio_txqueue *tx_queue_AC_BE; /* Best Effort */
+       struct b43_pio_txqueue *tx_queue_AC_VI; /* Video */
+       struct b43_pio_txqueue *tx_queue_AC_VO; /* Voice */
+       struct b43_pio_txqueue *tx_queue_mcast; /* Multicast */
+
+       struct b43_pio_rxqueue *rx_queue;
 };
 
 /* Context information for a noise calculation (Link Quality). */
@@ -607,6 +670,35 @@ struct b43_key {
        u8 algorithm;
 };
 
+/* SHM offsets to the QOS data structures for the 4 different queues. */
+#define B43_QOS_PARAMS(queue)  (B43_SHM_SH_EDCFQ + \
+                                (B43_NR_QOSPARAMS * sizeof(u16) * (queue)))
+#define B43_QOS_BACKGROUND     B43_QOS_PARAMS(0)
+#define B43_QOS_BESTEFFORT     B43_QOS_PARAMS(1)
+#define B43_QOS_VIDEO          B43_QOS_PARAMS(2)
+#define B43_QOS_VOICE          B43_QOS_PARAMS(3)
+
+/* QOS parameter hardware data structure offsets. */
+#define B43_NR_QOSPARAMS       22
+enum {
+       B43_QOSPARAM_TXOP = 0,
+       B43_QOSPARAM_CWMIN,
+       B43_QOSPARAM_CWMAX,
+       B43_QOSPARAM_CWCUR,
+       B43_QOSPARAM_AIFS,
+       B43_QOSPARAM_BSLOTS,
+       B43_QOSPARAM_REGGAP,
+       B43_QOSPARAM_STATUS,
+};
+
+/* QOS parameters for a queue. */
+struct b43_qos_params {
+       /* The QOS parameters */
+       struct ieee80211_tx_queue_params p;
+       /* Does this need to get uploaded to hardware? */
+       bool need_hw_update;
+};
+
 struct b43_wldev;
 
 /* Data structure for the WLAN parts (802.11 cores) of the b43 chip. */
@@ -618,6 +710,10 @@ struct b43_wl {
 
        struct mutex mutex;
        spinlock_t irq_lock;
+       /* R/W lock for data transmission.
+        * Transmissions on 2+ queues can run concurrently, but somebody else
+        * might sync with TX by write_lock_irqsave()'ing. */
+       rwlock_t tx_lock;
        /* Lock for LEDs access. */
        spinlock_t leds_lock;
        /* Lock for SHM access. */
@@ -659,6 +755,13 @@ struct b43_wl {
        struct sk_buff *current_beacon;
        bool beacon0_uploaded;
        bool beacon1_uploaded;
+       struct work_struct beacon_update_trigger;
+
+       /* The current QOS parameters for the 4 queues.
+        * This is protected by the irq_lock. */
+       struct b43_qos_params qos_params[4];
+       /* Workqueue for updating QOS parameters in hardware. */
+       struct work_struct qos_update_work;
 };
 
 /* In-memory representation of a cached microcode file. */
@@ -682,6 +785,13 @@ struct b43_firmware {
        u16 rev;
        /* Firmware patchlevel */
        u16 patch;
+
+       /* Set to true, if we are using an opensource firmware. */
+       bool opensource;
+       /* Set to true, if the core needs a PCM firmware, but
+        * we failed to load one. This is always false for
+        * core rev > 10, as these don't need PCM firmware. */
+       bool pcm_request_failed;
 };
 
 /* Device (802.11 core) initialization status. */
@@ -719,12 +829,20 @@ struct b43_wldev {
        bool dfq_valid;         /* Directed frame queue valid (IBSS PS mode, ATIM) */
        bool short_slot;        /* TRUE, if short slot timing is enabled. */
        bool radio_hw_enable;   /* saved state of radio hardware enabled state */
+       bool suspend_in_progress;       /* TRUE, if we are in a suspend/resume cycle */
 
        /* PHY/Radio device. */
        struct b43_phy phy;
 
-       /* DMA engines. */
-       struct b43_dma dma;
+       union {
+               /* DMA engines. */
+               struct b43_dma dma;
+               /* PIO engines. */
+               struct b43_pio pio;
+       };
+       /* Use b43_using_pio_transfers() to check whether we are using
+        * DMA or PIO data transfers. */
+       bool __using_pio_transfers;
 
        /* Various statistics about the physical device. */
        struct b43_stats stats;
@@ -808,6 +926,22 @@ static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value)
        ssb_write32(dev->dev, offset, value);
 }
 
+static inline bool b43_using_pio_transfers(struct b43_wldev *dev)
+{
+#ifdef CONFIG_B43_PIO
+       return dev->__using_pio_transfers;
+#else
+       return 0;
+#endif
+}
+
+#ifdef CONFIG_B43_FORCE_PIO
+# define B43_FORCE_PIO 1
+#else
+# define B43_FORCE_PIO 0
+#endif
+
+
 /* Message printing */
 void b43info(struct b43_wl *wl, const char *fmt, ...)
     __attribute__ ((format(printf, 2, 3)));
@@ -831,22 +965,6 @@ static inline bool __b43_warn_on_dummy(bool x) { return x; }
 # define B43_WARN_ON(x)        __b43_warn_on_dummy(unlikely(!!(x)))
 #endif
 
-/** Limit a value between two limits */
-#ifdef limit_value
-# undef limit_value
-#endif
-#define limit_value(value, min, max)  \
-       ({                                              \
-               typeof(value) __value = (value);        \
-               typeof(value) __min = (min);            \
-               typeof(value) __max = (max);            \
-               if (__value < __min)                    \
-                       __value = __min;                \
-               else if (__value > __max)               \
-                       __value = __max;                \
-               __value;                                \
-       })
-
 /* Convert an integer to a Q5.2 value */
 #define INT_TO_Q52(i)  ((i) << 2)
 /* Convert a Q5.2 value to an integer (precision loss!) */
index e38ed0f..210e278 100644 (file)
@@ -270,24 +270,22 @@ static int restart_write_file(struct b43_wldev *dev,
        return err;
 }
 
-static ssize_t append_lo_table(ssize_t count, char *buf, const size_t bufsize,
-                              struct b43_loctl table[B43_NR_BB][B43_NR_RF])
+static unsigned long calc_expire_secs(unsigned long now,
+                                     unsigned long time,
+                                     unsigned long expire)
 {
-       unsigned int i, j;
-       struct b43_loctl *ctl;
-
-       for (i = 0; i < B43_NR_BB; i++) {
-               for (j = 0; j < B43_NR_RF; j++) {
-                       ctl = &(table[i][j]);
-                       fappend("(bbatt %2u, rfatt %2u)  ->  "
-                               "(I %+3d, Q %+3d, Used: %d, Calibrated: %d)\n",
-                               i, j, ctl->i, ctl->q,
-                               ctl->used,
-                               b43_loctl_is_calibrated(ctl));
-               }
+       expire = time + expire;
+
+       if (time_after(now, expire))
+               return 0; /* expired */
+       if (expire < now) {
+               /* jiffies wrapped */
+               expire -= MAX_JIFFY_OFFSET;
+               now -= MAX_JIFFY_OFFSET;
        }
+       B43_WARN_ON(expire < now);
 
-       return count;
+       return (expire - now) / HZ;
 }
 
 static ssize_t loctls_read_file(struct b43_wldev *dev,
@@ -296,27 +294,45 @@ static ssize_t loctls_read_file(struct b43_wldev *dev,
        ssize_t count = 0;
        struct b43_txpower_lo_control *lo;
        int i, err = 0;
+       struct b43_lo_calib *cal;
+       unsigned long now = jiffies;
+       struct b43_phy *phy = &dev->phy;
 
-       if (dev->phy.type != B43_PHYTYPE_G) {
+       if (phy->type != B43_PHYTYPE_G) {
                fappend("Device is not a G-PHY\n");
                err = -ENODEV;
                goto out;
        }
-       lo = dev->phy.lo_control;
+       lo = phy->lo_control;
        fappend("-- Local Oscillator calibration data --\n\n");
-       fappend("Measured: %d,  Rebuild: %d,  HW-power-control: %d\n",
-               lo->lo_measured,
-               lo->rebuild,
+       fappend("HW-power-control enabled: %d\n",
                dev->phy.hardware_power_control);
-       fappend("TX Bias: 0x%02X,  TX Magn: 0x%02X\n",
-               lo->tx_bias, lo->tx_magn);
-       fappend("Power Vector: 0x%08X%08X\n",
+       fappend("TX Bias: 0x%02X,  TX Magn: 0x%02X  (expire in %lu sec)\n",
+               lo->tx_bias, lo->tx_magn,
+               calc_expire_secs(now, lo->txctl_measured_time,
+                                B43_LO_TXCTL_EXPIRE));
+       fappend("Power Vector: 0x%08X%08X  (expires in %lu sec)\n",
                (unsigned int)((lo->power_vector & 0xFFFFFFFF00000000ULL) >> 32),
-               (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL));
-       fappend("\nControl table WITH PADMIX:\n");
-       count = append_lo_table(count, buf, bufsize, lo->with_padmix);
-       fappend("\nControl table WITHOUT PADMIX:\n");
-       count = append_lo_table(count, buf, bufsize, lo->no_padmix);
+               (unsigned int)(lo->power_vector & 0x00000000FFFFFFFFULL),
+               calc_expire_secs(now, lo->pwr_vec_read_time,
+                                B43_LO_PWRVEC_EXPIRE));
+
+       fappend("\nCalibrated settings:\n");
+       list_for_each_entry(cal, &lo->calib_list, list) {
+               bool active;
+
+               active = (b43_compare_bbatt(&cal->bbatt, &phy->bbatt) &&
+                         b43_compare_rfatt(&cal->rfatt, &phy->rfatt));
+               fappend("BB(%d), RF(%d,%d)  ->  I=%d, Q=%d  "
+                       "(expires in %lu sec)%s\n",
+                       cal->bbatt.att,
+                       cal->rfatt.att, cal->rfatt.with_padmix,
+                       cal->ctl.i, cal->ctl.q,
+                       calc_expire_secs(now, cal->calib_time,
+                                        B43_LO_CALIB_EXPIRE),
+                       active ? "  ACTIVE" : "");
+       }
+
        fappend("\nUsed RF attenuation values:  Value(WithPadmix flag)\n");
        for (i = 0; i < lo->rfatt_list.len; i++) {
                fappend("%u(%d), ",
@@ -351,7 +367,7 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
        struct b43_dfs_file *dfile;
        ssize_t uninitialized_var(ret);
        char *buf;
-       const size_t bufsize = 1024 * 128;
+       const size_t bufsize = 1024 * 16; /* 16 kiB buffer */
        const size_t buforder = get_order(bufsize);
        int err = 0;
 
@@ -380,8 +396,6 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
                        err = -ENOMEM;
                        goto out_unlock;
                }
-               /* Sparse warns about the following memset, because it has a big
-                * size value. That warning is bogus, so I will ignore it. --mb */
                memset(buf, 0, bufsize);
                if (dfops->take_irqlock) {
                        spin_lock_irq(&dev->wl->irq_lock);
@@ -523,6 +537,7 @@ static void b43_add_dynamic_debug(struct b43_wldev *dev)
        add_dyn_dbg("debug_dmaverbose", B43_DBG_DMAVERBOSE, 0);
        add_dyn_dbg("debug_pwork_fast", B43_DBG_PWORK_FAST, 0);
        add_dyn_dbg("debug_pwork_stop", B43_DBG_PWORK_STOP, 0);
+       add_dyn_dbg("debug_lo", B43_DBG_LO, 0);
 
 #undef add_dyn_dbg
 }
@@ -618,6 +633,7 @@ void b43_debugfs_remove_device(struct b43_wldev *dev)
        kfree(e);
 }
 
+/* Called with IRQs disabled. */
 void b43_debugfs_log_txstat(struct b43_wldev *dev,
                            const struct b43_txstatus *status)
 {
@@ -629,8 +645,7 @@ void b43_debugfs_log_txstat(struct b43_wldev *dev,
        if (!e)
                return;
        log = &e->txstatlog;
-       B43_WARN_ON(!irqs_disabled());
-       spin_lock(&log->lock);
+       spin_lock(&log->lock); /* IRQs are already disabled. */
        i = log->end + 1;
        if (i == B43_NR_LOGGED_TXSTATUS)
                i = 0;
index 6eebe85..c75cff4 100644 (file)
@@ -10,6 +10,7 @@ enum b43_dyndbg {             /* Dynamic debugging features */
        B43_DBG_DMAVERBOSE,
        B43_DBG_PWORK_FAST,
        B43_DBG_PWORK_STOP,
+       B43_DBG_LO,
        __B43_NR_DYNDBG,
 };
 
index 3dfb28a..b4eadd9 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/delay.h>
 #include <linux/skbuff.h>
 #include <linux/etherdevice.h>
+#include <asm/div64.h>
 
 
 /* 32bit DMA ops. */
@@ -291,52 +292,6 @@ static inline int request_slot(struct b43_dmaring *ring)
        return slot;
 }
 
-/* Mac80211-queue to b43-ring mapping */
-static struct b43_dmaring *priority_to_txring(struct b43_wldev *dev,
-                                             int queue_priority)
-{
-       struct b43_dmaring *ring;
-
-/*FIXME: For now we always run on TX-ring-1 */
-       return dev->dma.tx_ring1;
-
-       /* 0 = highest priority */
-       switch (queue_priority) {
-       default:
-               B43_WARN_ON(1);
-               /* fallthrough */
-       case 0:
-               ring = dev->dma.tx_ring3;
-               break;
-       case 1:
-               ring = dev->dma.tx_ring2;
-               break;
-       case 2:
-               ring = dev->dma.tx_ring1;
-               break;
-       case 3:
-               ring = dev->dma.tx_ring0;
-               break;
-       }
-
-       return ring;
-}
-
-/* b43-ring to mac80211-queue mapping */
-static inline int txring_to_priority(struct b43_dmaring *ring)
-{
-       static const u8 idx_to_prio[] = { 3, 2, 1, 0, };
-       unsigned int index;
-
-/*FIXME: have only one queue, for now */
-       return 0;
-
-       index = ring->index;
-       if (B43_WARN_ON(index >= ARRAY_SIZE(idx_to_prio)))
-               index = 0;
-       return idx_to_prio[index];
-}
-
 static u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx)
 {
        static const u16 map64[] = {
@@ -373,10 +328,10 @@ static inline
        dma_addr_t dmaaddr;
 
        if (tx) {
-               dmaaddr = dma_map_single(ring->dev->dev->dev,
+               dmaaddr = dma_map_single(ring->dev->dev->dma_dev,
                                         buf, len, DMA_TO_DEVICE);
        } else {
-               dmaaddr = dma_map_single(ring->dev->dev->dev,
+               dmaaddr = dma_map_single(ring->dev->dev->dma_dev,
                                         buf, len, DMA_FROM_DEVICE);
        }
 
@@ -388,9 +343,10 @@ static inline
                          dma_addr_t addr, size_t len, int tx)
 {
        if (tx) {
-               dma_unmap_single(ring->dev->dev->dev, addr, len, DMA_TO_DEVICE);
+               dma_unmap_single(ring->dev->dev->dma_dev,
+                                addr, len, DMA_TO_DEVICE);
        } else {
-               dma_unmap_single(ring->dev->dev->dev,
+               dma_unmap_single(ring->dev->dev->dma_dev,
                                 addr, len, DMA_FROM_DEVICE);
        }
 }
@@ -400,7 +356,7 @@ static inline
                                 dma_addr_t addr, size_t len)
 {
        B43_WARN_ON(ring->tx);
-       dma_sync_single_for_cpu(ring->dev->dev->dev,
+       dma_sync_single_for_cpu(ring->dev->dev->dma_dev,
                                addr, len, DMA_FROM_DEVICE);
 }
 
@@ -409,7 +365,7 @@ static inline
                                    dma_addr_t addr, size_t len)
 {
        B43_WARN_ON(ring->tx);
-       dma_sync_single_for_device(ring->dev->dev->dev,
+       dma_sync_single_for_device(ring->dev->dev->dma_dev,
                                   addr, len, DMA_FROM_DEVICE);
 }
 
@@ -425,7 +381,7 @@ static inline
 
 static int alloc_ringmemory(struct b43_dmaring *ring)
 {
-       struct device *dev = ring->dev->dev->dev;
+       struct device *dma_dev = ring->dev->dev->dma_dev;
        gfp_t flags = GFP_KERNEL;
 
        /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
@@ -439,7 +395,7 @@ static int alloc_ringmemory(struct b43_dmaring *ring)
         */
        if (ring->type == B43_DMA_64BIT)
                flags |= GFP_DMA;
-       ring->descbase = dma_alloc_coherent(dev, B43_DMA_RINGMEMSIZE,
+       ring->descbase = dma_alloc_coherent(dma_dev, B43_DMA_RINGMEMSIZE,
                                            &(ring->dmabase), flags);
        if (!ring->descbase) {
                b43err(ring->dev->wl, "DMA ringmemory allocation failed\n");
@@ -452,9 +408,9 @@ static int alloc_ringmemory(struct b43_dmaring *ring)
 
 static void free_ringmemory(struct b43_dmaring *ring)
 {
-       struct device *dev = ring->dev->dev->dev;
+       struct device *dma_dev = ring->dev->dev->dma_dev;
 
-       dma_free_coherent(dev, B43_DMA_RINGMEMSIZE,
+       dma_free_coherent(dma_dev, B43_DMA_RINGMEMSIZE,
                          ring->descbase, ring->dmabase);
 }
 
@@ -560,7 +516,7 @@ static int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base,
 /* Check if a DMA mapping address is invalid. */
 static bool b43_dma_mapping_error(struct b43_dmaring *ring,
                                  dma_addr_t addr,
-                                 size_t buffersize)
+                                 size_t buffersize, bool dma_to_device)
 {
        if (unlikely(dma_mapping_error(addr)))
                return 1;
@@ -568,11 +524,11 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring,
        switch (ring->type) {
        case B43_DMA_30BIT:
                if ((u64)addr + buffersize > (1ULL << 30))
-                       return 1;
+                       goto address_error;
                break;
        case B43_DMA_32BIT:
                if ((u64)addr + buffersize > (1ULL << 32))
-                       return 1;
+                       goto address_error;
                break;
        case B43_DMA_64BIT:
                /* Currently we can't have addresses beyond
@@ -582,6 +538,12 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring,
 
        /* The address is OK. */
        return 0;
+
+address_error:
+       /* We can't support this address. Unmap it again. */
+       unmap_descbuffer(ring, addr, buffersize, dma_to_device);
+
+       return 1;
 }
 
 static int setup_rx_descbuffer(struct b43_dmaring *ring,
@@ -589,7 +551,6 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring,
                               struct b43_dmadesc_meta *meta, gfp_t gfp_flags)
 {
        struct b43_rxhdr_fw4 *rxhdr;
-       struct b43_hwtxstatus *txstat;
        dma_addr_t dmaaddr;
        struct sk_buff *skb;
 
@@ -599,7 +560,7 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring,
        if (unlikely(!skb))
                return -ENOMEM;
        dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0);
-       if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize)) {
+       if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) {
                /* ugh. try to realloc in zone_dma */
                gfp_flags |= GFP_DMA;
 
@@ -612,7 +573,8 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring,
                                         ring->rx_buffersize, 0);
        }
 
-       if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize)) {
+       if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) {
+               b43err(ring->dev->wl, "RX DMA buffer allocation failed\n");
                dev_kfree_skb_any(skb);
                return -EIO;
        }
@@ -624,8 +586,6 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring,
 
        rxhdr = (struct b43_rxhdr_fw4 *)(skb->data);
        rxhdr->frame_len = 0;
-       txstat = (struct b43_hwtxstatus *)(skb->data);
-       txstat->cookie = 0;
 
        return 0;
 }
@@ -814,6 +774,18 @@ static u64 supported_dma_mask(struct b43_wldev *dev)
        return DMA_30BIT_MASK;
 }
 
+static enum b43_dmatype dma_mask_to_engine_type(u64 dmamask)
+{
+       if (dmamask == DMA_30BIT_MASK)
+               return B43_DMA_30BIT;
+       if (dmamask == DMA_32BIT_MASK)
+               return B43_DMA_32BIT;
+       if (dmamask == DMA_64BIT_MASK)
+               return B43_DMA_64BIT;
+       B43_WARN_ON(1);
+       return B43_DMA_30BIT;
+}
+
 /* Main initialization function. */
 static
 struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
@@ -847,12 +819,13 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
                        goto err_kfree_meta;
 
                /* test for ability to dma to txhdr_cache */
-               dma_test = dma_map_single(dev->dev->dev,
+               dma_test = dma_map_single(dev->dev->dma_dev,
                                          ring->txhdr_cache,
                                          b43_txhdr_size(dev),
                                          DMA_TO_DEVICE);
 
-               if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev))) {
+               if (b43_dma_mapping_error(ring, dma_test,
+                                         b43_txhdr_size(dev), 1)) {
                        /* ugh realloc */
                        kfree(ring->txhdr_cache);
                        ring->txhdr_cache = kcalloc(nr_slots,
@@ -861,17 +834,21 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
                        if (!ring->txhdr_cache)
                                goto err_kfree_meta;
 
-                       dma_test = dma_map_single(dev->dev->dev,
+                       dma_test = dma_map_single(dev->dev->dma_dev,
                                                  ring->txhdr_cache,
                                                  b43_txhdr_size(dev),
                                                  DMA_TO_DEVICE);
 
                        if (b43_dma_mapping_error(ring, dma_test,
-                                                 b43_txhdr_size(dev)))
+                                                 b43_txhdr_size(dev), 1)) {
+
+                               b43err(dev->wl,
+                                      "TXHDR DMA allocation failed\n");
                                goto err_kfree_txhdr_cache;
+                       }
                }
 
-               dma_unmap_single(dev->dev->dev,
+               dma_unmap_single(dev->dev->dma_dev,
                                 dma_test, b43_txhdr_size(dev),
                                 DMA_TO_DEVICE);
        }
@@ -924,16 +901,52 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
        goto out;
 }
 
+#define divide(a, b)   ({      \
+       typeof(a) __a = a;      \
+       do_div(__a, b);         \
+       __a;                    \
+  })
+
+#define modulo(a, b)   ({      \
+       typeof(a) __a = a;      \
+       do_div(__a, b);         \
+  })
+
 /* Main cleanup function. */
-static void b43_destroy_dmaring(struct b43_dmaring *ring)
+static void b43_destroy_dmaring(struct b43_dmaring *ring,
+                               const char *ringname)
 {
        if (!ring)
                return;
 
-       b43dbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots: %d/%d\n",
-              (unsigned int)(ring->type),
-              ring->mmio_base,
-              (ring->tx) ? "TX" : "RX", ring->max_used_slots, ring->nr_slots);
+#ifdef CONFIG_B43_DEBUG
+       {
+               /* Print some statistics. */
+               u64 failed_packets = ring->nr_failed_tx_packets;
+               u64 succeed_packets = ring->nr_succeed_tx_packets;
+               u64 nr_packets = failed_packets + succeed_packets;
+               u64 permille_failed = 0, average_tries = 0;
+
+               if (nr_packets)
+                       permille_failed = divide(failed_packets * 1000, nr_packets);
+               if (nr_packets)
+                       average_tries = divide(ring->nr_total_packet_tries * 100, nr_packets);
+
+               b43dbg(ring->dev->wl, "DMA-%u %s: "
+                      "Used slots %d/%d, Failed frames %llu/%llu = %llu.%01llu%%, "
+                      "Average tries %llu.%02llu\n",
+                      (unsigned int)(ring->type), ringname,
+                      ring->max_used_slots,
+                      ring->nr_slots,
+                      (unsigned long long)failed_packets,
+                      (unsigned long long)nr_packets,
+                      (unsigned long long)divide(permille_failed, 10),
+                      (unsigned long long)modulo(permille_failed, 10),
+                      (unsigned long long)divide(average_tries, 100),
+                      (unsigned long long)modulo(average_tries, 100));
+       }
+#endif /* DEBUG */
+
        /* Device IRQs are disabled prior entering this function,
         * so no need to take care of concurrency with rx handler stuff.
         */
@@ -946,139 +959,129 @@ static void b43_destroy_dmaring(struct b43_dmaring *ring)
        kfree(ring);
 }
 
+#define destroy_ring(dma, ring) do {                           \
+       b43_destroy_dmaring((dma)->ring, __stringify(ring));    \
+       (dma)->ring = NULL;                                     \
+    } while (0)
+
 void b43_dma_free(struct b43_wldev *dev)
 {
-       struct b43_dma *dma = &dev->dma;
+       struct b43_dma *dma;
 
-       b43_destroy_dmaring(dma->rx_ring3);
-       dma->rx_ring3 = NULL;
-       b43_destroy_dmaring(dma->rx_ring0);
-       dma->rx_ring0 = NULL;
-
-       b43_destroy_dmaring(dma->tx_ring5);
-       dma->tx_ring5 = NULL;
-       b43_destroy_dmaring(dma->tx_ring4);
-       dma->tx_ring4 = NULL;
-       b43_destroy_dmaring(dma->tx_ring3);
-       dma->tx_ring3 = NULL;
-       b43_destroy_dmaring(dma->tx_ring2);
-       dma->tx_ring2 = NULL;
-       b43_destroy_dmaring(dma->tx_ring1);
-       dma->tx_ring1 = NULL;
-       b43_destroy_dmaring(dma->tx_ring0);
-       dma->tx_ring0 = NULL;
+       if (b43_using_pio_transfers(dev))
+               return;
+       dma = &dev->dma;
+
+       destroy_ring(dma, rx_ring);
+       destroy_ring(dma, tx_ring_AC_BK);
+       destroy_ring(dma, tx_ring_AC_BE);
+       destroy_ring(dma, tx_ring_AC_VI);
+       destroy_ring(dma, tx_ring_AC_VO);
+       destroy_ring(dma, tx_ring_mcast);
+}
+
+static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask)
+{
+       u64 orig_mask = mask;
+       bool fallback = 0;
+       int err;
+
+       /* Try to set the DMA mask. If it fails, try falling back to a
+        * lower mask, as we can always also support a lower one. */
+       while (1) {
+               err = ssb_dma_set_mask(dev->dev, mask);
+               if (!err)
+                       break;
+               if (mask == DMA_64BIT_MASK) {
+                       mask = DMA_32BIT_MASK;
+                       fallback = 1;
+                       continue;
+               }
+               if (mask == DMA_32BIT_MASK) {
+                       mask = DMA_30BIT_MASK;
+                       fallback = 1;
+                       continue;
+               }
+               b43err(dev->wl, "The machine/kernel does not support "
+                      "the required %u-bit DMA mask\n",
+                      (unsigned int)dma_mask_to_engine_type(orig_mask));
+               return -EOPNOTSUPP;
+       }
+       if (fallback) {
+               b43info(dev->wl, "DMA mask fallback from %u-bit to %u-bit\n",
+                       (unsigned int)dma_mask_to_engine_type(orig_mask),
+                       (unsigned int)dma_mask_to_engine_type(mask));
+       }
+
+       return 0;
 }
 
 int b43_dma_init(struct b43_wldev *dev)
 {
        struct b43_dma *dma = &dev->dma;
-       struct b43_dmaring *ring;
        int err;
        u64 dmamask;
        enum b43_dmatype type;
 
        dmamask = supported_dma_mask(dev);
-       switch (dmamask) {
-       default:
-               B43_WARN_ON(1);
-       case DMA_30BIT_MASK:
-               type = B43_DMA_30BIT;
-               break;
-       case DMA_32BIT_MASK:
-               type = B43_DMA_32BIT;
-               break;
-       case DMA_64BIT_MASK:
-               type = B43_DMA_64BIT;
-               break;
-       }
-       err = ssb_dma_set_mask(dev->dev, dmamask);
-       if (err) {
-               b43err(dev->wl, "The machine/kernel does not support "
-                      "the required DMA mask (0x%08X%08X)\n",
-                      (unsigned int)((dmamask & 0xFFFFFFFF00000000ULL) >> 32),
-                      (unsigned int)(dmamask & 0x00000000FFFFFFFFULL));
-               return -EOPNOTSUPP;
-       }
+       type = dma_mask_to_engine_type(dmamask);
+       err = b43_dma_set_mask(dev, dmamask);
+       if (err)
+               return err;
 
        err = -ENOMEM;
        /* setup TX DMA channels. */
-       ring = b43_setup_dmaring(dev, 0, 1, type);
-       if (!ring)
+       dma->tx_ring_AC_BK = b43_setup_dmaring(dev, 0, 1, type);
+       if (!dma->tx_ring_AC_BK)
                goto out;
-       dma->tx_ring0 = ring;
 
-       ring = b43_setup_dmaring(dev, 1, 1, type);
-       if (!ring)
-               goto err_destroy_tx0;
-       dma->tx_ring1 = ring;
+       dma->tx_ring_AC_BE = b43_setup_dmaring(dev, 1, 1, type);
+       if (!dma->tx_ring_AC_BE)
+               goto err_destroy_bk;
 
-       ring = b43_setup_dmaring(dev, 2, 1, type);
-       if (!ring)
-               goto err_destroy_tx1;
-       dma->tx_ring2 = ring;
+       dma->tx_ring_AC_VI = b43_setup_dmaring(dev, 2, 1, type);
+       if (!dma->tx_ring_AC_VI)
+               goto err_destroy_be;
 
-       ring = b43_setup_dmaring(dev, 3, 1, type);
-       if (!ring)
-               goto err_destroy_tx2;
-       dma->tx_ring3 = ring;
+       dma->tx_ring_AC_VO = b43_setup_dmaring(dev, 3, 1, type);
+       if (!dma->tx_ring_AC_VO)
+               goto err_destroy_vi;
 
-       ring = b43_setup_dmaring(dev, 4, 1, type);
-       if (!ring)
-               goto err_destroy_tx3;
-       dma->tx_ring4 = ring;
+       dma->tx_ring_mcast = b43_setup_dmaring(dev, 4, 1, type);
+       if (!dma->tx_ring_mcast)
+               goto err_destroy_vo;
 
-       ring = b43_setup_dmaring(dev, 5, 1, type);
-       if (!ring)
-               goto err_destroy_tx4;
-       dma->tx_ring5 = ring;
+       /* setup RX DMA channel. */
+       dma->rx_ring = b43_setup_dmaring(dev, 0, 0, type);
+       if (!dma->rx_ring)
+               goto err_destroy_mcast;
 
-       /* setup RX DMA channels. */
-       ring = b43_setup_dmaring(dev, 0, 0, type);
-       if (!ring)
-               goto err_destroy_tx5;
-       dma->rx_ring0 = ring;
-
-       if (dev->dev->id.revision < 5) {
-               ring = b43_setup_dmaring(dev, 3, 0, type);
-               if (!ring)
-                       goto err_destroy_rx0;
-               dma->rx_ring3 = ring;
-       }
+       /* No support for the TX status DMA ring. */
+       B43_WARN_ON(dev->dev->id.revision < 5);
 
        b43dbg(dev->wl, "%u-bit DMA initialized\n",
               (unsigned int)type);
        err = 0;
-      out:
+out:
        return err;
 
-      err_destroy_rx0:
-       b43_destroy_dmaring(dma->rx_ring0);
-       dma->rx_ring0 = NULL;
-      err_destroy_tx5:
-       b43_destroy_dmaring(dma->tx_ring5);
-       dma->tx_ring5 = NULL;
-      err_destroy_tx4:
-       b43_destroy_dmaring(dma->tx_ring4);
-       dma->tx_ring4 = NULL;
-      err_destroy_tx3:
-       b43_destroy_dmaring(dma->tx_ring3);
-       dma->tx_ring3 = NULL;
-      err_destroy_tx2:
-       b43_destroy_dmaring(dma->tx_ring2);
-       dma->tx_ring2 = NULL;
-      err_destroy_tx1:
-       b43_destroy_dmaring(dma->tx_ring1);
-       dma->tx_ring1 = NULL;
-      err_destroy_tx0:
-       b43_destroy_dmaring(dma->tx_ring0);
-       dma->tx_ring0 = NULL;
-       goto out;
+err_destroy_mcast:
+       destroy_ring(dma, tx_ring_mcast);
+err_destroy_vo:
+       destroy_ring(dma, tx_ring_AC_VO);
+err_destroy_vi:
+       destroy_ring(dma, tx_ring_AC_VI);
+err_destroy_be:
+       destroy_ring(dma, tx_ring_AC_BE);
+err_destroy_bk:
+       destroy_ring(dma, tx_ring_AC_BK);
+       return err;
 }
 
 /* Generate a cookie for the TX header. */
 static u16 generate_cookie(struct b43_dmaring *ring, int slot)
 {
-       u16 cookie = 0x1000;
+       u16 cookie;
 
        /* Use the upper 4 bits of the cookie as
         * DMA controller ID and store the slot number
@@ -1088,30 +1091,9 @@ static u16 generate_cookie(struct b43_dmaring *ring, int slot)
         * It can also not be 0xFFFF because that is special
         * for multicast frames.
         */
-       switch (ring->index) {
-       case 0:
-               cookie = 0x1000;
-               break;
-       case 1:
-               cookie = 0x2000;
-               break;
-       case 2:
-               cookie = 0x3000;
-               break;
-       case 3:
-               cookie = 0x4000;
-               break;
-       case 4:
-               cookie = 0x5000;
-               break;
-       case 5:
-               cookie = 0x6000;
-               break;
-       default:
-               B43_WARN_ON(1);
-       }
+       cookie = (((u16)ring->index + 1) << 12);
        B43_WARN_ON(slot & ~0x0FFF);
-       cookie |= (u16) slot;
+       cookie |= (u16)slot;
 
        return cookie;
 }
@@ -1125,22 +1107,19 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot)
 
        switch (cookie & 0xF000) {
        case 0x1000:
-               ring = dma->tx_ring0;
+               ring = dma->tx_ring_AC_BK;
                break;
        case 0x2000:
-               ring = dma->tx_ring1;
+               ring = dma->tx_ring_AC_BE;
                break;
        case 0x3000:
-               ring = dma->tx_ring2;
+               ring = dma->tx_ring_AC_VI;
                break;
        case 0x4000:
-               ring = dma->tx_ring3;
+               ring = dma->tx_ring_AC_VO;
                break;
        case 0x5000:
-               ring = dma->tx_ring4;
-               break;
-       case 0x6000:
-               ring = dma->tx_ring5;
+               ring = dma->tx_ring_mcast;
                break;
        default:
                B43_WARN_ON(1);
@@ -1152,10 +1131,10 @@ struct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot)
 }
 
 static int dma_tx_fragment(struct b43_dmaring *ring,
-                          struct sk_buff *skb,
-                          struct ieee80211_tx_control *ctl)
+                          struct sk_buff *skb)
 {
        const struct b43_dma_ops *ops = ring->ops;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        u8 *header;
        int slot, old_top_slot, old_used_slots;
        int err;
@@ -1167,7 +1146,6 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
        size_t hdrsize = b43_txhdr_size(ring->dev);
 
 #define SLOTS_PER_PACKET  2
-       B43_WARN_ON(skb_shinfo(skb)->nr_frags);
 
        old_top_slot = ring->current_slot;
        old_used_slots = ring->used_slots;
@@ -1180,7 +1158,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
        header = &(ring->txhdr_cache[slot * hdrsize]);
        cookie = generate_cookie(ring, slot);
        err = b43_generate_txhdr(ring->dev, header,
-                                skb->data, skb->len, ctl, cookie);
+                                skb->data, skb->len, info, cookie);
        if (unlikely(err)) {
                ring->current_slot = old_top_slot;
                ring->used_slots = old_used_slots;
@@ -1189,7 +1167,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
 
        meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header,
                                           hdrsize, 1);
-       if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize)) {
+       if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize, 1)) {
                ring->current_slot = old_top_slot;
                ring->used_slots = old_used_slots;
                return -EIO;
@@ -1202,13 +1180,12 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
        desc = ops->idx2desc(ring, slot, &meta);
        memset(meta, 0, sizeof(*meta));
 
-       memcpy(&meta->txstat.control, ctl, sizeof(*ctl));
        meta->skb = skb;
        meta->is_last_fragment = 1;
 
        meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
        /* create a bounce buffer in zone_dma on mapping failure. */
-       if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len)) {
+       if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
                bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
                if (!bounce_skb) {
                        ring->current_slot = old_top_slot;
@@ -1222,7 +1199,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
                skb = bounce_skb;
                meta->skb = skb;
                meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
-               if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len)) {
+               if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) {
                        ring->current_slot = old_top_slot;
                        ring->used_slots = old_used_slots;
                        err = -EIO;
@@ -1232,7 +1209,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
 
        ops->fill_descriptor(ring, desc, meta->dmaaddr, skb->len, 0, 1, 1);
 
-       if (ctl->flags & IEEE80211_TXCTL_SEND_AFTER_DTIM) {
+       if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
                /* Tell the firmware about the cookie of the last
                 * mcast frame, so it can clear the more-data bit in it. */
                b43_shm_write16(ring->dev, B43_SHM_SHARED,
@@ -1272,29 +1249,56 @@ static inline int should_inject_overflow(struct b43_dmaring *ring)
        return 0;
 }
 
-int b43_dma_tx(struct b43_wldev *dev,
-              struct sk_buff *skb, struct ieee80211_tx_control *ctl)
+/* Static mapping of mac80211's queues (priorities) to b43 DMA rings. */
+static struct b43_dmaring * select_ring_by_priority(struct b43_wldev *dev,
+                                                   u8 queue_prio)
+{
+       struct b43_dmaring *ring;
+
+       if (b43_modparam_qos) {
+               /* 0 = highest priority */
+               switch (queue_prio) {
+               default:
+                       B43_WARN_ON(1);
+                       /* fallthrough */
+               case 0:
+                       ring = dev->dma.tx_ring_AC_VO;
+                       break;
+               case 1:
+                       ring = dev->dma.tx_ring_AC_VI;
+                       break;
+               case 2:
+                       ring = dev->dma.tx_ring_AC_BE;
+                       break;
+               case 3:
+                       ring = dev->dma.tx_ring_AC_BK;
+                       break;
+               }
+       } else
+               ring = dev->dma.tx_ring_AC_BE;
+
+       return ring;
+}
+
+int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb)
 {
        struct b43_dmaring *ring;
        struct ieee80211_hdr *hdr;
        int err = 0;
        unsigned long flags;
-
-       if (unlikely(skb->len < 2 + 2 + 6)) {
-               /* Too short, this can't be a valid frame. */
-               return -EINVAL;
-       }
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
        hdr = (struct ieee80211_hdr *)skb->data;
-       if (ctl->flags & IEEE80211_TXCTL_SEND_AFTER_DTIM) {
+       if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
                /* The multicast ring will be sent after the DTIM */
-               ring = dev->dma.tx_ring4;
+               ring = dev->dma.tx_ring_mcast;
                /* Set the more-data bit. Ucode will clear it on
                 * the last frame for us. */
                hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
        } else {
                /* Decide by priority where to put this frame. */
-               ring = priority_to_txring(dev, ctl->queue);
+               ring = select_ring_by_priority(
+                       dev, skb_get_queue_mapping(skb));
        }
 
        spin_lock_irqsave(&ring->lock, flags);
@@ -1309,7 +1313,12 @@ int b43_dma_tx(struct b43_wldev *dev,
         * That would be a mac80211 bug. */
        B43_WARN_ON(ring->stopped);
 
-       err = dma_tx_fragment(ring, skb, ctl);
+       /* Assign the queue number to the ring (if not already done before)
+        * so TX status handling can use it. The queue to ring mapping is
+        * static, so we don't need to store it per frame. */
+       ring->queue_prio = skb_get_queue_mapping(skb);
+
+       err = dma_tx_fragment(ring, skb);
        if (unlikely(err == -ENOKEY)) {
                /* Drop this packet, as we don't have the encryption key
                 * anymore and must not transmit it unencrypted. */
@@ -1325,7 +1334,7 @@ int b43_dma_tx(struct b43_wldev *dev,
        if ((free_slots(ring) < SLOTS_PER_PACKET) ||
            should_inject_overflow(ring)) {
                /* This TX ring is full. */
-               ieee80211_stop_queue(dev->wl->hw, txring_to_priority(ring));
+               ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
                ring->stopped = 1;
                if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
                        b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
@@ -1337,6 +1346,7 @@ out_unlock:
        return err;
 }
 
+/* Called with IRQs disabled. */
 void b43_dma_handle_txstatus(struct b43_wldev *dev,
                             const struct b43_txstatus *status)
 {
@@ -1345,12 +1355,13 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
        struct b43_dmadesc_generic *desc;
        struct b43_dmadesc_meta *meta;
        int slot;
+       bool frame_succeed;
 
        ring = parse_cookie(dev, status->cookie, &slot);
        if (unlikely(!ring))
                return;
-       B43_WARN_ON(!irqs_disabled());
-       spin_lock(&ring->lock);
+
+       spin_lock(&ring->lock); /* IRQs are already disabled. */
 
        B43_WARN_ON(!ring->tx);
        ops = ring->ops;
@@ -1366,25 +1377,28 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
                                         b43_txhdr_size(dev), 1);
 
                if (meta->is_last_fragment) {
-                       B43_WARN_ON(!meta->skb);
-                       /* Call back to inform the ieee80211 subsystem about the
-                        * status of the transmission.
-                        * Some fields of txstat are already filled in dma_tx().
+                       struct ieee80211_tx_info *info;
+
+                       BUG_ON(!meta->skb);
+
+                       info = IEEE80211_SKB_CB(meta->skb);
+
+                       memset(&info->status, 0, sizeof(info->status));
+
+                       /*
+                        * Call back to inform the ieee80211 subsystem about
+                        * the status of the transmission.
                         */
-                       if (status->acked) {
-                               meta->txstat.flags |= IEEE80211_TX_STATUS_ACK;
-                       } else {
-                               if (!(meta->txstat.control.flags
-                                     & IEEE80211_TXCTL_NO_ACK))
-                                       meta->txstat.excessive_retries = 1;
-                       }
-                       if (status->frame_count == 0) {
-                               /* The frame was not transmitted at all. */
-                               meta->txstat.retry_count = 0;
-                       } else
-                               meta->txstat.retry_count = status->frame_count - 1;
-                       ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb,
-                                                   &(meta->txstat));
+                       frame_succeed = b43_fill_txstatus_report(info, status);
+#ifdef CONFIG_B43_DEBUG
+                       if (frame_succeed)
+                               ring->nr_succeed_tx_packets++;
+                       else
+                               ring->nr_failed_tx_packets++;
+                       ring->nr_total_packet_tries += status->frame_count;
+#endif /* DEBUG */
+                       ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb);
+
                        /* skb is freed by ieee80211_tx_status_irqsafe() */
                        meta->skb = NULL;
                } else {
@@ -1404,7 +1418,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
        dev->stats.last_tx = jiffies;
        if (ring->stopped) {
                B43_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET);
-               ieee80211_wake_queue(dev->wl->hw, txring_to_priority(ring));
+               ieee80211_wake_queue(dev->wl->hw, ring->queue_prio);
                ring->stopped = 0;
                if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
                        b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index);
@@ -1419,18 +1433,16 @@ void b43_dma_get_tx_stats(struct b43_wldev *dev,
 {
        const int nr_queues = dev->wl->hw->queues;
        struct b43_dmaring *ring;
-       struct ieee80211_tx_queue_stats_data *data;
        unsigned long flags;
        int i;
 
        for (i = 0; i < nr_queues; i++) {
-               data = &(stats->data[i]);
-               ring = priority_to_txring(dev, i);
+               ring = select_ring_by_priority(dev, i);
 
                spin_lock_irqsave(&ring->lock, flags);
-               data->len = ring->used_slots / SLOTS_PER_PACKET;
-               data->limit = ring->nr_slots / SLOTS_PER_PACKET;
-               data->count = ring->nr_tx_packets;
+               stats[i].len = ring->used_slots / SLOTS_PER_PACKET;
+               stats[i].limit = ring->nr_slots / SLOTS_PER_PACKET;
+               stats[i].count = ring->nr_tx_packets;
                spin_unlock_irqrestore(&ring->lock, flags);
        }
 }
@@ -1451,25 +1463,6 @@ static void dma_rx(struct b43_dmaring *ring, int *slot)
        sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);
        skb = meta->skb;
 
-       if (ring->index == 3) {
-               /* We received an xmit status. */
-               struct b43_hwtxstatus *hw = (struct b43_hwtxstatus *)skb->data;
-               int i = 0;
-
-               while (hw->cookie == 0) {
-                       if (i > 100)
-                               break;
-                       i++;
-                       udelay(2);
-                       barrier();
-               }
-               b43_handle_hwtxstatus(ring->dev, hw);
-               /* recycle the descriptor buffer. */
-               sync_descbuffer_for_device(ring, meta->dmaaddr,
-                                          ring->rx_buffersize);
-
-               return;
-       }
        rxhdr = (struct b43_rxhdr_fw4 *)skb->data;
        len = le16_to_cpu(rxhdr->frame_len);
        if (len == 0) {
@@ -1526,7 +1519,7 @@ static void dma_rx(struct b43_dmaring *ring, int *slot)
        skb_pull(skb, ring->frameoffset);
 
        b43_rx(ring->dev, skb, rxhdr);
-      drop:
+drop:
        return;
 }
 
@@ -1572,21 +1565,55 @@ static void b43_dma_tx_resume_ring(struct b43_dmaring *ring)
 void b43_dma_tx_suspend(struct b43_wldev *dev)
 {
        b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
-       b43_dma_tx_suspend_ring(dev->dma.tx_ring0);
-       b43_dma_tx_suspend_ring(dev->dma.tx_ring1);
-       b43_dma_tx_suspend_ring(dev->dma.tx_ring2);
-       b43_dma_tx_suspend_ring(dev->dma.tx_ring3);
-       b43_dma_tx_suspend_ring(dev->dma.tx_ring4);
-       b43_dma_tx_suspend_ring(dev->dma.tx_ring5);
+       b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BK);
+       b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BE);
+       b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VI);
+       b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VO);
+       b43_dma_tx_suspend_ring(dev->dma.tx_ring_mcast);
 }
 
 void b43_dma_tx_resume(struct b43_wldev *dev)
 {
-       b43_dma_tx_resume_ring(dev->dma.tx_ring5);
-       b43_dma_tx_resume_ring(dev->dma.tx_ring4);
-       b43_dma_tx_resume_ring(dev->dma.tx_ring3);
-       b43_dma_tx_resume_ring(dev->dma.tx_ring2);
-       b43_dma_tx_resume_ring(dev->dma.tx_ring1);
-       b43_dma_tx_resume_ring(dev->dma.tx_ring0);
+       b43_dma_tx_resume_ring(dev->dma.tx_ring_mcast);
+       b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VO);
+       b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VI);
+       b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BE);
+       b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK);
        b43_power_saving_ctl_bits(dev, 0);
 }
+
+#ifdef CONFIG_B43_PIO
+static void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type,
+                          u16 mmio_base, bool enable)
+{
+       u32 ctl;
+
+       if (type == B43_DMA_64BIT) {
+               ctl = b43_read32(dev, mmio_base + B43_DMA64_RXCTL);
+               ctl &= ~B43_DMA64_RXDIRECTFIFO;
+               if (enable)
+                       ctl |= B43_DMA64_RXDIRECTFIFO;
+               b43_write32(dev, mmio_base + B43_DMA64_RXCTL, ctl);
+       } else {
+               ctl = b43_read32(dev, mmio_base + B43_DMA32_RXCTL);
+               ctl &= ~B43_DMA32_RXDIRECTFIFO;
+               if (enable)
+                       ctl |= B43_DMA32_RXDIRECTFIFO;
+               b43_write32(dev, mmio_base + B43_DMA32_RXCTL, ctl);
+       }
+}
+
+/* Enable/Disable Direct FIFO Receive Mode (PIO) on a RX engine.
+ * This is called from PIO code, so DMA structures are not available. */
+void b43_dma_direct_fifo_rx(struct b43_wldev *dev,
+                           unsigned int engine_index, bool enable)
+{
+       enum b43_dmatype type;
+       u16 mmio_base;
+
+       type = dma_mask_to_engine_type(supported_dma_mask(dev));
+
+       mmio_base = b43_dmacontroller_base(type, engine_index);
+       direct_fifo_rx(dev, type, mmio_base, enable);
+}
+#endif /* CONFIG_B43_PIO */
index c0d6b69..d1eb5c0 100644 (file)
@@ -181,7 +181,6 @@ struct b43_dmadesc_meta {
        dma_addr_t dmaaddr;
        /* ieee80211 TX status. Only used once per 802.11 frag. */
        bool is_last_fragment;
-       struct ieee80211_tx_status txstat;
 };
 
 struct b43_dmaring;
@@ -245,6 +244,9 @@ struct b43_dmaring {
        enum b43_dmatype type;
        /* Boolean. Is this ring stopped at ieee80211 level? */
        bool stopped;
+       /* The QOS priority assigned to this ring. Only used for TX rings.
+        * This is the mac80211 "queue" value. */
+       u8 queue_prio;
        /* Lock, only used for TX. */
        spinlock_t lock;
        struct b43_wldev *dev;
@@ -253,7 +255,13 @@ struct b43_dmaring {
        int max_used_slots;
        /* Last time we injected a ring overflow. */
        unsigned long last_injected_overflow;
-#endif                         /* CONFIG_B43_DEBUG */
+       /* Statistics: Number of successfully transmitted packets */
+       u64 nr_succeed_tx_packets;
+       /* Statistics: Number of failed TX packets */
+       u64 nr_failed_tx_packets;
+       /* Statistics: Total number of TX plus all retries. */
+       u64 nr_total_packet_tries;
+#endif /* CONFIG_B43_DEBUG */
 };
 
 static inline u32 b43_dma_read(struct b43_dmaring *ring, u16 offset)
@@ -276,10 +284,13 @@ void b43_dma_get_tx_stats(struct b43_wldev *dev,
                          struct ieee80211_tx_queue_stats *stats);
 
 int b43_dma_tx(struct b43_wldev *dev,
-              struct sk_buff *skb, struct ieee80211_tx_control *ctl);
+              struct sk_buff *skb);
 void b43_dma_handle_txstatus(struct b43_wldev *dev,
                             const struct b43_txstatus *status);
 
 void b43_dma_rx(struct b43_dmaring *ring);
 
+void b43_dma_direct_fifo_rx(struct b43_wldev *dev,
+                           unsigned int engine_index, bool enable);
+
 #endif /* B43_DMA_H_ */
index 4b590d8..36a9c42 100644 (file)
@@ -144,12 +144,12 @@ static void b43_map_led(struct b43_wldev *dev,
        case B43_LED_TRANSFER:
        case B43_LED_APTRANSFER:
                snprintf(name, sizeof(name),
-                        "b43-%s:tx", wiphy_name(hw->wiphy));
+                        "b43-%s::tx", wiphy_name(hw->wiphy));
                b43_register_led(dev, &dev->led_tx, name,
                                 ieee80211_get_tx_led_name(hw),
                                 led_index, activelow);
                snprintf(name, sizeof(name),
-                        "b43-%s:rx", wiphy_name(hw->wiphy));
+                        "b43-%s::rx", wiphy_name(hw->wiphy));
                b43_register_led(dev, &dev->led_rx, name,
                                 ieee80211_get_rx_led_name(hw),
                                 led_index, activelow);
@@ -159,7 +159,7 @@ static void b43_map_led(struct b43_wldev *dev,
        case B43_LED_RADIO_B:
        case B43_LED_MODE_BG:
                snprintf(name, sizeof(name),
-                        "b43-%s:radio", wiphy_name(hw->wiphy));
+                        "b43-%s::radio", wiphy_name(hw->wiphy));
                b43_register_led(dev, &dev->led_radio, name,
                                 b43_rfkill_led_name(dev),
                                 led_index, activelow);
@@ -170,7 +170,7 @@ static void b43_map_led(struct b43_wldev *dev,
        case B43_LED_WEIRD:
        case B43_LED_ASSOC:
                snprintf(name, sizeof(name),
-                        "b43-%s:assoc", wiphy_name(hw->wiphy));
+                        "b43-%s::assoc", wiphy_name(hw->wiphy));
                b43_register_led(dev, &dev->led_assoc, name,
                                 ieee80211_get_assoc_led_name(hw),
                                 led_index, activelow);
index d890f36..9c854d6 100644 (file)
 #include <linux/sched.h>
 
 
-/* Define to 1 to always calibrate all possible LO control pairs.
- * This is a workaround until we fix the partial LO calibration optimization. */
-#define B43_CALIB_ALL_LOCTLS   1
+static struct b43_lo_calib * b43_find_lo_calib(struct b43_txpower_lo_control *lo,
+                                              const struct b43_bbatt *bbatt,
+                                              const struct b43_rfatt *rfatt)
+{
+       struct b43_lo_calib *c;
+
+       list_for_each_entry(c, &lo->calib_list, list) {
+               if (!b43_compare_bbatt(&c->bbatt, bbatt))
+                       continue;
+               if (!b43_compare_rfatt(&c->rfatt, rfatt))
+                       continue;
+               return c;
+       }
 
+       return NULL;
+}
 
 /* Write the LocalOscillator Control (adjust) value-pair. */
 static void b43_lo_write(struct b43_wldev *dev, struct b43_loctl *control)
 {
        struct b43_phy *phy = &dev->phy;
        u16 value;
-       u16 reg;
 
        if (B43_DEBUG) {
                if (unlikely(abs(control->i) > 16 || abs(control->q) > 16)) {
@@ -56,189 +67,11 @@ static void b43_lo_write(struct b43_wldev *dev, struct b43_loctl *control)
                        return;
                }
        }
+       B43_WARN_ON(phy->type != B43_PHYTYPE_G);
 
        value = (u8) (control->q);
        value |= ((u8) (control->i)) << 8;
-
-       reg = (phy->type == B43_PHYTYPE_B) ? 0x002F : B43_PHY_LO_CTL;
-       b43_phy_write(dev, reg, value);
-}
-
-static int assert_rfatt_and_bbatt(const struct b43_rfatt *rfatt,
-                                 const struct b43_bbatt *bbatt,
-                                 struct b43_wldev *dev)
-{
-       int err = 0;
-
-       /* Check the attenuation values against the LO control array sizes. */
-       if (unlikely(rfatt->att >= B43_NR_RF)) {
-               b43err(dev->wl, "rfatt(%u) >= size of LO array\n", rfatt->att);
-               err = -EINVAL;
-       }
-       if (unlikely(bbatt->att >= B43_NR_BB)) {
-               b43err(dev->wl, "bbatt(%u) >= size of LO array\n", bbatt->att);
-               err = -EINVAL;
-       }
-
-       return err;
-}
-
-#if !B43_CALIB_ALL_LOCTLS
-static
-struct b43_loctl *b43_get_lo_g_ctl_nopadmix(struct b43_wldev *dev,
-                                           const struct b43_rfatt *rfatt,
-                                           const struct b43_bbatt *bbatt)
-{
-       struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *lo = phy->lo_control;
-
-       if (assert_rfatt_and_bbatt(rfatt, bbatt, dev))
-               return &(lo->no_padmix[0][0]);  /* Just prevent a crash */
-       return &(lo->no_padmix[bbatt->att][rfatt->att]);
-}
-#endif /* !B43_CALIB_ALL_LOCTLS */
-
-struct b43_loctl *b43_get_lo_g_ctl(struct b43_wldev *dev,
-                                  const struct b43_rfatt *rfatt,
-                                  const struct b43_bbatt *bbatt)
-{
-       struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *lo = phy->lo_control;
-
-       if (assert_rfatt_and_bbatt(rfatt, bbatt, dev))
-               return &(lo->no_padmix[0][0]);  /* Just prevent a crash */
-       if (rfatt->with_padmix)
-               return &(lo->with_padmix[bbatt->att][rfatt->att]);
-       return &(lo->no_padmix[bbatt->att][rfatt->att]);
-}
-
-/* Call a function for every possible LO control value-pair. */
-static void b43_call_for_each_loctl(struct b43_wldev *dev,
-                                   void (*func) (struct b43_wldev *,
-                                                 struct b43_loctl *))
-{
-       struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *ctl = phy->lo_control;
-       int i, j;
-
-       for (i = 0; i < B43_NR_BB; i++) {
-               for (j = 0; j < B43_NR_RF; j++)
-                       func(dev, &(ctl->with_padmix[i][j]));
-       }
-       for (i = 0; i < B43_NR_BB; i++) {
-               for (j = 0; j < B43_NR_RF; j++)
-                       func(dev, &(ctl->no_padmix[i][j]));
-       }
-}
-
-static u16 lo_b_r15_loop(struct b43_wldev *dev)
-{
-       int i;
-       u16 ret = 0;
-
-       for (i = 0; i < 10; i++) {
-               b43_phy_write(dev, 0x0015, 0xAFA0);
-               udelay(1);
-               b43_phy_write(dev, 0x0015, 0xEFA0);
-               udelay(10);
-               b43_phy_write(dev, 0x0015, 0xFFA0);
-               udelay(40);
-               ret += b43_phy_read(dev, 0x002C);
-       }
-
-       return ret;
-}
-
-void b43_lo_b_measure(struct b43_wldev *dev)
-{
-       struct b43_phy *phy = &dev->phy;
-       u16 regstack[12] = { 0 };
-       u16 mls;
-       u16 fval;
-       int i, j;
-
-       regstack[0] = b43_phy_read(dev, 0x0015);
-       regstack[1] = b43_radio_read16(dev, 0x0052) & 0xFFF0;
-
-       if (phy->radio_ver == 0x2053) {
-               regstack[2] = b43_phy_read(dev, 0x000A);
-               regstack[3] = b43_phy_read(dev, 0x002A);
-               regstack[4] = b43_phy_read(dev, 0x0035);
-               regstack[5] = b43_phy_read(dev, 0x0003);
-               regstack[6] = b43_phy_read(dev, 0x0001);
-               regstack[7] = b43_phy_read(dev, 0x0030);
-
-               regstack[8] = b43_radio_read16(dev, 0x0043);
-               regstack[9] = b43_radio_read16(dev, 0x007A);
-               regstack[10] = b43_read16(dev, 0x03EC);
-               regstack[11] = b43_radio_read16(dev, 0x0052) & 0x00F0;
-
-               b43_phy_write(dev, 0x0030, 0x00FF);
-               b43_write16(dev, 0x03EC, 0x3F3F);
-               b43_phy_write(dev, 0x0035, regstack[4] & 0xFF7F);
-               b43_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0);
-       }
-       b43_phy_write(dev, 0x0015, 0xB000);
-       b43_phy_write(dev, 0x002B, 0x0004);
-
-       if (phy->radio_ver == 0x2053) {
-               b43_phy_write(dev, 0x002B, 0x0203);
-               b43_phy_write(dev, 0x002A, 0x08A3);
-       }
-
-       phy->minlowsig[0] = 0xFFFF;
-
-       for (i = 0; i < 4; i++) {
-               b43_radio_write16(dev, 0x0052, regstack[1] | i);
-               lo_b_r15_loop(dev);
-       }
-       for (i = 0; i < 10; i++) {
-               b43_radio_write16(dev, 0x0052, regstack[1] | i);
-               mls = lo_b_r15_loop(dev) / 10;
-               if (mls < phy->minlowsig[0]) {
-                       phy->minlowsig[0] = mls;
-                       phy->minlowsigpos[0] = i;
-               }
-       }
-       b43_radio_write16(dev, 0x0052, regstack[1] | phy->minlowsigpos[0]);
-
-       phy->minlowsig[1] = 0xFFFF;
-
-       for (i = -4; i < 5; i += 2) {
-               for (j = -4; j < 5; j += 2) {
-                       if (j < 0)
-                               fval = (0x0100 * i) + j + 0x0100;
-                       else
-                               fval = (0x0100 * i) + j;
-                       b43_phy_write(dev, 0x002F, fval);
-                       mls = lo_b_r15_loop(dev) / 10;
-                       if (mls < phy->minlowsig[1]) {
-                               phy->minlowsig[1] = mls;
-                               phy->minlowsigpos[1] = fval;
-                       }
-               }
-       }
-       phy->minlowsigpos[1] += 0x0101;
-
-       b43_phy_write(dev, 0x002F, phy->minlowsigpos[1]);
-       if (phy->radio_ver == 0x2053) {
-               b43_phy_write(dev, 0x000A, regstack[2]);
-               b43_phy_write(dev, 0x002A, regstack[3]);
-               b43_phy_write(dev, 0x0035, regstack[4]);
-               b43_phy_write(dev, 0x0003, regstack[5]);
-               b43_phy_write(dev, 0x0001, regstack[6]);
-               b43_phy_write(dev, 0x0030, regstack[7]);
-
-               b43_radio_write16(dev, 0x0043, regstack[8]);
-               b43_radio_write16(dev, 0x007A, regstack[9]);
-
-               b43_radio_write16(dev, 0x0052,
-                                 (b43_radio_read16(dev, 0x0052) & 0x000F)
-                                 | regstack[11]);
-
-               b43_write16(dev, 0x03EC, regstack[10]);
-       }
-       b43_phy_write(dev, 0x0015, regstack[0]);
+       b43_phy_write(dev, B43_PHY_LO_CTL, value);
 }
 
 static u16 lo_measure_feedthrough(struct b43_wldev *dev,
@@ -366,7 +199,7 @@ static void lo_measure_txctl_values(struct b43_wldev *dev)
                if (lb_gain > 10) {
                        radio_pctl_reg = 0;
                        pga = abs(10 - lb_gain) / 6;
-                       pga = limit_value(pga, 0, 15);
+                       pga = clamp_val(pga, 0, 15);
                } else {
                        int cmp_val;
                        int tmp;
@@ -438,48 +271,26 @@ static void lo_measure_txctl_values(struct b43_wldev *dev)
                b43_radio_write16(dev, 0x52, b43_radio_read16(dev, 0x52)
                                  & 0xFFF0);    /* TX bias == 0 */
        }
+       lo->txctl_measured_time = jiffies;
 }
 
 static void lo_read_power_vector(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
        struct b43_txpower_lo_control *lo = phy->lo_control;
-       u16 i;
+       int i;
        u64 tmp;
        u64 power_vector = 0;
-       int rf_offset, bb_offset;
-       struct b43_loctl *loctl;
 
        for (i = 0; i < 8; i += 2) {
                tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x310 + i);
-               /* Clear the top byte. We get holes in the bitmap... */
-               tmp &= 0xFF;
                power_vector |= (tmp << (i * 8));
                /* Clear the vector on the device. */
                b43_shm_write16(dev, B43_SHM_SHARED, 0x310 + i, 0);
        }
-
        if (power_vector)
                lo->power_vector = power_vector;
-       power_vector = lo->power_vector;
-
-       for (i = 0; i < 64; i++) {
-               if (power_vector & ((u64) 1ULL << i)) {
-                       /* Now figure out which b43_loctl corresponds
-                        * to this bit.
-                        */
-                       rf_offset = i / lo->rfatt_list.len;
-                       bb_offset = i % lo->rfatt_list.len;     //FIXME?
-                       loctl =
-                           b43_get_lo_g_ctl(dev,
-                                            &lo->rfatt_list.list[rf_offset],
-                                            &lo->bbatt_list.list[bb_offset]);
-                       /* And mark it as "used", as the device told us
-                        * through the bitmap it is using it.
-                        */
-                       loctl->used = 1;
-               }
-       }
+       lo->pwr_vec_read_time = jiffies;
 }
 
 /* 802.11/LO/GPHY/MeasuringGains */
@@ -510,7 +321,7 @@ static void lo_measure_gain_values(struct b43_wldev *dev,
                        phy->lna_lod_gain = 1;
                        trsw_rx_gain -= 8;
                }
-               trsw_rx_gain = limit_value(trsw_rx_gain, 0, 0x2D);
+               trsw_rx_gain = clamp_val(trsw_rx_gain, 0, 0x2D);
                phy->pga_gain = trsw_rx_gain / 3;
                if (phy->pga_gain >= 5) {
                        phy->pga_gain -= 5;
@@ -609,8 +420,6 @@ static void lo_measure_setup(struct b43_wldev *dev,
                b43_phy_write(dev, B43_PHY_CCK(0x16), 0x410);
                b43_phy_write(dev, B43_PHY_CCK(0x17), 0x820);
        }
-       if (!lo->rebuild && b43_has_hardware_pctl(phy))
-               lo_read_power_vector(dev);
        if (phy->rev >= 2) {
                sav->phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER);
                sav->phy_analogoverval =
@@ -691,8 +500,12 @@ static void lo_measure_setup(struct b43_wldev *dev,
        b43_radio_read16(dev, 0x51);    /* dummy read */
        if (phy->type == B43_PHYTYPE_G)
                b43_phy_write(dev, B43_PHY_CCK(0x2F), 0);
-       if (lo->rebuild)
+
+       /* Re-measure the txctl values, if needed. */
+       if (time_before(lo->txctl_measured_time,
+                       jiffies - B43_LO_TXCTL_EXPIRE))
                lo_measure_txctl_values(dev);
+
        if (phy->type == B43_PHYTYPE_G && phy->rev >= 3) {
                b43_phy_write(dev, B43_PHY_LO_MASK, 0xC078);
        } else {
@@ -707,7 +520,6 @@ static void lo_measure_restore(struct b43_wldev *dev,
                               struct lo_g_saved_values *sav)
 {
        struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *lo = phy->lo_control;
        u16 tmp;
 
        if (phy->rev >= 2) {
@@ -722,14 +534,6 @@ static void lo_measure_restore(struct b43_wldev *dev,
                tmp = (phy->pga_gain | 0xEFA0);
                b43_phy_write(dev, B43_PHY_PGACTL, tmp);
        }
-       if (b43_has_hardware_pctl(phy)) {
-               b43_gphy_dc_lt_init(dev);
-       } else {
-               if (lo->rebuild)
-                       b43_lo_g_adjust_to(dev, 3, 2, 0);
-               else
-                       b43_lo_g_adjust(dev);
-       }
        if (phy->type == B43_PHYTYPE_G) {
                if (phy->rev >= 3)
                        b43_phy_write(dev, B43_PHY_CCK(0x2E), 0xC078);
@@ -793,7 +597,6 @@ static int lo_probe_possible_loctls(struct b43_wldev *dev,
                                    struct b43_lo_g_statemachine *d)
 {
        struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *lo = phy->lo_control;
        struct b43_loctl test_loctl;
        struct b43_loctl orig_loctl;
        struct b43_loctl prev_loctl = {
@@ -852,7 +655,7 @@ static int lo_probe_possible_loctls(struct b43_wldev *dev,
                                found_lower = 1;
                                d->lowest_feedth = feedth;
                                if ((d->nr_measured < 2) &&
-                                   (!has_loopback_gain(phy) || lo->rebuild))
+                                   !has_loopback_gain(phy))
                                        break;
                        }
                }
@@ -874,7 +677,6 @@ static void lo_probe_loctls_statemachine(struct b43_wldev *dev,
                                         int *max_rx_gain)
 {
        struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *lo = phy->lo_control;
        struct b43_lo_g_statemachine d;
        u16 feedth;
        int found_lower;
@@ -883,18 +685,18 @@ static void lo_probe_loctls_statemachine(struct b43_wldev *dev,
 
        d.nr_measured = 0;
        d.state_val_multiplier = 1;
-       if (has_loopback_gain(phy) && !lo->rebuild)
+       if (has_loopback_gain(phy))
                d.state_val_multiplier = 3;
 
        memcpy(&d.min_loctl, loctl, sizeof(struct b43_loctl));
-       if (has_loopback_gain(phy) && lo->rebuild)
+       if (has_loopback_gain(phy))
                max_repeat = 4;
        do {
                b43_lo_write(dev, &d.min_loctl);
                feedth = lo_measure_feedthrough(dev, phy->lna_gain,
                                                phy->pga_gain,
                                                phy->trsw_rx_gain);
-               if (!lo->rebuild && feedth < 0x258) {
+               if (feedth < 0x258) {
                        if (feedth >= 0x12C)
                                *max_rx_gain += 6;
                        else
@@ -944,278 +746,188 @@ static void lo_probe_loctls_statemachine(struct b43_wldev *dev,
        } while (++repeat_cnt < max_repeat);
 }
 
-#if B43_CALIB_ALL_LOCTLS
-static const struct b43_rfatt b43_full_rfatt_list_items[] = {
-       { .att = 0, .with_padmix = 0, },
-       { .att = 1, .with_padmix = 0, },
-       { .att = 2, .with_padmix = 0, },
-       { .att = 3, .with_padmix = 0, },
-       { .att = 4, .with_padmix = 0, },
-       { .att = 5, .with_padmix = 0, },
-       { .att = 6, .with_padmix = 0, },
-       { .att = 7, .with_padmix = 0, },
-       { .att = 8, .with_padmix = 0, },
-       { .att = 9, .with_padmix = 0, },
-       { .att = 10, .with_padmix = 0, },
-       { .att = 11, .with_padmix = 0, },
-       { .att = 12, .with_padmix = 0, },
-       { .att = 13, .with_padmix = 0, },
-       { .att = 14, .with_padmix = 0, },
-       { .att = 15, .with_padmix = 0, },
-       { .att = 0, .with_padmix = 1, },
-       { .att = 1, .with_padmix = 1, },
-       { .att = 2, .with_padmix = 1, },
-       { .att = 3, .with_padmix = 1, },
-       { .att = 4, .with_padmix = 1, },
-       { .att = 5, .with_padmix = 1, },
-       { .att = 6, .with_padmix = 1, },
-       { .att = 7, .with_padmix = 1, },
-       { .att = 8, .with_padmix = 1, },
-       { .att = 9, .with_padmix = 1, },
-       { .att = 10, .with_padmix = 1, },
-       { .att = 11, .with_padmix = 1, },
-       { .att = 12, .with_padmix = 1, },
-       { .att = 13, .with_padmix = 1, },
-       { .att = 14, .with_padmix = 1, },
-       { .att = 15, .with_padmix = 1, },
-};
-static const struct b43_rfatt_list b43_full_rfatt_list = {
-       .list           = b43_full_rfatt_list_items,
-       .len            = ARRAY_SIZE(b43_full_rfatt_list_items),
-};
-
-static const struct b43_bbatt b43_full_bbatt_list_items[] = {
-       { .att = 0, },
-       { .att = 1, },
-       { .att = 2, },
-       { .att = 3, },
-       { .att = 4, },
-       { .att = 5, },
-       { .att = 6, },
-       { .att = 7, },
-       { .att = 8, },
-       { .att = 9, },
-       { .att = 10, },
-       { .att = 11, },
-};
-static const struct b43_bbatt_list b43_full_bbatt_list = {
-       .list           = b43_full_bbatt_list_items,
-       .len            = ARRAY_SIZE(b43_full_bbatt_list_items),
-};
-#endif /* B43_CALIB_ALL_LOCTLS */
-
-static void lo_measure(struct b43_wldev *dev)
+static
+struct b43_lo_calib * b43_calibrate_lo_setting(struct b43_wldev *dev,
+                                              const struct b43_bbatt *bbatt,
+                                              const struct b43_rfatt *rfatt)
 {
        struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *lo = phy->lo_control;
        struct b43_loctl loctl = {
                .i = 0,
                .q = 0,
        };
-       struct b43_loctl *ploctl;
        int max_rx_gain;
-       int rfidx, bbidx;
-       const struct b43_bbatt_list *bbatt_list;
-       const struct b43_rfatt_list *rfatt_list;
-
+       struct b43_lo_calib *cal;
+       struct lo_g_saved_values uninitialized_var(saved_regs);
        /* Values from the "TXCTL Register and Value Table" */
        u16 txctl_reg;
        u16 txctl_value;
        u16 pad_mix_gain;
 
-       bbatt_list = &lo->bbatt_list;
-       rfatt_list = &lo->rfatt_list;
-#if B43_CALIB_ALL_LOCTLS
-       bbatt_list = &b43_full_bbatt_list;
-       rfatt_list = &b43_full_rfatt_list;
-#endif
+       saved_regs.old_channel = phy->channel;
+       b43_mac_suspend(dev);
+       lo_measure_setup(dev, &saved_regs);
 
        txctl_reg = lo_txctl_register_table(dev, &txctl_value, &pad_mix_gain);
 
-       for (rfidx = 0; rfidx < rfatt_list->len; rfidx++) {
-
-               b43_radio_write16(dev, 0x43, (b43_radio_read16(dev, 0x43)
-                                             & 0xFFF0) |
-                                 rfatt_list->list[rfidx].att);
-               b43_radio_write16(dev, txctl_reg,
-                                 (b43_radio_read16(dev, txctl_reg)
-                                  & ~txctl_value)
-                                 | (rfatt_list->list[rfidx].with_padmix ?
-                                    txctl_value : 0));
-
-               for (bbidx = 0; bbidx < bbatt_list->len; bbidx++) {
-                       if (lo->rebuild) {
-#if B43_CALIB_ALL_LOCTLS
-                               ploctl = b43_get_lo_g_ctl(dev,
-                                                         &rfatt_list->list[rfidx],
-                                                         &bbatt_list->list[bbidx]);
-#else
-                               ploctl = b43_get_lo_g_ctl_nopadmix(dev,
-                                                                  &rfatt_list->
-                                                                  list[rfidx],
-                                                                  &bbatt_list->
-                                                                  list[bbidx]);
-#endif
-                       } else {
-                               ploctl = b43_get_lo_g_ctl(dev,
-                                                         &rfatt_list->list[rfidx],
-                                                         &bbatt_list->list[bbidx]);
-                               if (!ploctl->used)
-                                       continue;
-                       }
-                       memcpy(&loctl, ploctl, sizeof(loctl));
-                       loctl.i = 0;
-                       loctl.q = 0;
-
-                       max_rx_gain = rfatt_list->list[rfidx].att * 2;
-                       max_rx_gain += bbatt_list->list[bbidx].att / 2;
-                       if (rfatt_list->list[rfidx].with_padmix)
-                               max_rx_gain -= pad_mix_gain;
-                       if (has_loopback_gain(phy))
-                               max_rx_gain += phy->max_lb_gain;
-                       lo_measure_gain_values(dev, max_rx_gain,
-                                              has_loopback_gain(phy));
-
-                       b43_phy_set_baseband_attenuation(dev,
-                                                        bbatt_list->list[bbidx].att);
-                       lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain);
-                       if (phy->type == B43_PHYTYPE_B) {
-                               loctl.i++;
-                               loctl.q++;
-                       }
-                       b43_loctl_set_calibrated(&loctl, 1);
-                       memcpy(ploctl, &loctl, sizeof(loctl));
-               }
-       }
-}
-
-#if B43_DEBUG
-static void do_validate_loctl(struct b43_wldev *dev, struct b43_loctl *control)
-{
-       const int is_initializing = (b43_status(dev) == B43_STAT_UNINIT);
-       int i = control->i;
-       int q = control->q;
+       b43_radio_write16(dev, 0x43,
+                         (b43_radio_read16(dev, 0x43) & 0xFFF0)
+                         | rfatt->att);
+       b43_radio_write16(dev, txctl_reg,
+                         (b43_radio_read16(dev, txctl_reg) & ~txctl_value)
+                         | (rfatt->with_padmix) ? txctl_value : 0);
 
-       if (b43_loctl_is_calibrated(control)) {
-               if ((abs(i) > 16) || (abs(q) > 16))
-                       goto error;
-       } else {
-               if (control->used)
-                       goto error;
-               if (dev->phy.lo_control->rebuild) {
-                       control->i = 0;
-                       control->q = 0;
-                       if ((i != B43_LOCTL_POISON) ||
-                           (q != B43_LOCTL_POISON))
-                               goto error;
-               }
+       max_rx_gain = rfatt->att * 2;
+       max_rx_gain += bbatt->att / 2;
+       if (rfatt->with_padmix)
+               max_rx_gain -= pad_mix_gain;
+       if (has_loopback_gain(phy))
+               max_rx_gain += phy->max_lb_gain;
+       lo_measure_gain_values(dev, max_rx_gain,
+                              has_loopback_gain(phy));
+
+       b43_phy_set_baseband_attenuation(dev, bbatt->att);
+       lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain);
+
+       lo_measure_restore(dev, &saved_regs);
+       b43_mac_enable(dev);
+
+       if (b43_debug(dev, B43_DBG_LO)) {
+               b43dbg(dev->wl, "LO: Calibrated for BB(%u), RF(%u,%u) "
+                      "=> I=%d Q=%d\n",
+                      bbatt->att, rfatt->att, rfatt->with_padmix,
+                      loctl.i, loctl.q);
        }
-       if (is_initializing && control->used)
-               goto error;
-
-       return;
-error:
-       b43err(dev->wl, "LO control pair validation failed "
-              "(I: %d, Q: %d, used %u, calib: %u, initing: %d)\n",
-              i, q, control->used,
-              b43_loctl_is_calibrated(control),
-              is_initializing);
-}
 
-static void validate_all_loctls(struct b43_wldev *dev)
-{
-       b43_call_for_each_loctl(dev, do_validate_loctl);
-}
-
-static void do_reset_calib(struct b43_wldev *dev, struct b43_loctl *control)
-{
-       if (dev->phy.lo_control->rebuild ||
-           control->used) {
-               b43_loctl_set_calibrated(control, 0);
-               control->i = B43_LOCTL_POISON;
-               control->q = B43_LOCTL_POISON;
+       cal = kmalloc(sizeof(*cal), GFP_KERNEL);
+       if (!cal) {
+               b43warn(dev->wl, "LO calib: out of memory\n");
+               return NULL;
        }
+       memcpy(&cal->bbatt, bbatt, sizeof(*bbatt));
+       memcpy(&cal->rfatt, rfatt, sizeof(*rfatt));
+       memcpy(&cal->ctl, &loctl, sizeof(loctl));
+       cal->calib_time = jiffies;
+       INIT_LIST_HEAD(&cal->list);
+
+       return cal;
 }
 
-static void reset_all_loctl_calibration_states(struct b43_wldev *dev)
+/* Get a calibrated LO setting for the given attenuation values.
+ * Might return a NULL pointer under OOM! */
+static
+struct b43_lo_calib * b43_get_calib_lo_settings(struct b43_wldev *dev,
+                                               const struct b43_bbatt *bbatt,
+                                               const struct b43_rfatt *rfatt)
 {
-       b43_call_for_each_loctl(dev, do_reset_calib);
+       struct b43_txpower_lo_control *lo = dev->phy.lo_control;
+       struct b43_lo_calib *c;
+
+       c = b43_find_lo_calib(lo, bbatt, rfatt);
+       if (c)
+               return c;
+       /* Not in the list of calibrated LO settings.
+        * Calibrate it now. */
+       c = b43_calibrate_lo_setting(dev, bbatt, rfatt);
+       if (!c)
+               return NULL;
+       list_add(&c->list, &lo->calib_list);
+
+       return c;
 }
 
-#else /* B43_DEBUG */
-static inline void validate_all_loctls(struct b43_wldev *dev) { }
-static inline void reset_all_loctl_calibration_states(struct b43_wldev *dev) { }
-#endif /* B43_DEBUG */
-
-void b43_lo_g_measure(struct b43_wldev *dev)
+void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all)
 {
        struct b43_phy *phy = &dev->phy;
-       struct lo_g_saved_values uninitialized_var(sav);
-
-       B43_WARN_ON((phy->type != B43_PHYTYPE_B) &&
-                   (phy->type != B43_PHYTYPE_G));
-
-       sav.old_channel = phy->channel;
-       lo_measure_setup(dev, &sav);
-       reset_all_loctl_calibration_states(dev);
-       lo_measure(dev);
-       lo_measure_restore(dev, &sav);
-
-       validate_all_loctls(dev);
+       struct b43_txpower_lo_control *lo = phy->lo_control;
+       int i;
+       int rf_offset, bb_offset;
+       const struct b43_rfatt *rfatt;
+       const struct b43_bbatt *bbatt;
+       u64 power_vector;
+       bool table_changed = 0;
 
-       phy->lo_control->lo_measured = 1;
-       phy->lo_control->rebuild = 0;
-}
+       BUILD_BUG_ON(B43_DC_LT_SIZE != 32);
+       B43_WARN_ON(lo->rfatt_list.len * lo->bbatt_list.len > 64);
 
-#if B43_DEBUG
-static void validate_loctl_calibration(struct b43_wldev *dev,
-                                      struct b43_loctl *loctl,
-                                      struct b43_rfatt *rfatt,
-                                      struct b43_bbatt *bbatt)
-{
-       if (b43_loctl_is_calibrated(loctl))
-               return;
-       if (!dev->phy.lo_control->lo_measured) {
-               /* On init we set the attenuation values before we
-                * calibrated the LO. I guess that's OK. */
-               return;
+       power_vector = lo->power_vector;
+       if (!update_all && !power_vector)
+               return; /* Nothing to do. */
+
+       /* Suspend the MAC now to avoid continuous suspend/enable
+        * cycles in the loop. */
+       b43_mac_suspend(dev);
+
+       for (i = 0; i < B43_DC_LT_SIZE * 2; i++) {
+               struct b43_lo_calib *cal;
+               int idx;
+               u16 val;
+
+               if (!update_all && !(power_vector & (((u64)1ULL) << i)))
+                       continue;
+               /* Update the table entry for this power_vector bit.
+                * The table rows are RFatt entries and columns are BBatt. */
+               bb_offset = i / lo->rfatt_list.len;
+               rf_offset = i % lo->rfatt_list.len;
+               bbatt = &(lo->bbatt_list.list[bb_offset]);
+               rfatt = &(lo->rfatt_list.list[rf_offset]);
+
+               cal = b43_calibrate_lo_setting(dev, bbatt, rfatt);
+               if (!cal) {
+                       b43warn(dev->wl, "LO: Could not "
+                               "calibrate DC table entry\n");
+                       continue;
+               }
+               /*FIXME: Is Q really in the low nibble? */
+               val = (u8)(cal->ctl.q);
+               val |= ((u8)(cal->ctl.i)) << 4;
+               kfree(cal);
+
+               /* Get the index into the hardware DC LT. */
+               idx = i / 2;
+               /* Change the table in memory. */
+               if (i % 2) {
+                       /* Change the high byte. */
+                       lo->dc_lt[idx] = (lo->dc_lt[idx] & 0x00FF)
+                                        | ((val & 0x00FF) << 8);
+               } else {
+                       /* Change the low byte. */
+                       lo->dc_lt[idx] = (lo->dc_lt[idx] & 0xFF00)
+                                        | (val & 0x00FF);
+               }
+               table_changed = 1;
        }
-       b43err(dev->wl, "Adjusting Local Oscillator to an uncalibrated "
-              "control pair: rfatt=%u,%spadmix bbatt=%u\n",
-              rfatt->att,
-              (rfatt->with_padmix) ? "" : "no-",
-              bbatt->att);
-}
-#else
-static inline void validate_loctl_calibration(struct b43_wldev *dev,
-                                             struct b43_loctl *loctl,
-                                             struct b43_rfatt *rfatt,
-                                             struct b43_bbatt *bbatt)
-{
+       if (table_changed) {
+               /* The table changed in memory. Update the hardware table. */
+               for (i = 0; i < B43_DC_LT_SIZE; i++)
+                       b43_phy_write(dev, 0x3A0 + i, lo->dc_lt[i]);
+       }
+       b43_mac_enable(dev);
 }
-#endif
 
-static inline void fixup_rfatt_for_txcontrol(struct b43_rfatt *rf,
-                                            u8 tx_control)
+/* Fixup the RF attenuation value for the case where we are
+ * using the PAD mixer. */
+static inline void b43_lo_fixup_rfatt(struct b43_rfatt *rf)
 {
-       if (tx_control & B43_TXCTL_TXMIX) {
-               if (rf->att < 5)
-                       rf->att = 4;
-       }
+       if (!rf->with_padmix)
+               return;
+       if ((rf->att != 1) && (rf->att != 2) && (rf->att != 3))
+               rf->att = 4;
 }
 
 void b43_lo_g_adjust(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
+       struct b43_lo_calib *cal;
        struct b43_rfatt rf;
-       struct b43_loctl *loctl;
 
        memcpy(&rf, &phy->rfatt, sizeof(rf));
-       fixup_rfatt_for_txcontrol(&rf, phy->tx_control);
+       b43_lo_fixup_rfatt(&rf);
 
-       loctl = b43_get_lo_g_ctl(dev, &rf, &phy->bbatt);
-       validate_loctl_calibration(dev, loctl, &rf, &phy->bbatt);
-       b43_lo_write(dev, loctl);
+       cal = b43_get_calib_lo_settings(dev, &phy->bbatt, &rf);
+       if (!cal)
+               return;
+       b43_lo_write(dev, &cal->ctl);
 }
 
 void b43_lo_g_adjust_to(struct b43_wldev *dev,
@@ -1223,39 +935,102 @@ void b43_lo_g_adjust_to(struct b43_wldev *dev,
 {
        struct b43_rfatt rf;
        struct b43_bbatt bb;
-       struct b43_loctl *loctl;
+       struct b43_lo_calib *cal;
 
        memset(&rf, 0, sizeof(rf));
        memset(&bb, 0, sizeof(bb));
        rf.att = rfatt;
        bb.att = bbatt;
-       fixup_rfatt_for_txcontrol(&rf, tx_control);
-       loctl = b43_get_lo_g_ctl(dev, &rf, &bb);
-       validate_loctl_calibration(dev, loctl, &rf, &bb);
-       b43_lo_write(dev, loctl);
+       b43_lo_fixup_rfatt(&rf);
+       cal = b43_get_calib_lo_settings(dev, &bb, &rf);
+       if (!cal)
+               return;
+       b43_lo_write(dev, &cal->ctl);
 }
 
-static void do_mark_unused(struct b43_wldev *dev, struct b43_loctl *control)
+/* Periodic LO maintanance work */
+void b43_lo_g_maintanance_work(struct b43_wldev *dev)
 {
-       control->used = 0;
+       struct b43_phy *phy = &dev->phy;
+       struct b43_txpower_lo_control *lo = phy->lo_control;
+       unsigned long now;
+       unsigned long expire;
+       struct b43_lo_calib *cal, *tmp;
+       bool current_item_expired = 0;
+       bool hwpctl;
+
+       if (!lo)
+               return;
+       now = jiffies;
+       hwpctl = b43_has_hardware_pctl(phy);
+
+       if (hwpctl) {
+               /* Read the power vector and update it, if needed. */
+               expire = now - B43_LO_PWRVEC_EXPIRE;
+               if (time_before(lo->pwr_vec_read_time, expire)) {
+                       lo_read_power_vector(dev);
+                       b43_gphy_dc_lt_init(dev, 0);
+               }
+               //FIXME Recalc the whole DC table from time to time?
+       }
+
+       if (hwpctl)
+               return;
+       /* Search for expired LO settings. Remove them.
+        * Recalibrate the current setting, if expired. */
+       expire = now - B43_LO_CALIB_EXPIRE;
+       list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) {
+               if (!time_before(cal->calib_time, expire))
+                       continue;
+               /* This item expired. */
+               if (b43_compare_bbatt(&cal->bbatt, &phy->bbatt) &&
+                   b43_compare_rfatt(&cal->rfatt, &phy->rfatt)) {
+                       B43_WARN_ON(current_item_expired);
+                       current_item_expired = 1;
+               }
+               if (b43_debug(dev, B43_DBG_LO)) {
+                       b43dbg(dev->wl, "LO: Item BB(%u), RF(%u,%u), "
+                              "I=%d, Q=%d expired\n",
+                              cal->bbatt.att, cal->rfatt.att,
+                              cal->rfatt.with_padmix,
+                              cal->ctl.i, cal->ctl.q);
+               }
+               list_del(&cal->list);
+               kfree(cal);
+       }
+       if (current_item_expired || unlikely(list_empty(&lo->calib_list))) {
+               /* Recalibrate currently used LO setting. */
+               if (b43_debug(dev, B43_DBG_LO))
+                       b43dbg(dev->wl, "LO: Recalibrating current LO setting\n");
+               cal = b43_calibrate_lo_setting(dev, &phy->bbatt, &phy->rfatt);
+               if (cal) {
+                       list_add(&cal->list, &lo->calib_list);
+                       b43_lo_write(dev, &cal->ctl);
+               } else
+                       b43warn(dev->wl, "Failed to recalibrate current LO setting\n");
+       }
 }
 
-void b43_lo_g_ctl_mark_all_unused(struct b43_wldev *dev)
+void b43_lo_g_cleanup(struct b43_wldev *dev)
 {
-       struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *lo = phy->lo_control;
+       struct b43_txpower_lo_control *lo = dev->phy.lo_control;
+       struct b43_lo_calib *cal, *tmp;
 
-       b43_call_for_each_loctl(dev, do_mark_unused);
-       lo->rebuild = 1;
+       if (!lo)
+               return;
+       list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) {
+               list_del(&cal->list);
+               kfree(cal);
+       }
 }
 
-void b43_lo_g_ctl_mark_cur_used(struct b43_wldev *dev)
+/* LO Initialization */
+void b43_lo_g_init(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
-       struct b43_rfatt rf;
 
-       memcpy(&rf, &phy->rfatt, sizeof(rf));
-       fixup_rfatt_for_txcontrol(&rf, phy->tx_control);
-
-       b43_get_lo_g_ctl(dev, &rf, &phy->bbatt)->used = 1;
+       if (b43_has_hardware_pctl(phy)) {
+               lo_read_power_vector(dev);
+               b43_gphy_dc_lt_init(dev, 1);
+       }
 }
index 455615d..1da321c 100644 (file)
@@ -10,82 +10,63 @@ struct b43_loctl {
        /* Control values. */
        s8 i;
        s8 q;
-       /* "Used by hardware" flag. */
-       bool used;
-#ifdef CONFIG_B43_DEBUG
-       /* Is this lo-control-array entry calibrated? */
-       bool calibrated;
-#endif
 };
-
 /* Debugging: Poison value for i and q values. */
 #define B43_LOCTL_POISON       111
 
-/* loctl->calibrated debugging mechanism */
-#ifdef CONFIG_B43_DEBUG
-static inline void b43_loctl_set_calibrated(struct b43_loctl *loctl,
-                                           bool calibrated)
-{
-       loctl->calibrated = calibrated;
-}
-static inline bool b43_loctl_is_calibrated(struct b43_loctl *loctl)
-{
-       return loctl->calibrated;
-}
-#else
-static inline void b43_loctl_set_calibrated(struct b43_loctl *loctl,
-                                           bool calibrated)
-{
-}
-static inline bool b43_loctl_is_calibrated(struct b43_loctl *loctl)
-{
-       return 1;
-}
-#endif
-
-/* TX Power LO Control Array.
- * Value-pairs to adjust the LocalOscillator are stored
- * in this structure.
- * There are two different set of values. One for "Flag is Set"
- * and one for "Flag is Unset".
- * By "Flag" the flag in struct b43_rfatt is meant.
- * The Value arrays are two-dimensional. The first index
- * is the baseband attenuation and the second index
- * is the radio attenuation.
- * Use b43_get_lo_g_ctl() to retrieve a value from the lists.
- */
+/* This struct holds calibrated LO settings for a set of
+ * Baseband and RF attenuation settings. */
+struct b43_lo_calib {
+       /* The set of attenuation values this set of LO
+        * control values is calibrated for. */
+       struct b43_bbatt bbatt;
+       struct b43_rfatt rfatt;
+       /* The set of control values for the LO. */
+       struct b43_loctl ctl;
+       /* The time when these settings were calibrated (in jiffies) */
+       unsigned long calib_time;
+       /* List. */
+       struct list_head list;
+};
+
+/* Size of the DC Lookup Table in 16bit words. */
+#define B43_DC_LT_SIZE         32
+
+/* Local Oscillator calibration information */
 struct b43_txpower_lo_control {
-#define B43_NR_BB      12
-#define B43_NR_RF      16
-       /* LO Control values, with PAD Mixer */
-       struct b43_loctl with_padmix[B43_NR_BB][B43_NR_RF];
-       /* LO Control values, without PAD Mixer */
-       struct b43_loctl no_padmix[B43_NR_BB][B43_NR_RF];
-
-       /* Flag to indicate a complete rebuild of the two tables above
-        * to the LO measuring code. */
-       bool rebuild;
-
-       /* Lists of valid RF and BB attenuation values for this device. */
+       /* Lists of RF and BB attenuation values for this device.
+        * Used for building hardware power control tables. */
        struct b43_rfatt_list rfatt_list;
        struct b43_bbatt_list bbatt_list;
 
+       /* The DC Lookup Table is cached in memory here.
+        * Note that this is only used for Hardware Power Control. */
+       u16 dc_lt[B43_DC_LT_SIZE];
+
+       /* List of calibrated control values (struct b43_lo_calib). */
+       struct list_head calib_list;
+       /* Last time the power vector was read (jiffies). */
+       unsigned long pwr_vec_read_time;
+       /* Last time the txctl values were measured (jiffies). */
+       unsigned long txctl_measured_time;
+
        /* Current TX Bias value */
        u8 tx_bias;
        /* Current TX Magnification Value (if used by the device) */
        u8 tx_magn;
 
-       /* GPHY LO is measured. */
-       bool lo_measured;
-
        /* Saved device PowerVector */
        u64 power_vector;
 };
 
-/* Measure the BPHY Local Oscillator. */
-void b43_lo_b_measure(struct b43_wldev *dev);
-/* Measure the BPHY/GPHY Local Oscillator. */
-void b43_lo_g_measure(struct b43_wldev *dev);
+/* Calibration expire timeouts.
+ * Timeouts must be multiple of 15 seconds. To make sure
+ * the item really expired when the 15 second timer hits, we
+ * subtract two additional seconds from the timeout. */
+#define B43_LO_CALIB_EXPIRE    (HZ * (30 - 2))
+#define B43_LO_PWRVEC_EXPIRE   (HZ * (30 - 2))
+#define B43_LO_TXCTL_EXPIRE    (HZ * (180 - 4))
+
 
 /* Adjust the Local Oscillator to the saved attenuation
  * and txctl values.
@@ -95,18 +76,10 @@ void b43_lo_g_adjust(struct b43_wldev *dev);
 void b43_lo_g_adjust_to(struct b43_wldev *dev,
                        u16 rfatt, u16 bbatt, u16 tx_control);
 
-/* Mark all possible b43_lo_g_ctl as "unused" */
-void b43_lo_g_ctl_mark_all_unused(struct b43_wldev *dev);
-/* Mark the b43_lo_g_ctl corresponding to the current
- * attenuation values as used.
- */
-void b43_lo_g_ctl_mark_cur_used(struct b43_wldev *dev);
+void b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all);
 
-/* Get a reference to a LO Control value pair in the
- * TX Power LO Control Array.
- */
-struct b43_loctl *b43_get_lo_g_ctl(struct b43_wldev *dev,
-                                  const struct b43_rfatt *rfatt,
-                                  const struct b43_bbatt *bbatt);
+void b43_lo_g_maintanance_work(struct b43_wldev *dev);
+void b43_lo_g_cleanup(struct b43_wldev *dev);
+void b43_lo_g_init(struct b43_wldev *dev);
 
 #endif /* B43_LO_H_ */
index afb1094..cbb317b 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;
 
index 2d52d9d..dad23c4 100644 (file)
 /* Magic helper macro to pad structures. Ignore those above. It's magic. */
 #define PAD_BYTES(nr_bytes)            P4D_BYTES( __LINE__ , (nr_bytes))
 
+
+extern int b43_modparam_qos;
+
+
 /* Lightweight function to convert a frequency (in Mhz) to a channel number. */
 static inline u8 b43_freq_to_channel_5ghz(int freq)
 {
@@ -95,16 +99,13 @@ u16 b43_shm_read16(struct b43_wldev *dev, u16 routing, u16 offset);
 void b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value);
 void b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value);
 
-u32 b43_hf_read(struct b43_wldev *dev);
-void b43_hf_write(struct b43_wldev *dev, u32 value);
+u64 b43_hf_read(struct b43_wldev *dev);
+void b43_hf_write(struct b43_wldev *dev, u64 value);
 
 void b43_dummy_transmission(struct b43_wldev *dev);
 
 void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags);
 
-void b43_mac_suspend(struct b43_wldev *dev);
-void b43_mac_enable(struct b43_wldev *dev);
-
 void b43_controller_restart(struct b43_wldev *dev, const char *reason);
 
 #define B43_PS_ENABLED (1 << 0)        /* Force enable hardware power saving */
@@ -113,4 +114,7 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason);
 #define B43_PS_ASLEEP  (1 << 3)        /* Force device asleep */
 void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags);
 
+void b43_mac_suspend(struct b43_wldev *dev);
+void b43_mac_enable(struct b43_wldev *dev);
+
 #endif /* B43_MAIN_H_ */
index 705131e..644eed9 100644 (file)
@@ -29,8 +29,6 @@
 #include "nphy.h"
 #include "tables_nphy.h"
 
-#include <linux/delay.h>
-
 
 void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
 {//TODO
@@ -240,7 +238,6 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
 
        b43_phy_set(dev, B43_NPHY_IQFLIP,
                    B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
-       //FIXME the following condition is different in the specs.
        if (1 /* FIXME band is 2.4GHz */) {
                b43_phy_set(dev, B43_NPHY_CLASSCTL,
                            B43_NPHY_CLASSCTL_CCKEN);
index 5d95118..faf46b9 100644 (file)
 
 struct b43_wldev;
 
+
+#ifdef CONFIG_B43_NPHY
+/* N-PHY support enabled */
+
 int b43_phy_initn(struct b43_wldev *dev);
 
 void b43_nphy_radio_turn_on(struct b43_wldev *dev);
@@ -929,4 +933,40 @@ int b43_nphy_selectchannel(struct b43_wldev *dev, u8 channel);
 void b43_nphy_xmitpower(struct b43_wldev *dev);
 void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna);
 
+
+#else /* CONFIG_B43_NPHY */
+/* N-PHY support disabled */
+
+
+static inline
+int b43_phy_initn(struct b43_wldev *dev)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline
+void b43_nphy_radio_turn_on(struct b43_wldev *dev)
+{
+}
+static inline
+void b43_nphy_radio_turn_off(struct b43_wldev *dev)
+{
+}
+
+static inline
+int b43_nphy_selectchannel(struct b43_wldev *dev, u8 channel)
+{
+       return -ENOSYS;
+}
+
+static inline
+void b43_nphy_xmitpower(struct b43_wldev *dev)
+{
+}
+static inline
+void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
+{
+}
+
+#endif /* CONFIG_B43_NPHY */
 #endif /* B43_NPHY_H_ */
index b79a6bd..b8aa163 100644 (file)
@@ -43,14 +43,16 @@ MODULE_DEVICE_TABLE(pcmcia, b43_pcmcia_tbl);
 #ifdef CONFIG_PM
 static int b43_pcmcia_suspend(struct pcmcia_device *dev)
 {
-       //TODO
-       return 0;
+       struct ssb_bus *ssb = dev->priv;
+
+       return ssb_bus_suspend(ssb);
 }
 
 static int b43_pcmcia_resume(struct pcmcia_device *dev)
 {
-       //TODO
-       return 0;
+       struct ssb_bus *ssb = dev->priv;
+
+       return ssb_bus_resume(ssb);
 }
 #else /* CONFIG_PM */
 # define b43_pcmcia_suspend            NULL
@@ -91,6 +93,8 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev)
 
        dev->conf.ConfigBase = parse.config.base;
        dev->conf.Present = parse.config.rmask[0];
+       dev->conf.Attributes = CONF_ENABLE_IRQ;
+       dev->conf.IntType = INT_MEMORY_AND_IO;
 
        dev->io.BasePort2 = 0;
        dev->io.NumPorts2 = 0;
@@ -112,8 +116,8 @@ static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev)
        if (res != CS_SUCCESS)
                goto err_disable;
 
-       dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_FIRST_SHARED;
-       dev->irq.IRQInfo1 = IRQ_LEVEL_ID | IRQ_SHARE_ID;
+       dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+       dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
        dev->irq.Handler = NULL; /* The handler is registered later. */
        dev->irq.Instance = NULL;
        res = pcmcia_request_irq(dev, &dev->irq);
index 71507b2..305d4cd 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/types.h>
+#include <linux/bitrev.h>
 
 #include "b43.h"
 #include "phy.h"
@@ -83,25 +84,9 @@ const u8 b43_radio_channel_codes_bg[] = {
        72, 84,
 };
 
+#define bitrev4(tmp) (bitrev8(tmp) >> 4)
 static void b43_phy_initg(struct b43_wldev *dev);
 
-/* Reverse the bits of a 4bit value.
- * Example:  1101 is flipped 1011
- */
-static u16 flip_4bit(u16 value)
-{
-       u16 flipped = 0x0000;
-
-       B43_WARN_ON(value & ~0x000F);
-
-       flipped |= (value & 0x0001) << 3;
-       flipped |= (value & 0x0002) << 1;
-       flipped |= (value & 0x0004) >> 1;
-       flipped |= (value & 0x0008) >> 3;
-
-       return flipped;
-}
-
 static void generate_rfatt_list(struct b43_wldev *dev,
                                struct b43_rfatt_list *list)
 {
@@ -145,8 +130,7 @@ static void generate_rfatt_list(struct b43_wldev *dev,
                {.att = 9,.with_padmix = 1,},
        };
 
-       if ((phy->type == B43_PHYTYPE_A && phy->rev < 5) ||
-           (phy->type == B43_PHYTYPE_G && phy->rev < 6)) {
+       if (!b43_has_hardware_pctl(phy)) {
                /* Software pctl */
                list->list = rfatt_0;
                list->len = ARRAY_SIZE(rfatt_0);
@@ -158,7 +142,7 @@ static void generate_rfatt_list(struct b43_wldev *dev,
                /* Hardware pctl */
                list->list = rfatt_1;
                list->len = ARRAY_SIZE(rfatt_1);
-               list->min_val = 2;
+               list->min_val = 0;
                list->max_val = 14;
                return;
        }
@@ -346,6 +330,7 @@ void b43_set_txpower_g(struct b43_wldev *dev,
        /* Save the values for later */
        phy->tx_control = tx_control;
        memcpy(&phy->rfatt, rfatt, sizeof(*rfatt));
+       phy->rfatt.with_padmix = !!(tx_control & B43_TXCTL_TXMIX);
        memcpy(&phy->bbatt, bbatt, sizeof(*bbatt));
 
        if (b43_debug(dev, B43_DBG_XMITPOWER)) {
@@ -559,11 +544,6 @@ static void b43_gphy_gain_lt_init(struct b43_wldev *dev)
        u16 tmp;
        u8 rf, bb;
 
-       if (!lo->lo_measured) {
-               b43_phy_write(dev, 0x3FF, 0);
-               return;
-       }
-
        for (rf = 0; rf < lo->rfatt_list.len; rf++) {
                for (bb = 0; bb < lo->bbatt_list.len; bb++) {
                        if (nr_written >= 0x40)
@@ -581,42 +561,6 @@ static void b43_gphy_gain_lt_init(struct b43_wldev *dev)
        }
 }
 
-/* GPHY_DC_Lookup_Table */
-void b43_gphy_dc_lt_init(struct b43_wldev *dev)
-{
-       struct b43_phy *phy = &dev->phy;
-       struct b43_txpower_lo_control *lo = phy->lo_control;
-       struct b43_loctl *loctl0;
-       struct b43_loctl *loctl1;
-       int i;
-       int rf_offset, bb_offset;
-       u16 tmp;
-
-       for (i = 0; i < lo->rfatt_list.len + lo->bbatt_list.len; i += 2) {
-               rf_offset = i / lo->rfatt_list.len;
-               bb_offset = i % lo->rfatt_list.len;
-
-               loctl0 = b43_get_lo_g_ctl(dev, &lo->rfatt_list.list[rf_offset],
-                                         &lo->bbatt_list.list[bb_offset]);
-               if (i + 1 < lo->rfatt_list.len * lo->bbatt_list.len) {
-                       rf_offset = (i + 1) / lo->rfatt_list.len;
-                       bb_offset = (i + 1) % lo->rfatt_list.len;
-
-                       loctl1 =
-                           b43_get_lo_g_ctl(dev,
-                                            &lo->rfatt_list.list[rf_offset],
-                                            &lo->bbatt_list.list[bb_offset]);
-               } else
-                       loctl1 = loctl0;
-
-               tmp = ((u16) loctl0->q & 0xF);
-               tmp |= ((u16) loctl0->i & 0xF) << 4;
-               tmp |= ((u16) loctl1->q & 0xF) << 8;
-               tmp |= ((u16) loctl1->i & 0xF) << 12;   //FIXME?
-               b43_phy_write(dev, 0x3A0 + (i / 2), tmp);
-       }
-}
-
 static void hardware_pctl_init_aphy(struct b43_wldev *dev)
 {
        //TODO
@@ -643,7 +587,7 @@ static void hardware_pctl_init_gphy(struct b43_wldev *dev)
        b43_phy_write(dev, 0x0801, b43_phy_read(dev, 0x0801)
                      & 0xFFBF);
 
-       b43_gphy_dc_lt_init(dev);
+       b43_gphy_dc_lt_init(dev, 1);
 }
 
 /* HardwarePowerControl init for A and G PHY */
@@ -860,7 +804,7 @@ static void b43_phy_ww(struct b43_wldev *dev)
        b43_phy_write(dev, B43_PHY_OFDM(0xBB),
                (b43_phy_read(dev, B43_PHY_OFDM(0xBB)) & 0xF000) | 0x0053);
        b43_phy_write(dev, B43_PHY_OFDM61,
-               (b43_phy_read(dev, B43_PHY_OFDM61 & 0xFE1F)) | 0x0120);
+               (b43_phy_read(dev, B43_PHY_OFDM61) & 0xFE1F) | 0x0120);
        b43_phy_write(dev, B43_PHY_OFDM(0x13),
                (b43_phy_read(dev, B43_PHY_OFDM(0x13)) & 0x0FFF) | 0x3000);
        b43_phy_write(dev, B43_PHY_OFDM(0x14),
@@ -931,109 +875,6 @@ static void b43_phy_inita(struct b43_wldev *dev)
        }
 }
 
-static void b43_phy_initb2(struct b43_wldev *dev)
-{
-       struct b43_phy *phy = &dev->phy;
-       u16 offset, val;
-
-       b43_write16(dev, 0x03EC, 0x3F22);
-       b43_phy_write(dev, 0x0020, 0x301C);
-       b43_phy_write(dev, 0x0026, 0x0000);
-       b43_phy_write(dev, 0x0030, 0x00C6);
-       b43_phy_write(dev, 0x0088, 0x3E00);
-       val = 0x3C3D;
-       for (offset = 0x0089; offset < 0x00A7; offset++) {
-               b43_phy_write(dev, offset, val);
-               val -= 0x0202;
-       }
-       b43_phy_write(dev, 0x03E4, 0x3000);
-       b43_radio_selectchannel(dev, phy->channel, 0);
-       if (phy->radio_ver != 0x2050) {
-               b43_radio_write16(dev, 0x0075, 0x0080);
-               b43_radio_write16(dev, 0x0079, 0x0081);
-       }
-       b43_radio_write16(dev, 0x0050, 0x0020);
-       b43_radio_write16(dev, 0x0050, 0x0023);
-       if (phy->radio_ver == 0x2050) {
-               b43_radio_write16(dev, 0x0050, 0x0020);
-               b43_radio_write16(dev, 0x005A, 0x0070);
-               b43_radio_write16(dev, 0x005B, 0x007B);
-               b43_radio_write16(dev, 0x005C, 0x00B0);
-               b43_radio_write16(dev, 0x007A, 0x000F);
-               b43_phy_write(dev, 0x0038, 0x0677);
-               b43_radio_init2050(dev);
-       }
-       b43_phy_write(dev, 0x0014, 0x0080);
-       b43_phy_write(dev, 0x0032, 0x00CA);
-       b43_phy_write(dev, 0x0032, 0x00CC);
-       b43_phy_write(dev, 0x0035, 0x07C2);
-       b43_lo_b_measure(dev);
-       b43_phy_write(dev, 0x0026, 0xCC00);
-       if (phy->radio_ver != 0x2050)
-               b43_phy_write(dev, 0x0026, 0xCE00);
-       b43_write16(dev, B43_MMIO_CHANNEL_EXT, 0x1000);
-       b43_phy_write(dev, 0x002A, 0x88A3);
-       if (phy->radio_ver != 0x2050)
-               b43_phy_write(dev, 0x002A, 0x88C2);
-       b43_set_txpower_g(dev, &phy->bbatt, &phy->rfatt, phy->tx_control);
-       b43_phy_init_pctl(dev);
-}
-
-static void b43_phy_initb4(struct b43_wldev *dev)
-{
-       struct b43_phy *phy = &dev->phy;
-       u16 offset, val;
-
-       b43_write16(dev, 0x03EC, 0x3F22);
-       b43_phy_write(dev, 0x0020, 0x301C);
-       b43_phy_write(dev, 0x0026, 0x0000);
-       b43_phy_write(dev, 0x0030, 0x00C6);
-       b43_phy_write(dev, 0x0088, 0x3E00);
-       val = 0x3C3D;
-       for (offset = 0x0089; offset < 0x00A7; offset++) {
-               b43_phy_write(dev, offset, val);
-               val -= 0x0202;
-       }
-       b43_phy_write(dev, 0x03E4, 0x3000);
-       b43_radio_selectchannel(dev, phy->channel, 0);
-       if (phy->radio_ver != 0x2050) {
-               b43_radio_write16(dev, 0x0075, 0x0080);
-               b43_radio_write16(dev, 0x0079, 0x0081);
-       }
-       b43_radio_write16(dev, 0x0050, 0x0020);
-       b43_radio_write16(dev, 0x0050, 0x0023);
-       if (phy->radio_ver == 0x2050) {
-               b43_radio_write16(dev, 0x0050, 0x0020);
-               b43_radio_write16(dev, 0x005A, 0x0070);
-               b43_radio_write16(dev, 0x005B, 0x007B);
-               b43_radio_write16(dev, 0x005C, 0x00B0);
-               b43_radio_write16(dev, 0x007A, 0x000F);
-               b43_phy_write(dev, 0x0038, 0x0677);
-               b43_radio_init2050(dev);
-       }
-       b43_phy_write(dev, 0x0014, 0x0080);
-       b43_phy_write(dev, 0x0032, 0x00CA);
-       if (phy->radio_ver == 0x2050)
-               b43_phy_write(dev, 0x0032, 0x00E0);
-       b43_phy_write(dev, 0x0035, 0x07C2);
-
-       b43_lo_b_measure(dev);
-
-       b43_phy_write(dev, 0x0026, 0xCC00);
-       if (phy->radio_ver == 0x2050)
-               b43_phy_write(dev, 0x0026, 0xCE00);
-       b43_write16(dev, B43_MMIO_CHANNEL_EXT, 0x1100);
-       b43_phy_write(dev, 0x002A, 0x88A3);
-       if (phy->radio_ver == 0x2050)
-               b43_phy_write(dev, 0x002A, 0x88C2);
-       b43_set_txpower_g(dev, &phy->bbatt, &phy->rfatt, phy->tx_control);
-       if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_RSSI) {
-               b43_calc_nrssi_slope(dev);
-               b43_calc_nrssi_threshold(dev);
-       }
-       b43_phy_init_pctl(dev);
-}
-
 static void b43_phy_initb5(struct b43_wldev *dev)
 {
        struct ssb_bus *bus = dev->dev->bus;
@@ -1259,19 +1100,9 @@ static void b43_phy_initb6(struct b43_wldev *dev)
                b43_phy_write(dev, 0x0002, (b43_phy_read(dev, 0x0002) & 0xFFC0)
                              | 0x0004);
        }
-       if (phy->type == B43_PHYTYPE_B) {
-               b43_write16(dev, 0x03E6, 0x8140);
-               b43_phy_write(dev, 0x0016, 0x0410);
-               b43_phy_write(dev, 0x0017, 0x0820);
-               b43_phy_write(dev, 0x0062, 0x0007);
-               b43_radio_init2050(dev);
-               b43_lo_g_measure(dev);
-               if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_RSSI) {
-                       b43_calc_nrssi_slope(dev);
-                       b43_calc_nrssi_threshold(dev);
-               }
-               b43_phy_init_pctl(dev);
-       } else if (phy->type == B43_PHYTYPE_G)
+       if (phy->type == B43_PHYTYPE_B)
+               B43_WARN_ON(1);
+       else if (phy->type == B43_PHYTYPE_G)
                b43_write16(dev, 0x03E6, 0x0);
 }
 
@@ -1534,34 +1365,31 @@ static void b43_phy_initg(struct b43_wldev *dev)
                else
                        b43_radio_write16(dev, 0x0078, phy->initval);
        }
-       if (phy->lo_control->tx_bias == 0xFF) {
-               b43_lo_g_measure(dev);
+       b43_lo_g_init(dev);
+       if (has_tx_magnification(phy)) {
+               b43_radio_write16(dev, 0x52,
+                                 (b43_radio_read16(dev, 0x52) & 0xFF00)
+                                 | phy->lo_control->tx_bias | phy->
+                                 lo_control->tx_magn);
        } else {
-               if (has_tx_magnification(phy)) {
-                       b43_radio_write16(dev, 0x52,
-                                         (b43_radio_read16(dev, 0x52) & 0xFF00)
-                                         | phy->lo_control->tx_bias | phy->
-                                         lo_control->tx_magn);
-               } else {
-                       b43_radio_write16(dev, 0x52,
-                                         (b43_radio_read16(dev, 0x52) & 0xFFF0)
-                                         | phy->lo_control->tx_bias);
-               }
-               if (phy->rev >= 6) {
-                       b43_phy_write(dev, B43_PHY_CCK(0x36),
-                                     (b43_phy_read(dev, B43_PHY_CCK(0x36))
-                                      & 0x0FFF) | (phy->lo_control->
-                                                   tx_bias << 12));
-               }
-               if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_PACTRL)
-                       b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8075);
-               else
-                       b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x807F);
-               if (phy->rev < 2)
-                       b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x101);
-               else
-                       b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x202);
+               b43_radio_write16(dev, 0x52,
+                                 (b43_radio_read16(dev, 0x52) & 0xFFF0)
+                                 | phy->lo_control->tx_bias);
        }
+       if (phy->rev >= 6) {
+               b43_phy_write(dev, B43_PHY_CCK(0x36),
+                             (b43_phy_read(dev, B43_PHY_CCK(0x36))
+                              & 0x0FFF) | (phy->lo_control->
+                                           tx_bias << 12));
+       }
+       if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_PACTRL)
+               b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8075);
+       else
+               b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x807F);
+       if (phy->rev < 2)
+               b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x101);
+       else
+               b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x202);
        if (phy->gmode || phy->rev >= 2) {
                b43_lo_g_adjust(dev);
                b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078);
@@ -1572,7 +1400,7 @@ static void b43_phy_initg(struct b43_wldev *dev)
                 * the value 0x7FFFFFFF here. I think that is some weird
                 * compiler optimization in the original driver.
                 * Essentially, what we do here is resetting all NRSSI LT
-                * entries to -32 (see the limit_value() in nrssi_hw_update())
+                * entries to -32 (see the clamp_val() in nrssi_hw_update())
                 */
                b43_nrssi_hw_update(dev, 0xFFFF);       //FIXME?
                b43_calc_nrssi_threshold(dev);
@@ -1634,13 +1462,13 @@ static s8 b43_phy_estimate_power_out(struct b43_wldev *dev, s8 tssi)
        switch (phy->type) {
        case B43_PHYTYPE_A:
                tmp += 0x80;
-               tmp = limit_value(tmp, 0x00, 0xFF);
+               tmp = clamp_val(tmp, 0x00, 0xFF);
                dbm = phy->tssi2dbm[tmp];
                //TODO: There's a FIXME on the specs
                break;
        case B43_PHYTYPE_B:
        case B43_PHYTYPE_G:
-               tmp = limit_value(tmp, 0x00, 0x3F);
+               tmp = clamp_val(tmp, 0x00, 0x3F);
                dbm = phy->tssi2dbm[tmp];
                break;
        default:
@@ -1699,8 +1527,8 @@ void b43_put_attenuation_into_ranges(struct b43_wldev *dev,
                break;
        }
 
-       *_rfatt = limit_value(rfatt, rf_min, rf_max);
-       *_bbatt = limit_value(bbatt, bb_min, bb_max);
+       *_rfatt = clamp_val(rfatt, rf_min, rf_max);
+       *_bbatt = clamp_val(bbatt, bb_min, bb_max);
 }
 
 /* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */
@@ -1795,7 +1623,7 @@ void b43_phy_xmitpower(struct b43_wldev *dev)
                        /* Get desired power (in Q5.2) */
                        desired_pwr = INT_TO_Q52(phy->power_level);
                        /* And limit it. max_pwr already is Q5.2 */
-                       desired_pwr = limit_value(desired_pwr, 0, max_pwr);
+                       desired_pwr = clamp_val(desired_pwr, 0, max_pwr);
                        if (b43_debug(dev, B43_DBG_XMITPOWER)) {
                                b43dbg(dev->wl,
                                       "Current TX power output: " Q52_FMT
@@ -1821,10 +1649,8 @@ void b43_phy_xmitpower(struct b43_wldev *dev)
                        bbatt_delta -= 4 * rfatt_delta;
 
                        /* So do we finally need to adjust something? */
-                       if ((rfatt_delta == 0) && (bbatt_delta == 0)) {
-                               b43_lo_g_ctl_mark_cur_used(dev);
+                       if ((rfatt_delta == 0) && (bbatt_delta == 0))
                                return;
-                       }
 
                        /* Calculate the new attenuation values. */
                        bbatt = phy->bbatt.att;
@@ -1870,7 +1696,6 @@ void b43_phy_xmitpower(struct b43_wldev *dev)
                        b43_radio_lock(dev);
                        b43_set_txpower_g(dev, &phy->bbatt, &phy->rfatt,
                                          phy->tx_control);
-                       b43_lo_g_ctl_mark_cur_used(dev);
                        b43_radio_unlock(dev);
                        b43_phy_unlock(dev);
                        break;
@@ -1908,7 +1733,7 @@ static inline
                f = q;
                i++;
        } while (delta >= 2);
-       entry[index] = limit_value(b43_tssi2dbm_ad(m1 * f, 8192), -127, 128);
+       entry[index] = clamp_val(b43_tssi2dbm_ad(m1 * f, 8192), -127, 128);
        return 0;
 }
 
@@ -2007,24 +1832,6 @@ int b43_phy_init(struct b43_wldev *dev)
                else
                        unsupported = 1;
                break;
-       case B43_PHYTYPE_B:
-               switch (phy->rev) {
-               case 2:
-                       b43_phy_initb2(dev);
-                       break;
-               case 4:
-                       b43_phy_initb4(dev);
-                       break;
-               case 5:
-                       b43_phy_initb5(dev);
-                       break;
-               case 6:
-                       b43_phy_initb6(dev);
-                       break;
-               default:
-                       unsupported = 1;
-               }
-               break;
        case B43_PHYTYPE_G:
                b43_phy_initg(dev);
                break;
@@ -2043,7 +1850,7 @@ int b43_phy_init(struct b43_wldev *dev)
 void b43_set_rx_antenna(struct b43_wldev *dev, int antenna)
 {
        struct b43_phy *phy = &dev->phy;
-       u32 hf;
+       u64 hf;
        u16 tmp;
        int autodiv = 0;
 
@@ -2452,7 +2259,7 @@ void b43_nrssi_hw_update(struct b43_wldev *dev, u16 val)
        for (i = 0; i < 64; i++) {
                tmp = b43_nrssi_hw_read(dev, i);
                tmp -= val;
-               tmp = limit_value(tmp, -32, 31);
+               tmp = clamp_val(tmp, -32, 31);
                b43_nrssi_hw_write(dev, i, tmp);
        }
 }
@@ -2469,7 +2276,7 @@ void b43_nrssi_mem_update(struct b43_wldev *dev)
                tmp = (i - delta) * phy->nrssislope;
                tmp /= 0x10000;
                tmp += 0x3A;
-               tmp = limit_value(tmp, 0, 0x3F);
+               tmp = clamp_val(tmp, 0, 0x3F);
                phy->nrssi_lt[i] = tmp;
        }
 }
@@ -2906,7 +2713,7 @@ void b43_calc_nrssi_threshold(struct b43_wldev *dev)
                        } else
                                threshold = phy->nrssi[1] - 5;
 
-                       threshold = limit_value(threshold, 0, 0x3E);
+                       threshold = clamp_val(threshold, 0, 0x3E);
                        b43_phy_read(dev, 0x0020);      /* dummy read */
                        b43_phy_write(dev, 0x0020,
                                      (((u16) threshold) << 8) | 0x001C);
@@ -2957,7 +2764,7 @@ void b43_calc_nrssi_threshold(struct b43_wldev *dev)
                        else
                                a += 32;
                        a = a >> 6;
-                       a = limit_value(a, -31, 31);
+                       a = clamp_val(a, -31, 31);
 
                        b = b * (phy->nrssi[1] - phy->nrssi[0]);
                        b += (phy->nrssi[0] << 6);
@@ -2966,7 +2773,7 @@ void b43_calc_nrssi_threshold(struct b43_wldev *dev)
                        else
                                b += 32;
                        b = b >> 6;
-                       b = limit_value(b, -31, 31);
+                       b = clamp_val(b, -31, 31);
 
                        tmp_u16 = b43_phy_read(dev, 0x048A) & 0xF000;
                        tmp_u16 |= ((u32) b & 0x0000003F);
@@ -3069,13 +2876,13 @@ b43_radio_interference_mitigation_enable(struct b43_wldev *dev, int mode)
                }
                radio_stacksave(0x0078);
                tmp = (b43_radio_read16(dev, 0x0078) & 0x001E);
-               flipped = flip_4bit(tmp);
+               B43_WARN_ON(tmp > 15);
+               flipped = bitrev4(tmp);
                if (flipped < 10 && flipped >= 8)
                        flipped = 7;
                else if (flipped >= 10)
                        flipped -= 3;
-               flipped = flip_4bit(flipped);
-               flipped = (flipped << 1) | 0x0020;
+               flipped = (bitrev4(flipped) << 1) | 0x0020;
                b43_radio_write16(dev, 0x0078, flipped);
 
                b43_calc_nrssi_threshold(dev);
@@ -3708,7 +3515,7 @@ u16 b43_radio_init2050(struct b43_wldev *dev)
        tmp1 >>= 9;
 
        for (i = 0; i < 16; i++) {
-               radio78 = ((flip_4bit(i) << 1) | 0x20);
+               radio78 = (bitrev4(i) << 1) | 0x0020;
                b43_radio_write16(dev, 0x78, radio78);
                udelay(10);
                for (j = 0; j < 16; j++) {
index 6d165d8..4aab109 100644 (file)
@@ -225,7 +225,6 @@ int b43_phy_init(struct b43_wldev *dev);
 void b43_set_rx_antenna(struct b43_wldev *dev, int antenna);
 
 void b43_phy_xmitpower(struct b43_wldev *dev);
-void b43_gphy_dc_lt_init(struct b43_wldev *dev);
 
 /* Returns the boolean whether the board has HardwarePowerControl */
 bool b43_has_hardware_pctl(struct b43_phy *phy);
@@ -252,6 +251,14 @@ struct b43_rfatt_list {
        u8 max_val;
 };
 
+/* Returns true, if the values are the same. */
+static inline bool b43_compare_rfatt(const struct b43_rfatt *a,
+                                    const struct b43_rfatt *b)
+{
+       return ((a->att == b->att) &&
+               (a->with_padmix == b->with_padmix));
+}
+
 /* Baseband Attenuation */
 struct b43_bbatt {
        u8 att;                 /* Attenuation value */
@@ -265,6 +272,13 @@ struct b43_bbatt_list {
        u8 max_val;
 };
 
+/* Returns true, if the values are the same. */
+static inline bool b43_compare_bbatt(const struct b43_bbatt *a,
+                                    const struct b43_bbatt *b)
+{
+       return (a->att == b->att);
+}
+
 /* tx_control bits. */
 #define B43_TXCTL_PA3DB                0x40    /* PA Gain 3dB */
 #define B43_TXCTL_PA2DB                0x20    /* PA Gain 2dB */
diff --git a/package/b43/src/pio.c b/package/b43/src/pio.c
new file mode 100644 (file)
index 0000000..8b1555d
--- /dev/null
@@ -0,0 +1,842 @@
+/*
+
+  Broadcom B43 wireless driver
+
+  PIO data transfer
+
+  Copyright (c) 2005-2008 Michael Buesch <mb@bu3sch.de>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+
+*/
+
+#include "b43.h"
+#include "pio.h"
+#include "dma.h"
+#include "main.h"
+#include "xmit.h"
+
+#include <linux/delay.h>
+
+
+static void b43_pio_rx_work(struct work_struct *work);
+
+
+static u16 generate_cookie(struct b43_pio_txqueue *q,
+                          struct b43_pio_txpacket *pack)
+{
+       u16 cookie;
+
+       /* Use the upper 4 bits of the cookie as
+        * PIO controller ID and store the packet index number
+        * in the lower 12 bits.
+        * Note that the cookie must never be 0, as this
+        * is a special value used in RX path.
+        * It can also not be 0xFFFF because that is special
+        * for multicast frames.
+        */
+       cookie = (((u16)q->index + 1) << 12);
+       cookie |= pack->index;
+
+       return cookie;
+}
+
+static
+struct b43_pio_txqueue * parse_cookie(struct b43_wldev *dev,
+                                     u16 cookie,
+                                     struct b43_pio_txpacket **pack)
+{
+       struct b43_pio *pio = &dev->pio;
+       struct b43_pio_txqueue *q = NULL;
+       unsigned int pack_index;
+
+       switch (cookie & 0xF000) {
+       case 0x1000:
+               q = pio->tx_queue_AC_BK;
+               break;
+       case 0x2000:
+               q = pio->tx_queue_AC_BE;
+               break;
+       case 0x3000:
+               q = pio->tx_queue_AC_VI;
+               break;
+       case 0x4000:
+               q = pio->tx_queue_AC_VO;
+               break;
+       case 0x5000:
+               q = pio->tx_queue_mcast;
+               break;
+       }
+       if (B43_WARN_ON(!q))
+               return NULL;
+       pack_index = (cookie & 0x0FFF);
+       if (B43_WARN_ON(pack_index >= ARRAY_SIZE(q->packets)))
+               return NULL;
+       *pack = &q->packets[pack_index];
+
+       return q;
+}
+
+static u16 index_to_pioqueue_base(struct b43_wldev *dev,
+                                 unsigned int index)
+{
+       static const u16 bases[] = {
+               B43_MMIO_PIO_BASE0,
+               B43_MMIO_PIO_BASE1,
+               B43_MMIO_PIO_BASE2,
+               B43_MMIO_PIO_BASE3,
+               B43_MMIO_PIO_BASE4,
+               B43_MMIO_PIO_BASE5,
+               B43_MMIO_PIO_BASE6,
+               B43_MMIO_PIO_BASE7,
+       };
+       static const u16 bases_rev11[] = {
+               B43_MMIO_PIO11_BASE0,
+               B43_MMIO_PIO11_BASE1,
+               B43_MMIO_PIO11_BASE2,
+               B43_MMIO_PIO11_BASE3,
+               B43_MMIO_PIO11_BASE4,
+               B43_MMIO_PIO11_BASE5,
+       };
+
+       if (dev->dev->id.revision >= 11) {
+               B43_WARN_ON(index >= ARRAY_SIZE(bases_rev11));
+               return bases_rev11[index];
+       }
+       B43_WARN_ON(index >= ARRAY_SIZE(bases));
+       return bases[index];
+}
+
+static u16 pio_txqueue_offset(struct b43_wldev *dev)
+{
+       if (dev->dev->id.revision >= 11)
+               return 0x18;
+       return 0;
+}
+
+static u16 pio_rxqueue_offset(struct b43_wldev *dev)
+{
+       if (dev->dev->id.revision >= 11)
+               return 0x38;
+       return 8;
+}
+
+static struct b43_pio_txqueue * b43_setup_pioqueue_tx(struct b43_wldev *dev,
+                                                     unsigned int index)
+{
+       struct b43_pio_txqueue *q;
+       struct b43_pio_txpacket *p;
+       unsigned int i;
+
+       q = kzalloc(sizeof(*q), GFP_KERNEL);
+       if (!q)
+               return NULL;
+       spin_lock_init(&q->lock);
+       q->dev = dev;
+       q->rev = dev->dev->id.revision;
+       q->mmio_base = index_to_pioqueue_base(dev, index) +
+                      pio_txqueue_offset(dev);
+       q->index = index;
+
+       q->free_packet_slots = B43_PIO_MAX_NR_TXPACKETS;
+       if (q->rev >= 8) {
+               q->buffer_size = 1920; //FIXME this constant is wrong.
+       } else {
+               q->buffer_size = b43_piotx_read16(q, B43_PIO_TXQBUFSIZE);
+               q->buffer_size -= 80;
+       }
+
+       INIT_LIST_HEAD(&q->packets_list);
+       for (i = 0; i < ARRAY_SIZE(q->packets); i++) {
+               p = &(q->packets[i]);
+               INIT_LIST_HEAD(&p->list);
+               p->index = i;
+               p->queue = q;
+               list_add(&p->list, &q->packets_list);
+       }
+
+       return q;
+}
+
+static struct b43_pio_rxqueue * b43_setup_pioqueue_rx(struct b43_wldev *dev,
+                                                     unsigned int index)
+{
+       struct b43_pio_rxqueue *q;
+
+       q = kzalloc(sizeof(*q), GFP_KERNEL);
+       if (!q)
+               return NULL;
+       spin_lock_init(&q->lock);
+       q->dev = dev;
+       q->rev = dev->dev->id.revision;
+       q->mmio_base = index_to_pioqueue_base(dev, index) +
+                      pio_rxqueue_offset(dev);
+       INIT_WORK(&q->rx_work, b43_pio_rx_work);
+
+       /* Enable Direct FIFO RX (PIO) on the engine. */
+       b43_dma_direct_fifo_rx(dev, index, 1);
+
+       return q;
+}
+
+static void b43_pio_cancel_tx_packets(struct b43_pio_txqueue *q)
+{
+       struct b43_pio_txpacket *pack;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(q->packets); i++) {
+               pack = &(q->packets[i]);
+               if (pack->skb) {
+                       dev_kfree_skb_any(pack->skb);
+                       pack->skb = NULL;
+               }
+       }
+}
+
+static void b43_destroy_pioqueue_tx(struct b43_pio_txqueue *q,
+                                   const char *name)
+{
+       if (!q)
+               return;
+       b43_pio_cancel_tx_packets(q);
+       kfree(q);
+}
+
+static void b43_destroy_pioqueue_rx(struct b43_pio_rxqueue *q,
+                                   const char *name)
+{
+       if (!q)
+               return;
+       kfree(q);
+}
+
+#define destroy_queue_tx(pio, queue) do {                              \
+       b43_destroy_pioqueue_tx((pio)->queue, __stringify(queue));      \
+       (pio)->queue = NULL;                                            \
+  } while (0)
+
+#define destroy_queue_rx(pio, queue) do {                              \
+       b43_destroy_pioqueue_rx((pio)->queue, __stringify(queue));      \
+       (pio)->queue = NULL;                                            \
+  } while (0)
+
+void b43_pio_free(struct b43_wldev *dev)
+{
+       struct b43_pio *pio;
+
+       if (!b43_using_pio_transfers(dev))
+               return;
+       pio = &dev->pio;
+
+       destroy_queue_rx(pio, rx_queue);
+       destroy_queue_tx(pio, tx_queue_mcast);
+       destroy_queue_tx(pio, tx_queue_AC_VO);
+       destroy_queue_tx(pio, tx_queue_AC_VI);
+       destroy_queue_tx(pio, tx_queue_AC_BE);
+       destroy_queue_tx(pio, tx_queue_AC_BK);
+}
+
+void b43_pio_stop(struct b43_wldev *dev)
+{
+       if (!b43_using_pio_transfers(dev))
+               return;
+       cancel_work_sync(&dev->pio.rx_queue->rx_work);
+}
+
+int b43_pio_init(struct b43_wldev *dev)
+{
+       struct b43_pio *pio = &dev->pio;
+       int err = -ENOMEM;
+
+       b43_write32(dev, B43_MMIO_MACCTL, b43_read32(dev, B43_MMIO_MACCTL)
+                   & ~B43_MACCTL_BE);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_RXPADOFF, 0);
+
+       pio->tx_queue_AC_BK = b43_setup_pioqueue_tx(dev, 0);
+       if (!pio->tx_queue_AC_BK)
+               goto out;
+
+       pio->tx_queue_AC_BE = b43_setup_pioqueue_tx(dev, 1);
+       if (!pio->tx_queue_AC_BE)
+               goto err_destroy_bk;
+
+       pio->tx_queue_AC_VI = b43_setup_pioqueue_tx(dev, 2);
+       if (!pio->tx_queue_AC_VI)
+               goto err_destroy_be;
+
+       pio->tx_queue_AC_VO = b43_setup_pioqueue_tx(dev, 3);
+       if (!pio->tx_queue_AC_VO)
+               goto err_destroy_vi;
+
+       pio->tx_queue_mcast = b43_setup_pioqueue_tx(dev, 4);
+       if (!pio->tx_queue_mcast)
+               goto err_destroy_vo;
+
+       pio->rx_queue = b43_setup_pioqueue_rx(dev, 0);
+       if (!pio->rx_queue)
+               goto err_destroy_mcast;
+
+       b43dbg(dev->wl, "PIO initialized\n");
+       err = 0;
+out:
+       return err;
+
+err_destroy_mcast:
+       destroy_queue_tx(pio, tx_queue_mcast);
+err_destroy_vo:
+       destroy_queue_tx(pio, tx_queue_AC_VO);
+err_destroy_vi:
+       destroy_queue_tx(pio, tx_queue_AC_VI);
+err_destroy_be:
+       destroy_queue_tx(pio, tx_queue_AC_BE);
+err_destroy_bk:
+       destroy_queue_tx(pio, tx_queue_AC_BK);
+       return err;
+}
+
+/* Static mapping of mac80211's queues (priorities) to b43 PIO queues. */
+static struct b43_pio_txqueue * select_queue_by_priority(struct b43_wldev *dev,
+                                                        u8 queue_prio)
+{
+       struct b43_pio_txqueue *q;
+
+       if (b43_modparam_qos) {
+               /* 0 = highest priority */
+               switch (queue_prio) {
+               default:
+                       B43_WARN_ON(1);
+                       /* fallthrough */
+               case 0:
+                       q = dev->pio.tx_queue_AC_VO;
+                       break;
+               case 1:
+                       q = dev->pio.tx_queue_AC_VI;
+                       break;
+               case 2:
+                       q = dev->pio.tx_queue_AC_BE;
+                       break;
+               case 3:
+                       q = dev->pio.tx_queue_AC_BK;
+                       break;
+               }
+       } else
+               q = dev->pio.tx_queue_AC_BE;
+
+       return q;
+}
+
+static u16 tx_write_2byte_queue(struct b43_pio_txqueue *q,
+                               u16 ctl,
+                               const void *_data,
+                               unsigned int data_len)
+{
+       struct b43_wldev *dev = q->dev;
+       const u8 *data = _data;
+
+       ctl |= B43_PIO_TXCTL_WRITELO | B43_PIO_TXCTL_WRITEHI;
+       b43_piotx_write16(q, B43_PIO_TXCTL, ctl);
+
+       ssb_block_write(dev->dev, data, (data_len & ~1),
+                       q->mmio_base + B43_PIO_TXDATA,
+                       sizeof(u16));
+       if (data_len & 1) {
+               /* Write the last byte. */
+               ctl &= ~B43_PIO_TXCTL_WRITEHI;
+               b43_piotx_write16(q, B43_PIO_TXCTL, ctl);
+               b43_piotx_write16(q, B43_PIO_TXDATA, data[data_len - 1]);
+       }
+
+       return ctl;
+}
+
+static void pio_tx_frame_2byte_queue(struct b43_pio_txpacket *pack,
+                                    const u8 *hdr, unsigned int hdrlen)
+{
+       struct b43_pio_txqueue *q = pack->queue;
+       const char *frame = pack->skb->data;
+       unsigned int frame_len = pack->skb->len;
+       u16 ctl;
+
+       ctl = b43_piotx_read16(q, B43_PIO_TXCTL);
+       ctl |= B43_PIO_TXCTL_FREADY;
+       ctl &= ~B43_PIO_TXCTL_EOF;
+
+       /* Transfer the header data. */
+       ctl = tx_write_2byte_queue(q, ctl, hdr, hdrlen);
+       /* Transfer the frame data. */
+       ctl = tx_write_2byte_queue(q, ctl, frame, frame_len);
+
+       ctl |= B43_PIO_TXCTL_EOF;
+       b43_piotx_write16(q, B43_PIO_TXCTL, ctl);
+}
+
+static u32 tx_write_4byte_queue(struct b43_pio_txqueue *q,
+                               u32 ctl,
+                               const void *_data,
+                               unsigned int data_len)
+{
+       struct b43_wldev *dev = q->dev;
+       const u8 *data = _data;
+
+       ctl |= B43_PIO8_TXCTL_0_7 | B43_PIO8_TXCTL_8_15 |
+              B43_PIO8_TXCTL_16_23 | B43_PIO8_TXCTL_24_31;
+       b43_piotx_write32(q, B43_PIO8_TXCTL, ctl);
+
+       ssb_block_write(dev->dev, data, (data_len & ~3),
+                       q->mmio_base + B43_PIO8_TXDATA,
+                       sizeof(u32));
+       if (data_len & 3) {
+               u32 value = 0;
+
+               /* Write the last few bytes. */
+               ctl &= ~(B43_PIO8_TXCTL_8_15 | B43_PIO8_TXCTL_16_23 |
+                        B43_PIO8_TXCTL_24_31);
+               data = &(data[data_len - 1]);
+               switch (data_len & 3) {
+               case 3:
+                       ctl |= B43_PIO8_TXCTL_16_23;
+                       value |= (u32)(*data) << 16;
+                       data--;
+               case 2:
+                       ctl |= B43_PIO8_TXCTL_8_15;
+                       value |= (u32)(*data) << 8;
+                       data--;
+               case 1:
+                       value |= (u32)(*data);
+               }
+               b43_piotx_write32(q, B43_PIO8_TXCTL, ctl);
+               b43_piotx_write32(q, B43_PIO8_TXDATA, value);
+       }
+
+       return ctl;
+}
+
+static void pio_tx_frame_4byte_queue(struct b43_pio_txpacket *pack,
+                                    const u8 *hdr, unsigned int hdrlen)
+{
+       struct b43_pio_txqueue *q = pack->queue;
+       const char *frame = pack->skb->data;
+       unsigned int frame_len = pack->skb->len;
+       u32 ctl;
+
+       ctl = b43_piotx_read32(q, B43_PIO8_TXCTL);
+       ctl |= B43_PIO8_TXCTL_FREADY;
+       ctl &= ~B43_PIO8_TXCTL_EOF;
+
+       /* Transfer the header data. */
+       ctl = tx_write_4byte_queue(q, ctl, hdr, hdrlen);
+       /* Transfer the frame data. */
+       ctl = tx_write_4byte_queue(q, ctl, frame, frame_len);
+
+       ctl |= B43_PIO8_TXCTL_EOF;
+       b43_piotx_write32(q, B43_PIO_TXCTL, ctl);
+}
+
+static int pio_tx_frame(struct b43_pio_txqueue *q,
+                       struct sk_buff *skb)
+{
+       struct b43_pio_txpacket *pack;
+       struct b43_txhdr txhdr;
+       u16 cookie;
+       int err;
+       unsigned int hdrlen;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+       B43_WARN_ON(list_empty(&q->packets_list));
+       pack = list_entry(q->packets_list.next,
+                         struct b43_pio_txpacket, list);
+
+       cookie = generate_cookie(q, pack);
+       hdrlen = b43_txhdr_size(q->dev);
+       err = b43_generate_txhdr(q->dev, (u8 *)&txhdr, skb->data,
+                                skb->len, info, cookie);
+       if (err)
+               return err;
+
+       if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+               /* Tell the firmware about the cookie of the last
+                * mcast frame, so it can clear the more-data bit in it. */
+               b43_shm_write16(q->dev, B43_SHM_SHARED,
+                               B43_SHM_SH_MCASTCOOKIE, cookie);
+       }
+
+       pack->skb = skb;
+       if (q->rev >= 8)
+               pio_tx_frame_4byte_queue(pack, (const u8 *)&txhdr, hdrlen);
+       else
+               pio_tx_frame_2byte_queue(pack, (const u8 *)&txhdr, hdrlen);
+
+       /* Remove it from the list of available packet slots.
+        * It will be put back when we receive the status report. */
+       list_del(&pack->list);
+
+       /* Update the queue statistics. */
+       q->buffer_used += roundup(skb->len + hdrlen, 4);
+       q->free_packet_slots -= 1;
+
+       return 0;
+}
+
+int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb)
+{
+       struct b43_pio_txqueue *q;
+       struct ieee80211_hdr *hdr;
+       unsigned long flags;
+       unsigned int hdrlen, total_len;
+       int err = 0;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+
+       if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
+               /* The multicast queue will be sent after the DTIM. */
+               q = dev->pio.tx_queue_mcast;
+               /* Set the frame More-Data bit. Ucode will clear it
+                * for us on the last frame. */
+               hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+       } else {
+               /* Decide by priority where to put this frame. */
+               q = select_queue_by_priority(dev, skb_get_queue_mapping(skb));
+       }
+
+       spin_lock_irqsave(&q->lock, flags);
+
+       hdrlen = b43_txhdr_size(dev);
+       total_len = roundup(skb->len + hdrlen, 4);
+
+       if (unlikely(total_len > q->buffer_size)) {
+               err = -ENOBUFS;
+               b43dbg(dev->wl, "PIO: TX packet longer than queue.\n");
+               goto out_unlock;
+       }
+       if (unlikely(q->free_packet_slots == 0)) {
+               err = -ENOBUFS;
+               b43warn(dev->wl, "PIO: TX packet overflow.\n");
+               goto out_unlock;
+       }
+       B43_WARN_ON(q->buffer_used > q->buffer_size);
+
+       if (total_len > (q->buffer_size - q->buffer_used)) {
+               /* Not enough memory on the queue. */
+               err = -EBUSY;
+               ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
+               q->stopped = 1;
+               goto out_unlock;
+       }
+
+       /* Assign the queue number to the ring (if not already done before)
+        * so TX status handling can use it. The mac80211-queue to b43-queue
+        * mapping is static, so we don't need to store it per frame. */
+       q->queue_prio = skb_get_queue_mapping(skb);
+
+       err = pio_tx_frame(q, skb);
+       if (unlikely(err == -ENOKEY)) {
+               /* Drop this packet, as we don't have the encryption key
+                * anymore and must not transmit it unencrypted. */
+               dev_kfree_skb_any(skb);
+               err = 0;
+               goto out_unlock;
+       }
+       if (unlikely(err)) {
+               b43err(dev->wl, "PIO transmission failure\n");
+               goto out_unlock;
+       }
+       q->nr_tx_packets++;
+
+       B43_WARN_ON(q->buffer_used > q->buffer_size);
+       if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) ||
+           (q->free_packet_slots == 0)) {
+               /* The queue is full. */
+               ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
+               q->stopped = 1;
+       }
+
+out_unlock:
+       spin_unlock_irqrestore(&q->lock, flags);
+
+       return err;
+}
+
+/* Called with IRQs disabled. */
+void b43_pio_handle_txstatus(struct b43_wldev *dev,
+                            const struct b43_txstatus *status)
+{
+       struct b43_pio_txqueue *q;
+       struct b43_pio_txpacket *pack = NULL;
+       unsigned int total_len;
+       struct ieee80211_tx_info *info;
+
+       q = parse_cookie(dev, status->cookie, &pack);
+       if (unlikely(!q))
+               return;
+       B43_WARN_ON(!pack);
+
+       spin_lock(&q->lock); /* IRQs are already disabled. */
+
+       info = (void *)pack->skb;
+       memset(&info->status, 0, sizeof(info->status));
+
+       b43_fill_txstatus_report(info, status);
+
+       total_len = pack->skb->len + b43_txhdr_size(dev);
+       total_len = roundup(total_len, 4);
+       q->buffer_used -= total_len;
+       q->free_packet_slots += 1;
+
+       ieee80211_tx_status_irqsafe(dev->wl->hw, pack->skb);
+       pack->skb = NULL;
+       list_add(&pack->list, &q->packets_list);
+
+       if (q->stopped) {
+               ieee80211_wake_queue(dev->wl->hw, q->queue_prio);
+               q->stopped = 0;
+       }
+
+       spin_unlock(&q->lock);
+}
+
+void b43_pio_get_tx_stats(struct b43_wldev *dev,
+                         struct ieee80211_tx_queue_stats *stats)
+{
+       const int nr_queues = dev->wl->hw->queues;
+       struct b43_pio_txqueue *q;
+       unsigned long flags;
+       int i;
+
+       for (i = 0; i < nr_queues; i++) {
+               q = select_queue_by_priority(dev, i);
+
+               spin_lock_irqsave(&q->lock, flags);
+               stats[i].len = B43_PIO_MAX_NR_TXPACKETS - q->free_packet_slots;
+               stats[i].limit = B43_PIO_MAX_NR_TXPACKETS;
+               stats[i].count = q->nr_tx_packets;
+               spin_unlock_irqrestore(&q->lock, flags);
+       }
+}
+
+/* Returns whether we should fetch another frame. */
+static bool pio_rx_frame(struct b43_pio_rxqueue *q)
+{
+       struct b43_wldev *dev = q->dev;
+       struct b43_rxhdr_fw4 rxhdr;
+       u16 len;
+       u32 macstat;
+       unsigned int i, padding;
+       struct sk_buff *skb;
+       const char *err_msg = NULL;
+
+       memset(&rxhdr, 0, sizeof(rxhdr));
+
+       /* Check if we have data and wait for it to get ready. */
+       if (q->rev >= 8) {
+               u32 ctl;
+
+               ctl = b43_piorx_read32(q, B43_PIO8_RXCTL);
+               if (!(ctl & B43_PIO8_RXCTL_FRAMERDY))
+                       return 0;
+               b43_piorx_write32(q, B43_PIO8_RXCTL,
+                                 B43_PIO8_RXCTL_FRAMERDY);
+               for (i = 0; i < 10; i++) {
+                       ctl = b43_piorx_read32(q, B43_PIO8_RXCTL);
+                       if (ctl & B43_PIO8_RXCTL_DATARDY)
+                               goto data_ready;
+                       udelay(10);
+               }
+       } else {
+               u16 ctl;
+
+               ctl = b43_piorx_read16(q, B43_PIO_RXCTL);
+               if (!(ctl & B43_PIO_RXCTL_FRAMERDY))
+                       return 0;
+               b43_piorx_write16(q, B43_PIO_RXCTL,
+                                 B43_PIO_RXCTL_FRAMERDY);
+               for (i = 0; i < 10; i++) {
+                       ctl = b43_piorx_read16(q, B43_PIO_RXCTL);
+                       if (ctl & B43_PIO_RXCTL_DATARDY)
+                               goto data_ready;
+                       udelay(10);
+               }
+       }
+       b43dbg(q->dev->wl, "PIO RX timed out\n");
+       return 1;
+data_ready:
+
+       /* Get the preamble (RX header) */
+       if (q->rev >= 8) {
+               ssb_block_read(dev->dev, &rxhdr, sizeof(rxhdr),
+                              q->mmio_base + B43_PIO8_RXDATA,
+                              sizeof(u32));
+       } else {
+               ssb_block_read(dev->dev, &rxhdr, sizeof(rxhdr),
+                              q->mmio_base + B43_PIO_RXDATA,
+                              sizeof(u16));
+       }
+       /* Sanity checks. */
+       len = le16_to_cpu(rxhdr.frame_len);
+       if (unlikely(len > 0x700)) {
+               err_msg = "len > 0x700";
+               goto rx_error;
+       }
+       if (unlikely(len == 0)) {
+               err_msg = "len == 0";
+               goto rx_error;
+       }
+
+       macstat = le32_to_cpu(rxhdr.mac_status);
+       if (macstat & B43_RX_MAC_FCSERR) {
+               if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) {
+                       /* Drop frames with failed FCS. */
+                       err_msg = "Frame FCS error";
+                       goto rx_error;
+               }
+       }
+
+       /* We always pad 2 bytes, as that's what upstream code expects
+        * due to the RX-header being 30 bytes. In case the frame is
+        * unaligned, we pad another 2 bytes. */
+       padding = (macstat & B43_RX_MAC_PADDING) ? 2 : 0;
+       skb = dev_alloc_skb(len + padding + 2);
+       if (unlikely(!skb)) {
+               err_msg = "Out of memory";
+               goto rx_error;
+       }
+       skb_reserve(skb, 2);
+       skb_put(skb, len + padding);
+       if (q->rev >= 8) {
+               ssb_block_read(dev->dev, skb->data + padding, (len & ~3),
+                              q->mmio_base + B43_PIO8_RXDATA,
+                              sizeof(u32));
+               if (len & 3) {
+                       u32 value;
+                       char *data;
+
+                       /* Read the last few bytes. */
+                       value = b43_piorx_read32(q, B43_PIO8_RXDATA);
+                       data = &(skb->data[len + padding - 1]);
+                       switch (len & 3) {
+                       case 3:
+                               *data = (value >> 16);
+                               data--;
+                       case 2:
+                               *data = (value >> 8);
+                               data--;
+                       case 1:
+                               *data = value;
+                       }
+               }
+       } else {
+               ssb_block_read(dev->dev, skb->data + padding, (len & ~1),
+                              q->mmio_base + B43_PIO_RXDATA,
+                              sizeof(u16));
+               if (len & 1) {
+                       u16 value;
+
+                       /* Read the last byte. */
+                       value = b43_piorx_read16(q, B43_PIO_RXDATA);
+                       skb->data[len + padding - 1] = value;
+               }
+       }
+
+       b43_rx(q->dev, skb, &rxhdr);
+
+       return 1;
+
+rx_error:
+       if (err_msg)
+               b43dbg(q->dev->wl, "PIO RX error: %s\n", err_msg);
+       b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY);
+       return 1;
+}
+
+/* RX workqueue. We can sleep, yay! */
+static void b43_pio_rx_work(struct work_struct *work)
+{
+       struct b43_pio_rxqueue *q = container_of(work, struct b43_pio_rxqueue,
+                                                rx_work);
+       unsigned int budget = 50;
+       bool stop;
+
+       do {
+               spin_lock_irq(&q->lock);
+               stop = (pio_rx_frame(q) == 0);
+               spin_unlock_irq(&q->lock);
+               cond_resched();
+               if (stop)
+                       break;
+       } while (--budget);
+}
+
+/* Called with IRQs disabled. */
+void b43_pio_rx(struct b43_pio_rxqueue *q)
+{
+       /* Due to latency issues we must run the RX path in
+        * a workqueue to be able to schedule between packets. */
+       queue_work(q->dev->wl->hw->workqueue, &q->rx_work);
+}
+
+static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&q->lock, flags);
+       if (q->rev >= 8) {
+               b43_piotx_write32(q, B43_PIO8_TXCTL,
+                                 b43_piotx_read32(q, B43_PIO8_TXCTL)
+                                 | B43_PIO8_TXCTL_SUSPREQ);
+       } else {
+               b43_piotx_write16(q, B43_PIO_TXCTL,
+                                 b43_piotx_read16(q, B43_PIO_TXCTL)
+                                 | B43_PIO_TXCTL_SUSPREQ);
+       }
+       spin_unlock_irqrestore(&q->lock, flags);
+}
+
+static void b43_pio_tx_resume_queue(struct b43_pio_txqueue *q)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&q->lock, flags);
+       if (q->rev >= 8) {
+               b43_piotx_write32(q, B43_PIO8_TXCTL,
+                                 b43_piotx_read32(q, B43_PIO8_TXCTL)
+                                 & ~B43_PIO8_TXCTL_SUSPREQ);
+       } else {
+               b43_piotx_write16(q, B43_PIO_TXCTL,
+                                 b43_piotx_read16(q, B43_PIO_TXCTL)
+                                 & ~B43_PIO_TXCTL_SUSPREQ);
+       }
+       spin_unlock_irqrestore(&q->lock, flags);
+}
+
+void b43_pio_tx_suspend(struct b43_wldev *dev)
+{
+       b43_power_saving_ctl_bits(dev, B43_PS_AWAKE);
+       b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BK);
+       b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_BE);
+       b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VI);
+       b43_pio_tx_suspend_queue(dev->pio.tx_queue_AC_VO);
+       b43_pio_tx_suspend_queue(dev->pio.tx_queue_mcast);
+}
+
+void b43_pio_tx_resume(struct b43_wldev *dev)
+{
+       b43_pio_tx_resume_queue(dev->pio.tx_queue_mcast);
+       b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VO);
+       b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_VI);
+       b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BE);
+       b43_pio_tx_resume_queue(dev->pio.tx_queue_AC_BK);
+       b43_power_saving_ctl_bits(dev, 0);
+}
diff --git a/package/b43/src/pio.h b/package/b43/src/pio.h
new file mode 100644 (file)
index 0000000..6c174c9
--- /dev/null
@@ -0,0 +1,216 @@
+#ifndef B43_PIO_H_
+#define B43_PIO_H_
+
+#include "b43.h"
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+
+
+/*** Registers for PIO queues up to revision 7. ***/
+/* TX queue. */
+#define B43_PIO_TXCTL                  0x00
+#define  B43_PIO_TXCTL_WRITELO         0x0001
+#define  B43_PIO_TXCTL_WRITEHI         0x0002
+#define  B43_PIO_TXCTL_EOF             0x0004
+#define  B43_PIO_TXCTL_FREADY          0x0008
+#define  B43_PIO_TXCTL_FLUSHREQ                0x0020
+#define  B43_PIO_TXCTL_FLUSHPEND       0x0040
+#define  B43_PIO_TXCTL_SUSPREQ         0x0080
+#define  B43_PIO_TXCTL_QSUSP           0x0100
+#define  B43_PIO_TXCTL_COMMCNT         0xFC00
+#define  B43_PIO_TXCTL_COMMCNT_SHIFT   10
+#define B43_PIO_TXDATA                 0x02
+#define B43_PIO_TXQBUFSIZE             0x04
+/* RX queue. */
+#define B43_PIO_RXCTL                  0x00
+#define  B43_PIO_RXCTL_FRAMERDY                0x0001
+#define  B43_PIO_RXCTL_DATARDY         0x0002
+#define B43_PIO_RXDATA                 0x02
+
+/*** Registers for PIO queues revision 8 and later. ***/
+/* TX queue */
+#define B43_PIO8_TXCTL                 0x00
+#define  B43_PIO8_TXCTL_0_7            0x00000001
+#define  B43_PIO8_TXCTL_8_15           0x00000002
+#define  B43_PIO8_TXCTL_16_23          0x00000004
+#define  B43_PIO8_TXCTL_24_31          0x00000008
+#define  B43_PIO8_TXCTL_EOF            0x00000010
+#define  B43_PIO8_TXCTL_FREADY         0x00000080
+#define  B43_PIO8_TXCTL_SUSPREQ                0x00000100
+#define  B43_PIO8_TXCTL_QSUSP          0x00000200
+#define  B43_PIO8_TXCTL_FLUSHREQ       0x00000400
+#define  B43_PIO8_TXCTL_FLUSHPEND      0x00000800
+#define B43_PIO8_TXDATA                        0x04
+/* RX queue */
+#define B43_PIO8_RXCTL                 0x00
+#define  B43_PIO8_RXCTL_FRAMERDY       0x00000001
+#define  B43_PIO8_RXCTL_DATARDY                0x00000002
+#define B43_PIO8_RXDATA                        0x04
+
+
+/* The maximum number of TX-packets the HW can handle. */
+#define B43_PIO_MAX_NR_TXPACKETS       32
+
+
+#ifdef CONFIG_B43_PIO
+
+struct b43_pio_txpacket {
+       /* Pointer to the TX queue we belong to. */
+       struct b43_pio_txqueue *queue;
+       /* The TX data packet. */
+       struct sk_buff *skb;
+       /* Index in the (struct b43_pio_txqueue)->packets array. */
+       u8 index;
+
+       struct list_head list;
+};
+
+struct b43_pio_txqueue {
+       struct b43_wldev *dev;
+       spinlock_t lock;
+       u16 mmio_base;
+
+       /* The device queue buffer size in bytes. */
+       u16 buffer_size;
+       /* The number of used bytes in the device queue buffer. */
+       u16 buffer_used;
+       /* The number of packets that can still get queued.
+        * This is decremented on queueing a packet and incremented
+        * after receiving the transmit status. */
+       u16 free_packet_slots;
+
+       /* True, if the mac80211 queue was stopped due to overflow at TX. */
+       bool stopped;
+       /* Our b43 queue index number */
+       u8 index;
+       /* The mac80211 QoS queue priority. */
+       u8 queue_prio;
+
+       /* Buffer for TX packet meta data. */
+       struct b43_pio_txpacket packets[B43_PIO_MAX_NR_TXPACKETS];
+       struct list_head packets_list;
+
+       /* Total number of transmitted packets. */
+       unsigned int nr_tx_packets;
+
+       /* Shortcut to the 802.11 core revision. This is to
+        * avoid horrible pointer dereferencing in the fastpaths. */
+       u8 rev;
+};
+
+struct b43_pio_rxqueue {
+       struct b43_wldev *dev;
+       spinlock_t lock;
+       u16 mmio_base;
+
+       /* Work to reduce latency issues on RX. */
+       struct work_struct rx_work;
+
+       /* Shortcut to the 802.11 core revision. This is to
+        * avoid horrible pointer dereferencing in the fastpaths. */
+       u8 rev;
+};
+
+
+static inline u16 b43_piotx_read16(struct b43_pio_txqueue *q, u16 offset)
+{
+       return b43_read16(q->dev, q->mmio_base + offset);
+}
+
+static inline u32 b43_piotx_read32(struct b43_pio_txqueue *q, u16 offset)
+{
+       return b43_read32(q->dev, q->mmio_base + offset);
+}
+
+static inline void b43_piotx_write16(struct b43_pio_txqueue *q,
+                                    u16 offset, u16 value)
+{
+       b43_write16(q->dev, q->mmio_base + offset, value);
+}
+
+static inline void b43_piotx_write32(struct b43_pio_txqueue *q,
+                                    u16 offset, u32 value)
+{
+       b43_write32(q->dev, q->mmio_base + offset, value);
+}
+
+
+static inline u16 b43_piorx_read16(struct b43_pio_rxqueue *q, u16 offset)
+{
+       return b43_read16(q->dev, q->mmio_base + offset);
+}
+
+static inline u32 b43_piorx_read32(struct b43_pio_rxqueue *q, u16 offset)
+{
+       return b43_read32(q->dev, q->mmio_base + offset);
+}
+
+static inline void b43_piorx_write16(struct b43_pio_rxqueue *q,
+                                    u16 offset, u16 value)
+{
+       b43_write16(q->dev, q->mmio_base + offset, value);
+}
+
+static inline void b43_piorx_write32(struct b43_pio_rxqueue *q,
+                                    u16 offset, u32 value)
+{
+       b43_write32(q->dev, q->mmio_base + offset, value);
+}
+
+
+int b43_pio_init(struct b43_wldev *dev);
+void b43_pio_stop(struct b43_wldev *dev);
+void b43_pio_free(struct b43_wldev *dev);
+
+int b43_pio_tx(struct b43_wldev *dev, struct sk_buff *skb);
+void b43_pio_handle_txstatus(struct b43_wldev *dev,
+                            const struct b43_txstatus *status);
+void b43_pio_get_tx_stats(struct b43_wldev *dev,
+                         struct ieee80211_tx_queue_stats *stats);
+void b43_pio_rx(struct b43_pio_rxqueue *q);
+
+void b43_pio_tx_suspend(struct b43_wldev *dev);
+void b43_pio_tx_resume(struct b43_wldev *dev);
+
+
+#else /* CONFIG_B43_PIO */
+
+
+static inline int b43_pio_init(struct b43_wldev *dev)
+{
+       return 0;
+}
+static inline void b43_pio_free(struct b43_wldev *dev)
+{
+}
+static inline void b43_pio_stop(struct b43_wldev *dev)
+{
+}
+static inline int b43_pio_tx(struct b43_wldev *dev,
+                            struct sk_buff *skb)
+{
+       return 0;
+}
+static inline void b43_pio_handle_txstatus(struct b43_wldev *dev,
+                                          const struct b43_txstatus *status)
+{
+}
+static inline void b43_pio_get_tx_stats(struct b43_wldev *dev,
+                                       struct ieee80211_tx_queue_stats *stats)
+{
+}
+static inline void b43_pio_rx(struct b43_pio_rxqueue *q)
+{
+}
+static inline void b43_pio_tx_suspend(struct b43_wldev *dev)
+{
+}
+static inline void b43_pio_tx_resume(struct b43_wldev *dev)
+{
+}
+
+#endif /* CONFIG_B43_PIO */
+#endif /* B43_PIO_H_ */
index e632125..daa9421 100644 (file)
@@ -204,42 +204,43 @@ static void b43_wa_rt(struct b43_wldev *dev) /* Rotor table */
                b43_ofdmtab_write32(dev, B43_OFDMTAB_ROTOR, i, b43_tab_rotor[i]);
 }
 
+static void b43_write_null_nst(struct b43_wldev *dev)
+{
+       int i;
+
+       for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
+               b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, 0);
+}
+
+static void b43_write_nst(struct b43_wldev *dev, const u16 *nst)
+{
+       int i;
+
+       for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
+               b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE, i, nst[i]);
+}
+
 static void b43_wa_nst(struct b43_wldev *dev) /* Noise scale table */
 {
        struct b43_phy *phy = &dev->phy;
-       int i;
 
        if (phy->type == B43_PHYTYPE_A) {
                if (phy->rev <= 1)
-                       for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
-                               b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
-                                                       i, 0);
+                       b43_write_null_nst(dev);
                else if (phy->rev == 2)
-                       for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
-                               b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
-                                                       i, b43_tab_noisescalea2[i]);
+                       b43_write_nst(dev, b43_tab_noisescalea2);
                else if (phy->rev == 3)
-                       for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
-                               b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
-                                                       i, b43_tab_noisescalea3[i]);
+                       b43_write_nst(dev, b43_tab_noisescalea3);
                else
-                       for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
-                               b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
-                                                       i, b43_tab_noisescaleg3[i]);
+                       b43_write_nst(dev, b43_tab_noisescaleg3);
        } else {
                if (phy->rev >= 6) {
                        if (b43_phy_read(dev, B43_PHY_ENCORE) & B43_PHY_ENCORE_EN)
-                               for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
-                                       b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
-                                               i, b43_tab_noisescaleg3[i]);
+                               b43_write_nst(dev, b43_tab_noisescaleg3);
                        else
-                               for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
-                                       b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
-                                               i, b43_tab_noisescaleg2[i]);
+                               b43_write_nst(dev, b43_tab_noisescaleg2);
                } else {
-                       for (i = 0; i < B43_TAB_NOISESCALE_SIZE; i++)
-                               b43_ofdmtab_write16(dev, B43_OFDMTAB_NOISESCALE,
-                                                       i, b43_tab_noisescaleg1[i]);
+                       b43_write_nst(dev, b43_tab_noisescaleg1);
                }
        }
 }
index 4014b6c..f9e1cff 100644 (file)
@@ -30,6 +30,7 @@
 #include "xmit.h"
 #include "phy.h"
 #include "dma.h"
+#include "pio.h"
 
 
 /* Extract the bitrate index out of a CCK PLCP header. */
@@ -184,14 +185,14 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                       u8 *_txhdr,
                       const unsigned char *fragment_data,
                       unsigned int fragment_len,
-                      const struct ieee80211_tx_control *txctl,
+                      const struct ieee80211_tx_info *info,
                       u16 cookie)
 {
        struct b43_txhdr *txhdr = (struct b43_txhdr *)_txhdr;
        const struct b43_phy *phy = &dev->phy;
        const struct ieee80211_hdr *wlhdr =
            (const struct ieee80211_hdr *)fragment_data;
-       int use_encryption = (!(txctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT));
+       int use_encryption = (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT));
        u16 fctl = le16_to_cpu(wlhdr->frame_control);
        struct ieee80211_rate *fbrate;
        u8 rate, rate_fb;
@@ -200,13 +201,14 @@ int b43_generate_txhdr(struct b43_wldev *dev,
        u32 mac_ctl = 0;
        u16 phy_ctl = 0;
        u8 extra_ft = 0;
+       struct ieee80211_rate *txrate;
 
        memset(txhdr, 0, sizeof(*txhdr));
 
-       WARN_ON(!txctl->tx_rate);
-       rate = txctl->tx_rate ? txctl->tx_rate->hw_value : B43_CCK_RATE_1MB;
+       txrate = ieee80211_get_tx_rate(dev->wl->hw, info);
+       rate = txrate ? txrate->hw_value : B43_CCK_RATE_1MB;
        rate_ofdm = b43_is_ofdm_rate(rate);
-       fbrate = txctl->alt_retry_rate ? : txctl->tx_rate;
+       fbrate = ieee80211_get_alt_retry_rate(dev->wl->hw, info) ? : txrate;
        rate_fb = fbrate->hw_value;
        rate_fb_ofdm = b43_is_ofdm_rate(rate_fb);
 
@@ -226,15 +228,13 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                 * use the original dur_id field. */
                txhdr->dur_fb = wlhdr->duration_id;
        } else {
-               txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw,
-                                                                txctl->vif,
-                                                                fragment_len,
-                                                                fbrate);
+               txhdr->dur_fb = ieee80211_generic_frame_duration(
+                       dev->wl->hw, info->control.vif, fragment_len, fbrate);
        }
 
        plcp_fragment_len = fragment_len + FCS_LEN;
        if (use_encryption) {
-               u8 key_idx = (u16) (txctl->key_idx);
+               u8 key_idx = info->control.hw_key->hw_key_idx;
                struct b43_key *key;
                int wlhdr_len;
                size_t iv_len;
@@ -252,7 +252,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                }
 
                /* Hardware appends ICV. */
-               plcp_fragment_len += txctl->icv_len;
+               plcp_fragment_len += info->control.icv_len;
 
                key_idx = b43_kidx_to_fw(dev, key_idx);
                mac_ctl |= (key_idx << B43_TXH_MAC_KEYIDX_SHIFT) &
@@ -260,7 +260,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                mac_ctl |= (key->algorithm << B43_TXH_MAC_KEYALG_SHIFT) &
                           B43_TXH_MAC_KEYALG;
                wlhdr_len = ieee80211_get_hdrlen(fctl);
-               iv_len = min((size_t) txctl->iv_len,
+               iv_len = min((size_t) info->control.iv_len,
                             ARRAY_SIZE(txhdr->iv));
                memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len);
        }
@@ -291,10 +291,10 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                phy_ctl |= B43_TXH_PHY_ENC_OFDM;
        else
                phy_ctl |= B43_TXH_PHY_ENC_CCK;
-       if (txctl->flags & IEEE80211_TXCTL_SHORT_PREAMBLE)
+       if (info->flags & IEEE80211_TX_CTL_SHORT_PREAMBLE)
                phy_ctl |= B43_TXH_PHY_SHORTPRMBL;
 
-       switch (b43_ieee80211_antenna_sanitize(dev, txctl->antenna_sel_tx)) {
+       switch (b43_ieee80211_antenna_sanitize(dev, info->antenna_sel_tx)) {
        case 0: /* Default */
                phy_ctl |= B43_TXH_PHY_ANT01AUTO;
                break;
@@ -315,34 +315,36 @@ int b43_generate_txhdr(struct b43_wldev *dev,
        }
 
        /* MAC control */
-       if (!(txctl->flags & IEEE80211_TXCTL_NO_ACK))
+       if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
                mac_ctl |= B43_TXH_MAC_ACK;
        if (!(((fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) &&
              ((fctl & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)))
                mac_ctl |= B43_TXH_MAC_HWSEQ;
-       if (txctl->flags & IEEE80211_TXCTL_FIRST_FRAGMENT)
+       if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
                mac_ctl |= B43_TXH_MAC_STMSDU;
        if (phy->type == B43_PHYTYPE_A)
                mac_ctl |= B43_TXH_MAC_5GHZ;
-       if (txctl->flags & IEEE80211_TXCTL_LONG_RETRY_LIMIT)
+       if (info->flags & IEEE80211_TX_CTL_LONG_RETRY_LIMIT)
                mac_ctl |= B43_TXH_MAC_LONGFRAME;
 
        /* Generate the RTS or CTS-to-self frame */
-       if ((txctl->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
-           (txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
+       if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) ||
+           (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) {
                unsigned int len;
                struct ieee80211_hdr *hdr;
                int rts_rate, rts_rate_fb;
                int rts_rate_ofdm, rts_rate_fb_ofdm;
                struct b43_plcp_hdr6 *plcp;
+               struct ieee80211_rate *rts_cts_rate;
 
-               WARN_ON(!txctl->rts_cts_rate);
-               rts_rate = txctl->rts_cts_rate ? txctl->rts_cts_rate->hw_value : B43_CCK_RATE_1MB;
+               rts_cts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info);
+
+               rts_rate = rts_cts_rate ? rts_cts_rate->hw_value : B43_CCK_RATE_1MB;
                rts_rate_ofdm = b43_is_ofdm_rate(rts_rate);
                rts_rate_fb = b43_calc_fallback_rate(rts_rate);
                rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb);
 
-               if (txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) {
+               if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) {
                        struct ieee80211_cts *cts;
 
                        if (b43_is_old_txhdr_format(dev)) {
@@ -352,9 +354,9 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                                cts = (struct ieee80211_cts *)
                                        (txhdr->new_format.rts_frame);
                        }
-                       ieee80211_ctstoself_get(dev->wl->hw, txctl->vif,
+                       ieee80211_ctstoself_get(dev->wl->hw, info->control.vif,
                                                fragment_data, fragment_len,
-                                               txctl, cts);
+                                               info, cts);
                        mac_ctl |= B43_TXH_MAC_SENDCTS;
                        len = sizeof(struct ieee80211_cts);
                } else {
@@ -367,9 +369,9 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                                rts = (struct ieee80211_rts *)
                                        (txhdr->new_format.rts_frame);
                        }
-                       ieee80211_rts_get(dev->wl->hw, txctl->vif,
+                       ieee80211_rts_get(dev->wl->hw, info->control.vif,
                                          fragment_data, fragment_len,
-                                         txctl, rts);
+                                         info, rts);
                        mac_ctl |= B43_TXH_MAC_SENDRTS;
                        len = sizeof(struct ieee80211_rts);
                }
@@ -512,7 +514,6 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
        u32 macstat;
        u16 chanid;
        u16 phytype;
-       u8 jssi;
        int padding;
 
        memset(&status, 0, sizeof(status));
@@ -520,7 +521,6 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
        /* Get metadata about the frame from the header. */
        phystat0 = le16_to_cpu(rxhdr->phy_status0);
        phystat3 = le16_to_cpu(rxhdr->phy_status3);
-       jssi = rxhdr->jssi;
        macstat = le32_to_cpu(rxhdr->mac_status);
        mactime = le16_to_cpu(rxhdr->mac_time);
        chanstat = le16_to_cpu(rxhdr->channel);
@@ -574,13 +574,21 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
                }
        }
 
-       status.ssi = b43_rssi_postprocess(dev, jssi,
-                                         (phystat0 & B43_RX_PHYST0_OFDM),
-                                         (phystat0 & B43_RX_PHYST0_GAINCTL),
-                                         (phystat3 & B43_RX_PHYST3_TRSTATE));
+       /* Link quality statistics */
        status.noise = dev->stats.link_noise;
-       /* the next line looks wrong, but is what mac80211 wants */
-       status.signal = (jssi * 100) / B43_RX_MAX_SSI;
+       if ((chanstat & B43_RX_CHAN_PHYTYPE) == B43_PHYTYPE_N) {
+//             s8 rssi = max(rxhdr->power0, rxhdr->power1);
+               //TODO: Find out what the rssi value is (dBm or percentage?)
+               //      and also find out what the maximum possible value is.
+               //      Fill status.ssi and status.signal fields.
+       } else {
+               status.signal = b43_rssi_postprocess(dev, rxhdr->jssi,
+                                                 (phystat0 & B43_RX_PHYST0_OFDM),
+                                                 (phystat0 & B43_RX_PHYST0_GAINCTL),
+                                                 (phystat3 & B43_RX_PHYST3_TRSTATE));
+               status.qual = (rxhdr->jssi * 100) / B43_RX_MAX_SSI;
+       }
+
        if (phystat0 & B43_RX_PHYST0_OFDM)
                status.rate_idx = b43_plcp_get_bitrate_idx_ofdm(plcp,
                                                phytype == B43_PHYTYPE_A);
@@ -589,12 +597,16 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
        status.antenna = !!(phystat0 & B43_RX_PHYST0_ANT);
 
        /*
-        * If monitors are present get full 64-bit timestamp. This
-        * code assumes we get to process the packet within 16 bits
-        * of timestamp, i.e. about 65 milliseconds after the PHY
-        * received the first symbol.
+        * All frames on monitor interfaces and beacons always need a full
+        * 64-bit timestamp. Monitor interfaces need it for diagnostic
+        * purposes and beacons for IBSS merging.
+        * This code assumes we get to process the packet within 16 bits
+        * of timestamp, i.e. about 65 milliseconds after the PHY received
+        * the first symbol.
         */
-       if (dev->wl->radiotap_enabled) {
+       if (((fctl & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
+           == (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON)) ||
+           dev->wl->radiotap_enabled) {
                u16 low_mactime_now;
 
                b43_tsf_read(dev, &status.mactime);
@@ -664,67 +676,54 @@ void b43_handle_txstatus(struct b43_wldev *dev,
                        dev->wl->ieee_stats.dot11RTSSuccessCount++;
        }
 
-       b43_dma_handle_txstatus(dev, status);
+       if (b43_using_pio_transfers(dev))
+               b43_pio_handle_txstatus(dev, status);
+       else
+               b43_dma_handle_txstatus(dev, status);
 }
 
-/* Handle TX status report as received through DMA/PIO queues */
-void b43_handle_hwtxstatus(struct b43_wldev *dev,
-                          const struct b43_hwtxstatus *hw)
+/* Fill out the mac80211 TXstatus report based on the b43-specific
+ * txstatus report data. This returns a boolean whether the frame was
+ * successfully transmitted. */
+bool b43_fill_txstatus_report(struct ieee80211_tx_info *report,
+                             const struct b43_txstatus *status)
 {
-       struct b43_txstatus status;
-       u8 tmp;
-
-       status.cookie = le16_to_cpu(hw->cookie);
-       status.seq = le16_to_cpu(hw->seq);
-       status.phy_stat = hw->phy_stat;
-       tmp = hw->count;
-       status.frame_count = (tmp >> 4);
-       status.rts_count = (tmp & 0x0F);
-       tmp = hw->flags;
-       status.supp_reason = ((tmp & 0x1C) >> 2);
-       status.pm_indicated = !!(tmp & 0x80);
-       status.intermediate = !!(tmp & 0x40);
-       status.for_ampdu = !!(tmp & 0x20);
-       status.acked = !!(tmp & 0x02);
-
-       b43_handle_txstatus(dev, &status);
+       bool frame_success = 1;
+
+       if (status->acked) {
+               /* The frame was ACKed. */
+               report->flags |= IEEE80211_TX_STAT_ACK;
+       } else {
+               /* The frame was not ACKed... */
+               if (!(report->flags & IEEE80211_TX_CTL_NO_ACK)) {
+                       /* ...but we expected an ACK. */
+                       frame_success = 0;
+                       report->status.excessive_retries = 1;
+               }
+       }
+       if (status->frame_count == 0) {
+               /* The frame was not transmitted at all. */
+               report->status.retry_count = 0;
+       } else
+               report->status.retry_count = status->frame_count - 1;
+
+       return frame_success;
 }
 
 /* Stop any TX operation on the device (suspend the hardware queues) */
 void b43_tx_suspend(struct b43_wldev *dev)
 {
-       b43_dma_tx_suspend(dev);
+       if (b43_using_pio_transfers(dev))
+               b43_pio_tx_suspend(dev);
+       else
+               b43_dma_tx_suspend(dev);
 }
 
 /* Resume any TX operation on the device (resume the hardware queues) */
 void b43_tx_resume(struct b43_wldev *dev)
 {
-       b43_dma_tx_resume(dev);
-}
-
-#if 0
-static void upload_qos_parms(struct b43_wldev *dev,
-                            const u16 * parms, u16 offset)
-{
-       int i;
-
-       for (i = 0; i < B43_NR_QOSPARMS; i++) {
-               b43_shm_write16(dev, B43_SHM_SHARED,
-                               offset + (i * 2), parms[i]);
-       }
-}
-#endif
-
-/* Initialize the QoS parameters */
-void b43_qos_init(struct b43_wldev *dev)
-{
-       /* FIXME: This function must probably be called from the mac80211
-        * config callback. */
-       return;
-
-       b43_hf_write(dev, b43_hf_read(dev) | B43_HF_EDCF);
-       //FIXME kill magic
-       b43_write16(dev, 0x688, b43_read16(dev, 0x688) | 0x4);
-
-       /*TODO: We might need some stack support here to get the values. */
+       if (b43_using_pio_transfers(dev))
+               b43_pio_tx_resume(dev);
+       else
+               b43_dma_tx_resume(dev);
 }
index 4176503..0215faf 100644 (file)
@@ -178,7 +178,7 @@ int b43_generate_txhdr(struct b43_wldev *dev,
                       u8 * txhdr,
                       const unsigned char *fragment_data,
                       unsigned int fragment_len,
-                      const struct ieee80211_tx_control *txctl, u16 cookie);
+                      const struct ieee80211_tx_info *txctl, u16 cookie);
 
 /* Transmit Status */
 struct b43_txstatus {
@@ -207,25 +207,24 @@ enum {
        B43_TXST_SUPP_ABNACK,   /* Afterburner NACK */
 };
 
-/* Transmit Status as received through DMA/PIO on old chips */
-struct b43_hwtxstatus {
-       PAD_BYTES(4);
-       __le16 cookie;
-       u8 flags;
-       u8 count;
-        PAD_BYTES(2);
-       __le16 seq;
-       u8 phy_stat;
-        PAD_BYTES(1);
-} __attribute__ ((__packed__));
-
 /* Receive header for v4 firmware. */
 struct b43_rxhdr_fw4 {
        __le16 frame_len;       /* Frame length */
         PAD_BYTES(2);
        __le16 phy_status0;     /* PHY RX Status 0 */
-       __u8 jssi;              /* PHY RX Status 1: JSSI */
-       __u8 sig_qual;          /* PHY RX Status 1: Signal Quality */
+       union {
+               /* RSSI for A/B/G-PHYs */
+               struct {
+                       __u8 jssi;      /* PHY RX Status 1: JSSI */
+                       __u8 sig_qual;  /* PHY RX Status 1: Signal Quality */
+               } __attribute__ ((__packed__));
+
+               /* RSSI for N-PHYs */
+               struct {
+                       __s8 power0;    /* PHY RX Status 1: Power 0 */
+                       __s8 power1;    /* PHY RX Status 1: Power 1 */
+               } __attribute__ ((__packed__));
+       } __attribute__ ((__packed__));
        __le16 phy_status2;     /* PHY RX Status 2 */
        __le16 phy_status3;     /* PHY RX Status 3 */
        __le32 mac_status;      /* MAC RX status */
@@ -295,25 +294,12 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr);
 
 void b43_handle_txstatus(struct b43_wldev *dev,
                         const struct b43_txstatus *status);
-
-void b43_handle_hwtxstatus(struct b43_wldev *dev,
-                          const struct b43_hwtxstatus *hw);
+bool b43_fill_txstatus_report(struct ieee80211_tx_info *report,
+                             const struct b43_txstatus *status);
 
 void b43_tx_suspend(struct b43_wldev *dev);
 void b43_tx_resume(struct b43_wldev *dev);
 
-#define B43_NR_QOSPARMS                22
-enum {
-       B43_QOSPARM_TXOP = 0,
-       B43_QOSPARM_CWMIN,
-       B43_QOSPARM_CWMAX,
-       B43_QOSPARM_CWCUR,
-       B43_QOSPARM_AIFS,
-       B43_QOSPARM_BSLOTS,
-       B43_QOSPARM_REGGAP,
-       B43_QOSPARM_STATUS,
-};
-void b43_qos_init(struct b43_wldev *dev);
 
 /* Helper functions for converting the key-table index from "firmware-format"
  * to "raw-format" and back. The firmware API changed for this at some revision.