diff --git a/package/b43/Makefile b/package/b43/Makefile index 3d5eb264c2..6616585e2e 100644 --- a/package/b43/Makefile +++ b/package/b43/Makefile @@ -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) \ diff --git a/package/b43/src/Makefile b/package/b43/src/Makefile index ac1329dba0..8c52b0b986 100644 --- a/package/b43/src/Makefile +++ b/package/b43/src/Makefile @@ -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 diff --git a/package/b43/src/b43.h b/package/b43/src/b43.h index 0dc1aaf46b..e919189919 100644 --- a/package/b43/src/b43.h +++ b/package/b43/src/b43.h @@ -14,6 +14,12 @@ #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 @@ -69,6 +75,23 @@ #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 @@ -88,11 +111,14 @@ #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 *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_ring0; - struct b43_dmaring *rx_ring3; /* only available on core.rev < 5 */ + 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!) */ diff --git a/package/b43/src/debugfs.c b/package/b43/src/debugfs.c index e38ed0fe72..210e2789c1 100644 --- a/package/b43/src/debugfs.c +++ b/package/b43/src/debugfs.c @@ -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; + expire = time + expire; - 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)); - } + 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; diff --git a/package/b43/src/debugfs.h b/package/b43/src/debugfs.h index 6eebe858db..c75cff4151 100644 --- a/package/b43/src/debugfs.h +++ b/package/b43/src/debugfs.h @@ -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, }; diff --git a/package/b43/src/dma.c b/package/b43/src/dma.c index 3dfb28a34b..b4eadd908b 100644 --- a/package/b43/src/dma.c +++ b/package/b43/src/dma.c @@ -38,6 +38,7 @@ #include #include #include +#include /* 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; + if (b43_using_pio_transfers(dev)) + return; + dma = &dev->dma; - 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; + 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 */ diff --git a/package/b43/src/dma.h b/package/b43/src/dma.h index c0d6b69e65..d1eb5c0848 100644 --- a/package/b43/src/dma.h +++ b/package/b43/src/dma.h @@ -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_ */ diff --git a/package/b43/src/leds.c b/package/b43/src/leds.c index 4b590d8c65..36a9c42df8 100644 --- a/package/b43/src/leds.c +++ b/package/b43/src/leds.c @@ -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); diff --git a/package/b43/src/lo.c b/package/b43/src/lo.c index d890f366a2..9c854d6aae 100644 --- a/package/b43/src/lo.c +++ b/package/b43/src/lo.c @@ -36,17 +36,28 @@ #include -/* 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->att); + b43_radio_write16(dev, txctl_reg, + (b43_radio_read16(dev, txctl_reg) & ~txctl_value) + | (rfatt->with_padmix) ? txctl_value : 0); - 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)); + 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)); - 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; + b43_phy_set_baseband_attenuation(dev, bbatt->att); + lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain); - 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)); + lo_measure_restore(dev, &saved_regs); + b43_mac_enable(dev); - 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(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 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; - - 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; - } + cal = kmalloc(sizeof(*cal), GFP_KERNEL); + if (!cal) { + b43warn(dev->wl, "LO calib: out of memory\n"); + return NULL; } - if (is_initializing && control->used) - goto error; + 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; -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); + return cal; } -static void validate_all_loctls(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_validate_loctl); + 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; } -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; - } -} - -static void reset_all_loctl_calibration_states(struct b43_wldev *dev) -{ - b43_call_for_each_loctl(dev, do_reset_calib); -} - -#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); + 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; - B43_WARN_ON((phy->type != B43_PHYTYPE_B) && - (phy->type != B43_PHYTYPE_G)); + BUILD_BUG_ON(B43_DC_LT_SIZE != 32); + B43_WARN_ON(lo->rfatt_list.len * lo->bbatt_list.len > 64); - sav.old_channel = phy->channel; - lo_measure_setup(dev, &sav); - reset_all_loctl_calibration_states(dev); - lo_measure(dev); - lo_measure_restore(dev, &sav); + power_vector = lo->power_vector; + if (!update_all && !power_vector) + return; /* Nothing to do. */ - validate_all_loctls(dev); + /* Suspend the MAC now to avoid continuous suspend/enable + * cycles in the loop. */ + b43_mac_suspend(dev); - phy->lo_control->lo_measured = 1; - phy->lo_control->rebuild = 0; -} + for (i = 0; i < B43_DC_LT_SIZE * 2; i++) { + struct b43_lo_calib *cal; + int idx; + u16 val; -#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; + 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) -{ -} -#endif - -static inline void fixup_rfatt_for_txcontrol(struct b43_rfatt *rf, - u8 tx_control) -{ - if (tx_control & B43_TXCTL_TXMIX) { - if (rf->att < 5) - rf->att = 4; + 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); +} + +/* 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 (!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) -{ - control->used = 0; -} - -void b43_lo_g_ctl_mark_all_unused(struct b43_wldev *dev) +/* Periodic LO maintanance work */ +void b43_lo_g_maintanance_work(struct b43_wldev *dev) { 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; - b43_call_for_each_loctl(dev, do_mark_unused); - lo->rebuild = 1; + 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_cur_used(struct b43_wldev *dev) +void b43_lo_g_cleanup(struct b43_wldev *dev) +{ + struct b43_txpower_lo_control *lo = dev->phy.lo_control; + struct b43_lo_calib *cal, *tmp; + + if (!lo) + return; + list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) { + list_del(&cal->list); + kfree(cal); + } +} + +/* 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); + } } diff --git a/package/b43/src/lo.h b/package/b43/src/lo.h index 455615d1f8..1da321cabc 100644 --- a/package/b43/src/lo.h +++ b/package/b43/src/lo.h @@ -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 +/* 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; +}; -/* 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. - */ +/* 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_ */ diff --git a/package/b43/src/main.c b/package/b43/src/main.c index afb109447a..cbb317bb34 100644 --- a/package/b43/src/main.c +++ b/package/b43/src/main.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -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; + 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; } - 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; - } - 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); - } + ant = b43_antenna_to_phyctl(antenna); - /* FIXME We also need to set the other flags of the PHY control field somewhere. */ - - /* 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(¶ms, 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); } -/* Expects wl->mutex locked */ -static int b43_switch_phymode(struct b43_wl *wl, unsigned int new_mode) +static const char * band_to_string(enum ieee80211_band band) { - struct b43_wldev *up_dev; + 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_band(struct b43_wl *wl, struct ieee80211_channel *chan) +{ + 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; diff --git a/package/b43/src/main.h b/package/b43/src/main.h index 2d52d9de93..dad23c42b4 100644 --- a/package/b43/src/main.h +++ b/package/b43/src/main.h @@ -38,6 +38,10 @@ /* 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_ */ diff --git a/package/b43/src/nphy.c b/package/b43/src/nphy.c index 705131ef4b..644eed993b 100644 --- a/package/b43/src/nphy.c +++ b/package/b43/src/nphy.c @@ -29,8 +29,6 @@ #include "nphy.h" #include "tables_nphy.h" -#include - 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); diff --git a/package/b43/src/nphy.h b/package/b43/src/nphy.h index 5d95118b81..faf46b9cbf 100644 --- a/package/b43/src/nphy.h +++ b/package/b43/src/nphy.h @@ -919,6 +919,10 @@ 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_ */ diff --git a/package/b43/src/pcmcia.c b/package/b43/src/pcmcia.c index b79a6bd539..b8aa16307f 100644 --- a/package/b43/src/pcmcia.c +++ b/package/b43/src/pcmcia.c @@ -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); diff --git a/package/b43/src/phy.c b/package/b43/src/phy.c index 71507b260b..305d4cd6fd 100644 --- a/package/b43/src/phy.c +++ b/package/b43/src/phy.c @@ -28,6 +28,7 @@ #include #include #include +#include #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++) { diff --git a/package/b43/src/phy.h b/package/b43/src/phy.h index 6d165d8221..4aab109035 100644 --- a/package/b43/src/phy.h +++ b/package/b43/src/phy.h @@ -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 index 0000000000..8b1555d95f --- /dev/null +++ b/package/b43/src/pio.c @@ -0,0 +1,842 @@ +/* + + Broadcom B43 wireless driver + + PIO data transfer + + Copyright (c) 2005-2008 Michael Buesch + + 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 + + +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 index 0000000000..6c174c91ca --- /dev/null +++ b/package/b43/src/pio.h @@ -0,0 +1,216 @@ +#ifndef B43_PIO_H_ +#define B43_PIO_H_ + +#include "b43.h" + +#include +#include +#include +#include + + +/*** 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_ */ diff --git a/package/b43/src/wa.c b/package/b43/src/wa.c index e632125cb7..daa94211f8 100644 --- a/package/b43/src/wa.c +++ b/package/b43/src/wa.c @@ -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); } } } diff --git a/package/b43/src/xmit.c b/package/b43/src/xmit.c index 4014b6c827..f9e1cff2ae 100644 --- a/package/b43/src/xmit.c +++ b/package/b43/src/xmit.c @@ -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; + bool frame_success = 1; - 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); + 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; - b43_handle_txstatus(dev, &status); + 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); } diff --git a/package/b43/src/xmit.h b/package/b43/src/xmit.h index 4176503955..0215faf475 100644 --- a/package/b43/src/xmit.h +++ b/package/b43/src/xmit.h @@ -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.