diff options
Diffstat (limited to 'drivers/net/wireless/rt2x00')
48 files changed, 29194 insertions, 7985 deletions
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index da05b1faf60..006b8bcb2e3 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -1,118 +1,257 @@ -config RT2X00 +menuconfig RT2X00 tristate "Ralink driver support" - depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + depends on MAC80211 && HAS_DMA ---help--- - This will enable the experimental support for the Ralink drivers, + This will enable the support for the Ralink drivers, developed in the rt2x00 project <http://rt2x00.serialmonkey.com>. - These drivers will make use of the Devicescape ieee80211 stack. + These drivers make use of the mac80211 stack. When building one of the individual drivers, the rt2x00 library will also be created. That library (when the driver is built as - a module) will be called "rt2x00lib.ko". + a module) will be called rt2x00lib. -config RT2X00_LIB - tristate - depends on RT2X00 - -config RT2X00_LIB_PCI - tristate - depends on RT2X00 - select RT2X00_LIB - -config RT2X00_LIB_USB - tristate - depends on RT2X00 - select RT2X00_LIB - -config RT2X00_LIB_FIRMWARE - boolean - depends on RT2X00_LIB - select CRC_ITU_T - select FW_LOADER + Additionally PCI and USB libraries will also be build depending + on the types of drivers being selected, these libraries will be + called rt2x00pci and rt2x00usb. -config RT2X00_LIB_RFKILL - boolean - depends on RT2X00_LIB - select RFKILL - select INPUT_POLLDEV +if RT2X00 config RT2400PCI - tristate "Ralink rt2400 pci/pcmcia support" - depends on RT2X00 && PCI + tristate "Ralink rt2400 (PCI/PCMCIA) support" + depends on PCI + select RT2X00_LIB_MMIO select RT2X00_LIB_PCI select EEPROM_93CX6 ---help--- - This is an experimental driver for the Ralink rt2400 wireless chip. - - When compiled as a module, this driver will be called "rt2400pci.ko". + This adds support for rt2400 wireless chipset family. + Supported chips: RT2460. -config RT2400PCI_RFKILL - bool "RT2400 rfkill support" - depends on RT2400PCI - select RT2X00_LIB_RFKILL - ---help--- - This adds support for integrated rt2400 devices that feature a - hardware button to control the radio state. - This feature depends on the RF switch subsystem rfkill. + When compiled as a module, this driver will be called rt2400pci. config RT2500PCI - tristate "Ralink rt2500 pci/pcmcia support" - depends on RT2X00 && PCI + tristate "Ralink rt2500 (PCI/PCMCIA) support" + depends on PCI + select RT2X00_LIB_MMIO select RT2X00_LIB_PCI select EEPROM_93CX6 ---help--- - This is an experimental driver for the Ralink rt2500 wireless chip. + This adds support for rt2500 wireless chipset family. + Supported chips: RT2560. - When compiled as a module, this driver will be called "rt2500pci.ko". + When compiled as a module, this driver will be called rt2500pci. -config RT2500PCI_RFKILL - bool "RT2500 rfkill support" - depends on RT2500PCI - select RT2X00_LIB_RFKILL +config RT61PCI + tristate "Ralink rt2501/rt61 (PCI/PCMCIA) support" + depends on PCI + select RT2X00_LIB_PCI + select RT2X00_LIB_MMIO + select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_ITU_T + select EEPROM_93CX6 ---help--- - This adds support for integrated rt2500 devices that feature a - hardware button to control the radio state. - This feature depends on the RF switch subsystem rfkill. + This adds support for rt2501 wireless chipset family. + Supported chips: RT2561, RT2561S & RT2661. -config RT61PCI - tristate "Ralink rt61 pci/pcmcia support" - depends on RT2X00 && PCI + When compiled as a module, this driver will be called rt61pci. + +config RT2800PCI + tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support" + depends on PCI + select RT2800_LIB + select RT2800_LIB_MMIO + select RT2X00_LIB_MMIO select RT2X00_LIB_PCI select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_CCITT select EEPROM_93CX6 ---help--- - This is an experimental driver for the Ralink rt61 wireless chip. + This adds support for rt27xx/rt28xx/rt30xx wireless chipset family. + Supported chips: RT2760, RT2790, RT2860, RT2880, RT2890, RT3052, + RT3090, RT3091 & RT3092 + + When compiled as a module, this driver will be called "rt2800pci.ko". - When compiled as a module, this driver will be called "rt61pci.ko". +if RT2800PCI -config RT61PCI_RFKILL - bool "RT61 rfkill support" - depends on RT61PCI - select RT2X00_LIB_RFKILL +config RT2800PCI_RT33XX + bool "rt2800pci - Include support for rt33xx devices" + default y ---help--- - This adds support for integrated rt61 devices that feature a - hardware button to control the radio state. - This feature depends on the RF switch subsystem rfkill. + This adds support for rt33xx wireless chipset family to the + rt2800pci driver. + Supported chips: RT3390 + +config RT2800PCI_RT35XX + bool "rt2800pci - Include support for rt35xx devices (EXPERIMENTAL)" + default y + ---help--- + This adds support for rt35xx wireless chipset family to the + rt2800pci driver. + Supported chips: RT3060, RT3062, RT3562, RT3592 + + +config RT2800PCI_RT53XX + bool "rt2800pci - Include support for rt53xx devices (EXPERIMENTAL)" + default y + ---help--- + This adds support for rt53xx wireless chipset family to the + rt2800pci driver. + Supported chips: RT5390 + +config RT2800PCI_RT3290 + bool "rt2800pci - Include support for rt3290 devices (EXPERIMENTAL)" + default y + ---help--- + This adds support for rt3290 wireless chipset family to the + rt2800pci driver. + Supported chips: RT3290 +endif config RT2500USB - tristate "Ralink rt2500 usb support" - depends on RT2X00 && USB + tristate "Ralink rt2500 (USB) support" + depends on USB select RT2X00_LIB_USB + select RT2X00_LIB_CRYPTO ---help--- - This is an experimental driver for the Ralink rt2500 wireless chip. + This adds support for rt2500 wireless chipset family. + Supported chips: RT2571 & RT2572. - When compiled as a module, this driver will be called "rt2500usb.ko". + When compiled as a module, this driver will be called rt2500usb. config RT73USB - tristate "Ralink rt73 usb support" - depends on RT2X00 && USB + tristate "Ralink rt2501/rt73 (USB) support" + depends on USB select RT2X00_LIB_USB select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_ITU_T + ---help--- + This adds support for rt2501 wireless chipset family. + Supported chips: RT2571W, RT2573 & RT2671. + + When compiled as a module, this driver will be called rt73usb. + +config RT2800USB + tristate "Ralink rt27xx/rt28xx/rt30xx (USB) support" + depends on USB + select RT2800_LIB + select RT2X00_LIB_USB + select RT2X00_LIB_FIRMWARE + select RT2X00_LIB_CRYPTO + select CRC_CCITT + ---help--- + This adds support for rt27xx/rt28xx/rt30xx wireless chipset family. + Supported chips: RT2770, RT2870 & RT3070, RT3071 & RT3072 + + When compiled as a module, this driver will be called "rt2800usb.ko". + +if RT2800USB + +config RT2800USB_RT33XX + bool "rt2800usb - Include support for rt33xx devices" + default y ---help--- - This is an experimental driver for the Ralink rt73 wireless chip. + This adds support for rt33xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT3370 - When compiled as a module, this driver will be called "rt73usb.ko". +config RT2800USB_RT35XX + bool "rt2800usb - Include support for rt35xx devices (EXPERIMENTAL)" + default y + ---help--- + This adds support for rt35xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT3572 + +config RT2800USB_RT3573 + bool "rt2800usb - Include support for rt3573 devices (EXPERIMENTAL)" + ---help--- + This enables support for RT3573 chipset based wireless USB devices + in the rt2800usb driver. + +config RT2800USB_RT53XX + bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)" + ---help--- + This adds support for rt53xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT5370 + +config RT2800USB_RT55XX + bool "rt2800usb - Include support for rt55xx devices (EXPERIMENTAL)" + ---help--- + This adds support for rt55xx wireless chipset family to the + rt2800usb driver. + Supported chips: RT5572 + +config RT2800USB_UNKNOWN + bool "rt2800usb - Include support for unknown (USB) devices" + default n + ---help--- + This adds support for rt2800usb devices that are known to + have a rt28xx family compatible chipset, but for which the exact + chipset is unknown. + + Support status for these devices is unknown, and enabling these + devices may or may not work. + +endif + +config RT2800SOC + tristate "Ralink WiSoC support" + depends on SOC_RT288X || SOC_RT305X + select RT2X00_LIB_SOC + select RT2X00_LIB_MMIO + select RT2X00_LIB_CRYPTO + select RT2X00_LIB_FIRMWARE + select RT2800_LIB + select RT2800_LIB_MMIO + ---help--- + This adds support for Ralink WiSoC devices. + Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352. + + When compiled as a module, this driver will be called rt2800soc. + + +config RT2800_LIB + tristate + +config RT2800_LIB_MMIO + tristate + select RT2X00_LIB_MMIO + select RT2800_LIB + +config RT2X00_LIB_MMIO + tristate + +config RT2X00_LIB_PCI + tristate + select RT2X00_LIB + +config RT2X00_LIB_SOC + tristate + select RT2X00_LIB + +config RT2X00_LIB_USB + tristate + select RT2X00_LIB + +config RT2X00_LIB + tristate + select AVERAGE + +config RT2X00_LIB_FIRMWARE + boolean + select FW_LOADER + +config RT2X00_LIB_CRYPTO + boolean + +config RT2X00_LIB_LEDS + boolean + default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n) config RT2X00_LIB_DEBUGFS bool "Ralink debugfs support" @@ -120,7 +259,7 @@ config RT2X00_LIB_DEBUGFS ---help--- Enable creation of debugfs files for the rt2x00 drivers. These debugfs files support both reading and writing of the - most important register types of the rt2x00 devices. + most important register types of the rt2x00 hardware. config RT2X00_DEBUG bool "Ralink debug output" @@ -128,3 +267,4 @@ config RT2X00_DEBUG ---help--- Enable debugging output for all rt2x00 modules +endif diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/rt2x00/Makefile index 30d654a42ee..24a66015a49 100644 --- a/drivers/net/wireless/rt2x00/Makefile +++ b/drivers/net/wireless/rt2x00/Makefile @@ -1,22 +1,25 @@ -rt2x00lib-objs := rt2x00dev.o rt2x00mac.o rt2x00config.o +rt2x00lib-y += rt2x00dev.o +rt2x00lib-y += rt2x00mac.o +rt2x00lib-y += rt2x00config.o +rt2x00lib-y += rt2x00queue.o +rt2x00lib-y += rt2x00link.o +rt2x00lib-$(CONFIG_RT2X00_LIB_DEBUGFS) += rt2x00debug.o +rt2x00lib-$(CONFIG_RT2X00_LIB_CRYPTO) += rt2x00crypto.o +rt2x00lib-$(CONFIG_RT2X00_LIB_FIRMWARE) += rt2x00firmware.o +rt2x00lib-$(CONFIG_RT2X00_LIB_LEDS) += rt2x00leds.o -ifeq ($(CONFIG_RT2X00_LIB_DEBUGFS),y) - rt2x00lib-objs += rt2x00debug.o -endif - -ifeq ($(CONFIG_RT2X00_LIB_RFKILL),y) - rt2x00lib-objs += rt2x00rfkill.o -endif - -ifeq ($(CONFIG_RT2X00_LIB_FIRMWARE),y) - rt2x00lib-objs += rt2x00firmware.o -endif - -obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o -obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o -obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o -obj-$(CONFIG_RT2400PCI) += rt2400pci.o -obj-$(CONFIG_RT2500PCI) += rt2500pci.o -obj-$(CONFIG_RT61PCI) += rt61pci.o -obj-$(CONFIG_RT2500USB) += rt2500usb.o -obj-$(CONFIG_RT73USB) += rt73usb.o +obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o +obj-$(CONFIG_RT2X00_LIB_MMIO) += rt2x00mmio.o +obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o +obj-$(CONFIG_RT2X00_LIB_SOC) += rt2x00soc.o +obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o +obj-$(CONFIG_RT2800_LIB) += rt2800lib.o +obj-$(CONFIG_RT2800_LIB_MMIO) += rt2800mmio.o +obj-$(CONFIG_RT2400PCI) += rt2400pci.o +obj-$(CONFIG_RT2500PCI) += rt2500pci.o +obj-$(CONFIG_RT61PCI) += rt61pci.o +obj-$(CONFIG_RT2800PCI) += rt2800pci.o +obj-$(CONFIG_RT2500USB) += rt2500usb.o +obj-$(CONFIG_RT73USB) += rt73usb.o +obj-$(CONFIG_RT2800USB) += rt2800usb.o +obj-$(CONFIG_RT2800SOC) += rt2800soc.o diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index c69f85ed766..4ccfef5094e 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -26,68 +24,57 @@ #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/eeprom_93cx6.h> +#include <linux/slab.h> #include "rt2x00.h" +#include "rt2x00mmio.h" #include "rt2x00pci.h" #include "rt2400pci.h" /* * Register access. * All access to the CSR registers will go through the methods - * rt2x00pci_register_read and rt2x00pci_register_write. + * rt2x00mmio_register_read and rt2x00mmio_register_write. * BBP and RF register require indirect register access, * and use the CSR registers BBPCSR and RFCSR to achieve this. * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attampt. When the busy bit is still set at that time, + * between each attempt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. */ -static u32 rt2400pci_bbp_check(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - unsigned int i; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, BBPCSR, ®); - if (!rt2x00_get_field32(reg, BBPCSR_BUSY)) - break; - udelay(REGISTER_BUSY_DELAY); - } - - return reg; -} +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg)) static void rt2400pci_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; - /* - * Wait until the BBP becomes ready. - */ - reg = rt2400pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, BBPCSR_BUSY)) { - ERROR(rt2x00dev, "BBPCSR register busy. Write failed.\n"); - return; - } + mutex_lock(&rt2x00dev->csr_mutex); /* - * Write the data into the BBP. + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. */ - reg = 0; - rt2x00_set_field32(®, BBPCSR_VALUE, value); - rt2x00_set_field32(®, BBPCSR_REGNUM, word); - rt2x00_set_field32(®, BBPCSR_BUSY, 1); - rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBPCSR_VALUE, value); + rt2x00_set_field32(®, BBPCSR_REGNUM, word); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); + + rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); + } - rt2x00pci_register_write(rt2x00dev, BBPCSR, reg); + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2400pci_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -95,66 +82,55 @@ static void rt2400pci_bbp_read(struct rt2x00_dev *rt2x00dev, { u32 reg; - /* - * Wait until the BBP becomes ready. - */ - reg = rt2400pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, BBPCSR_BUSY)) { - ERROR(rt2x00dev, "BBPCSR register busy. Read failed.\n"); - return; - } + mutex_lock(&rt2x00dev->csr_mutex); /* - * Write the request into the BBP. + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. */ - reg = 0; - rt2x00_set_field32(®, BBPCSR_REGNUM, word); - rt2x00_set_field32(®, BBPCSR_BUSY, 1); - rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBPCSR_REGNUM, word); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); - rt2x00pci_register_write(rt2x00dev, BBPCSR, reg); + rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); - /* - * Wait until the BBP becomes ready. - */ - reg = rt2400pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, BBPCSR_BUSY)) { - ERROR(rt2x00dev, "BBPCSR register busy. Read failed.\n"); - *value = 0xff; - return; + WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, BBPCSR_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2400pci_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; - unsigned int i; - if (!word) - return; + mutex_lock(&rt2x00dev->csr_mutex); - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, RFCSR, ®); - if (!rt2x00_get_field32(reg, RFCSR_BUSY)) - goto rf_write; - udelay(REGISTER_BUSY_DELAY); + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RFCSR_VALUE, value); + rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); + rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); + rt2x00_set_field32(®, RFCSR_BUSY, 1); + + rt2x00mmio_register_write(rt2x00dev, RFCSR, reg); + rt2x00_rf_write(rt2x00dev, word, value); } - ERROR(rt2x00dev, "RFCSR register busy. Write failed.\n"); - return; - -rf_write: - reg = 0; - rt2x00_set_field32(®, RFCSR_VALUE, value); - rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); - rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); - rt2x00_set_field32(®, RFCSR_BUSY, 1); - - rt2x00pci_register_write(rt2x00dev, RFCSR, reg); - rt2x00_rf_write(rt2x00dev, word, value); + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2400pci_eepromregister_read(struct eeprom_93cx6 *eeprom) @@ -162,7 +138,7 @@ static void rt2400pci_eepromregister_read(struct eeprom_93cx6 *eeprom) struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg; - rt2x00pci_register_read(rt2x00dev, CSR21, ®); + rt2x00mmio_register_read(rt2x00dev, CSR21, ®); eeprom->reg_data_in = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_IN); eeprom->reg_data_out = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_OUT); @@ -184,112 +160,166 @@ static void rt2400pci_eepromregister_write(struct eeprom_93cx6 *eeprom) rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, !!eeprom->reg_chip_select); - rt2x00pci_register_write(rt2x00dev, CSR21, reg); + rt2x00mmio_register_write(rt2x00dev, CSR21, reg); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS -#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) ) - -static void rt2400pci_read_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 *data) -{ - rt2x00pci_register_read(rt2x00dev, CSR_OFFSET(word), data); -} - -static void rt2400pci_write_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 data) -{ - rt2x00pci_register_write(rt2x00dev, CSR_OFFSET(word), data); -} - static const struct rt2x00debug rt2400pci_rt2x00debug = { .owner = THIS_MODULE, .csr = { - .read = rt2400pci_read_csr, - .write = rt2400pci_write_csr, + .read = rt2x00mmio_register_read, + .write = rt2x00mmio_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt2400pci_bbp_read, .write = rt2400pci_bbp_write, + .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt2400pci_rf_write, + .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT2400PCI_RFKILL static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt2x00pci_register_read(rt2x00dev, GPIOCSR, ®); - return rt2x00_get_field32(reg, GPIOCSR_BIT0); + rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); + return rt2x00_get_field32(reg, GPIOCSR_VAL0); } -#else -#define rt2400pci_rfkill_poll NULL -#endif /* CONFIG_RT2400PCI_RFKILL */ -/* - * Configuration handlers. - */ -static void rt2400pci_config_mac_addr(struct rt2x00_dev *rt2x00dev, - __le32 *mac) +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2400pci_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { - rt2x00pci_register_multiwrite(rt2x00dev, CSR3, mac, - (2 * sizeof(__le32))); + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); + + if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) + rt2x00_set_field32(®, LEDCSR_LINK, enabled); + else if (led->type == LED_TYPE_ACTIVITY) + rt2x00_set_field32(®, LEDCSR_ACTIVITY, enabled); + + rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); } -static void rt2400pci_config_bssid(struct rt2x00_dev *rt2x00dev, - __le32 *bssid) +static int rt2400pci_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) { - rt2x00pci_register_multiwrite(rt2x00dev, CSR5, bssid, - (2 * sizeof(__le32))); + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); + rt2x00_set_field32(®, LEDCSR_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, *delay_off); + rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); + + return 0; } -static void rt2400pci_config_type(struct rt2x00_dev *rt2x00dev, const int type, - const int tsf_sync) +static void rt2400pci_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) { - u32 reg; + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2400pci_brightness_set; + led->led_dev.blink_set = rt2400pci_blink_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ - rt2x00pci_register_write(rt2x00dev, CSR14, 0); +/* + * Configuration handlers. + */ +static void rt2400pci_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; /* - * Enable beacon config + * Start configuration steps. + * Note that the version error will always be dropped + * since there is no filter for it at this time. */ - rt2x00pci_register_read(rt2x00dev, BCNCSR1, ®); - rt2x00_set_field32(®, BCNCSR1_PRELOAD, - PREAMBLE + get_duration(IEEE80211_HEADER, 20)); - rt2x00pci_register_write(rt2x00dev, BCNCSR1, reg); + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, + !(filter_flags & FIF_PROMISC_IN_BSS)); + rt2x00_set_field32(®, RXCSR0_DROP_TODS, + !(filter_flags & FIF_PROMISC_IN_BSS) && + !rt2x00dev->intf_ap_count); + rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); +} - /* - * Enable synchronisation. - */ - rt2x00pci_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); - rt2x00_set_field32(®, CSR14_TBCN, (tsf_sync == TSF_SYNC_BEACON)); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); - rt2x00_set_field32(®, CSR14_TSF_SYNC, tsf_sync); - rt2x00pci_register_write(rt2x00dev, CSR14, reg); +static void rt2400pci_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) +{ + unsigned int bcn_preload; + u32 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable beacon config + */ + bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); + rt2x00mmio_register_read(rt2x00dev, BCNCSR1, ®); + rt2x00_set_field32(®, BCNCSR1_PRELOAD, bcn_preload); + rt2x00mmio_register_write(rt2x00dev, BCNCSR1, reg); + + /* + * Enable synchronisation. + */ + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_SYNC, conf->sync); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + } + + if (flags & CONFIG_UPDATE_MAC) + rt2x00mmio_register_multiwrite(rt2x00dev, CSR3, + conf->mac, sizeof(conf->mac)); + + if (flags & CONFIG_UPDATE_BSSID) + rt2x00mmio_register_multiwrite(rt2x00dev, CSR5, + conf->bssid, + sizeof(conf->bssid)); } -static void rt2400pci_config_preamble(struct rt2x00_dev *rt2x00dev, - const int short_preamble, - const int ack_timeout, - const int ack_consume_time) +static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) { int preamble_mask; u32 reg; @@ -297,42 +327,124 @@ static void rt2400pci_config_preamble(struct rt2x00_dev *rt2x00dev, /* * When short preamble is enabled, we should set bit 0x08 */ - preamble_mask = short_preamble << 3; - - rt2x00pci_register_read(rt2x00dev, TXCSR1, ®); - rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, ack_timeout); - rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, ack_consume_time); - rt2x00pci_register_write(rt2x00dev, TXCSR1, reg); - - rt2x00pci_register_read(rt2x00dev, ARCSR2, ®); - rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00 | preamble_mask); - rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, get_duration(ACK_SIZE, 10)); - rt2x00pci_register_write(rt2x00dev, ARCSR2, reg); - - rt2x00pci_register_read(rt2x00dev, ARCSR3, ®); - rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); - rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, get_duration(ACK_SIZE, 20)); - rt2x00pci_register_write(rt2x00dev, ARCSR3, reg); - - rt2x00pci_register_read(rt2x00dev, ARCSR4, ®); - rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); - rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, get_duration(ACK_SIZE, 55)); - rt2x00pci_register_write(rt2x00dev, ARCSR4, reg); - - rt2x00pci_register_read(rt2x00dev, ARCSR5, ®); - rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); - rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); - rt2x00_set_field32(®, ARCSR2_LENGTH, get_duration(ACK_SIZE, 110)); - rt2x00pci_register_write(rt2x00dev, ARCSR5, reg); + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + preamble_mask = erp->short_preamble << 3; + + rt2x00mmio_register_read(rt2x00dev, TXCSR1, ®); + rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, 0x1ff); + rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, 0x13a); + rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR2, ®); + rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00); + rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 10)); + rt2x00mmio_register_write(rt2x00dev, ARCSR2, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR3, ®); + rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); + rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 20)); + rt2x00mmio_register_write(rt2x00dev, ARCSR3, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR4, ®); + rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); + rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 55)); + rt2x00mmio_register_write(rt2x00dev, ARCSR4, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR5, ®); + rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); + rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 110)); + rt2x00mmio_register_write(rt2x00dev, ARCSR5, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) + rt2x00mmio_register_write(rt2x00dev, ARCSR1, erp->basic_rates); + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR18, ®); + rt2x00_set_field32(®, CSR18_SIFS, erp->sifs); + rt2x00_set_field32(®, CSR18_PIFS, erp->pifs); + rt2x00mmio_register_write(rt2x00dev, CSR18, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR19, ®); + rt2x00_set_field32(®, CSR19_DIFS, erp->difs); + rt2x00_set_field32(®, CSR19_EIFS, erp->eifs); + rt2x00mmio_register_write(rt2x00dev, CSR19, reg); + } + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2x00mmio_register_read(rt2x00dev, CSR12, ®); + rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, + erp->beacon_int * 16); + rt2x00mmio_register_write(rt2x00dev, CSR12, reg); + } } -static void rt2400pci_config_phymode(struct rt2x00_dev *rt2x00dev, - const int basic_rate_mask) +static void rt2400pci_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) { - rt2x00pci_register_write(rt2x00dev, ARCSR1, basic_rate_mask); + u8 r1; + u8 r4; + + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + rt2400pci_bbp_read(rt2x00dev, 4, &r4); + rt2400pci_bbp_read(rt2x00dev, 1, &r1); + + /* + * Configure the TX antenna. + */ + switch (ant->tx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 1); + break; + case ANTENNA_A: + rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 2); + break; + } + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_HW_DIVERSITY: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 1); + break; + case ANTENNA_A: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 2); + break; + } + + rt2400pci_bbp_write(rt2x00dev, 4, r4); + rt2400pci_bbp_write(rt2x00dev, 1, r1); } static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev, @@ -351,7 +463,7 @@ static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev, /* * RF2420 chipset don't need any additional actions. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2420)) + if (rt2x00_rf(rt2x00dev, RF2420)) return; /* @@ -383,7 +495,7 @@ static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev, /* * Clear false CRC during channel switch. */ - rt2x00pci_register_read(rt2x00dev, CNT0, &rf->rf1); + rt2x00mmio_register_read(rt2x00dev, CNT0, &rf->rf1); } static void rt2400pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) @@ -391,149 +503,73 @@ static void rt2400pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) rt2400pci_bbp_write(rt2x00dev, 3, TXPOWER_TO_DEV(txpower)); } -static void rt2400pci_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) +static void rt2400pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) { - u8 r1; - u8 r4; - - rt2400pci_bbp_read(rt2x00dev, 4, &r4); - rt2400pci_bbp_read(rt2x00dev, 1, &r1); - - /* - * Configure the TX antenna. - */ - switch (ant->tx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 1); - break; - case ANTENNA_A: - rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 0); - break; - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ - case ANTENNA_B: - rt2x00_set_field8(&r1, BBP_R1_TX_ANTENNA, 2); - break; - } - - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_HW_DIVERSITY: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 1); - break; - case ANTENNA_A: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 0); - break; - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ - case ANTENNA_B: - rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA, 2); - break; - } + u32 reg; - rt2400pci_bbp_write(rt2x00dev, 4, r4); - rt2400pci_bbp_write(rt2x00dev, 1, r1); + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_LONG_RETRY, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, CSR11_SHORT_RETRY, + libconf->conf->short_frame_max_tx_count); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); } -static void rt2400pci_config_duration(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) +static void rt2400pci_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) { + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; u32 reg; - rt2x00pci_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_SLOT_TIME, libconf->slot_time); - rt2x00pci_register_write(rt2x00dev, CSR11, reg); - - rt2x00pci_register_read(rt2x00dev, CSR18, ®); - rt2x00_set_field32(®, CSR18_SIFS, libconf->sifs); - rt2x00_set_field32(®, CSR18_PIFS, libconf->pifs); - rt2x00pci_register_write(rt2x00dev, CSR18, reg); - - rt2x00pci_register_read(rt2x00dev, CSR19, ®); - rt2x00_set_field32(®, CSR19_DIFS, libconf->difs); - rt2x00_set_field32(®, CSR19_EIFS, libconf->eifs); - rt2x00pci_register_write(rt2x00dev, CSR19, reg); - - rt2x00pci_register_read(rt2x00dev, TXCSR1, ®); - rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); - rt2x00pci_register_write(rt2x00dev, TXCSR1, reg); - - rt2x00pci_register_read(rt2x00dev, CSR12, ®); - rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, - libconf->conf->beacon_int * 16); - rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, - libconf->conf->beacon_int * 16); - rt2x00pci_register_write(rt2x00dev, CSR12, reg); + if (state == STATE_SLEEP) { + rt2x00mmio_register_read(rt2x00dev, CSR20, ®); + rt2x00_set_field32(®, CSR20_DELAY_AFTER_TBCN, + (rt2x00dev->beacon_int - 20) * 16); + rt2x00_set_field32(®, CSR20_TBCN_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + + rt2x00_set_field32(®, CSR20_AUTOWAKE, 1); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + } else { + rt2x00mmio_register_read(rt2x00dev, CSR20, ®); + rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); + } + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); } static void rt2400pci_config(struct rt2x00_dev *rt2x00dev, - const unsigned int flags, - struct rt2x00lib_conf *libconf) + struct rt2x00lib_conf *libconf, + const unsigned int flags) { - if (flags & CONFIG_UPDATE_PHYMODE) - rt2400pci_config_phymode(rt2x00dev, libconf->basic_rates); - if (flags & CONFIG_UPDATE_CHANNEL) + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) rt2400pci_config_channel(rt2x00dev, &libconf->rf); - if (flags & CONFIG_UPDATE_TXPOWER) + if (flags & IEEE80211_CONF_CHANGE_POWER) rt2400pci_config_txpower(rt2x00dev, libconf->conf->power_level); - if (flags & CONFIG_UPDATE_ANTENNA) - rt2400pci_config_antenna(rt2x00dev, &libconf->ant); - if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT)) - rt2400pci_config_duration(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt2400pci_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2400pci_config_ps(rt2x00dev, libconf); } static void rt2400pci_config_cw(struct rt2x00_dev *rt2x00dev, - struct ieee80211_tx_queue_params *params) + const int cw_min, const int cw_max) { u32 reg; - rt2x00pci_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_CWMIN, params->cw_min); - rt2x00_set_field32(®, CSR11_CWMAX, params->cw_max); - rt2x00pci_register_write(rt2x00dev, CSR11, reg); -} - -/* - * LED functions. - */ -static void rt2400pci_enable_led(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00pci_register_read(rt2x00dev, LEDCSR, ®); - - rt2x00_set_field32(®, LEDCSR_ON_PERIOD, 70); - rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, 30); - rt2x00_set_field32(®, LEDCSR_LINK, - (rt2x00dev->led_mode != LED_MODE_ASUS)); - rt2x00_set_field32(®, LEDCSR_ACTIVITY, - (rt2x00dev->led_mode != LED_MODE_TXRX_ACTIVITY)); - rt2x00pci_register_write(rt2x00dev, LEDCSR, reg); -} - -static void rt2400pci_disable_led(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00pci_register_read(rt2x00dev, LEDCSR, ®); - rt2x00_set_field32(®, LEDCSR_LINK, 0); - rt2x00_set_field32(®, LEDCSR_ACTIVITY, 0); - rt2x00pci_register_write(rt2x00dev, LEDCSR, reg); + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_CWMIN, cw_min); + rt2x00_set_field32(®, CSR11_CWMAX, cw_max); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); } /* @@ -548,7 +584,7 @@ static void rt2400pci_link_stats(struct rt2x00_dev *rt2x00dev, /* * Update FCS error count from register. */ - rt2x00pci_register_read(rt2x00dev, CNT0, ®); + rt2x00mmio_register_read(rt2x00dev, CNT0, ®); qual->rx_failed = rt2x00_get_field32(reg, CNT0_FCS_ERROR); /* @@ -558,126 +594,223 @@ static void rt2400pci_link_stats(struct rt2x00_dev *rt2x00dev, qual->false_cca = bbp; } -static void rt2400pci_reset_tuner(struct rt2x00_dev *rt2x00dev) +static inline void rt2400pci_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) { - rt2400pci_bbp_write(rt2x00dev, 13, 0x08); - rt2x00dev->link.vgc_level = 0x08; + if (qual->vgc_level_reg != vgc_level) { + rt2400pci_bbp_write(rt2x00dev, 13, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } } -static void rt2400pci_link_tuner(struct rt2x00_dev *rt2x00dev) +static void rt2400pci_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) { - u8 reg; + rt2400pci_set_vgc(rt2x00dev, qual, 0x08); +} +static void rt2400pci_link_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count) +{ /* * The link tuner should not run longer then 60 seconds, * and should run once every 2 seconds. */ - if (rt2x00dev->link.count > 60 || !(rt2x00dev->link.count & 1)) + if (count > 60 || !(count & 1)) return; /* * Base r13 link tuning on the false cca count. */ - rt2400pci_bbp_read(rt2x00dev, 13, ®); - - if (rt2x00dev->link.qual.false_cca > 512 && reg < 0x20) { - rt2400pci_bbp_write(rt2x00dev, 13, ++reg); - rt2x00dev->link.vgc_level = reg; - } else if (rt2x00dev->link.qual.false_cca < 100 && reg > 0x08) { - rt2400pci_bbp_write(rt2x00dev, 13, --reg); - rt2x00dev->link.vgc_level = reg; + if ((qual->false_cca > 512) && (qual->vgc_level < 0x20)) + rt2400pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level); + else if ((qual->false_cca < 100) && (qual->vgc_level > 0x08)) + rt2400pci_set_vgc(rt2x00dev, qual, --qual->vgc_level); +} + +/* + * Queue handlers. + */ +static void rt2400pci_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); + rt2x00_set_field32(®, CSR14_TBCN, 1); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + break; + default: + break; + } +} + +static void rt2400pci_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_AC_VI: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_ATIM: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + default: + break; + } +} + +static void rt2400pci_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_ATIM: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_ABORT, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); + rt2x00_set_field32(®, CSR14_TBCN, 0); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + /* + * Wait for possibly running tbtt tasklets. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + break; + default: + break; } } /* * Initialization functions. */ -static void rt2400pci_init_rxentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry) +static bool rt2400pci_get_entry_state(struct queue_entry *entry) { - __le32 *rxd = entry->priv; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; u32 word; - rt2x00_desc_read(rxd, 2, &word); - rt2x00_set_field32(&word, RXD_W2_BUFFER_LENGTH, entry->ring->data_size); - rt2x00_desc_write(rxd, 2, word); + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_desc_read(rxd, 1, &word); - rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, entry->data_dma); - rt2x00_desc_write(rxd, 1, word); + return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_desc_read(rxd, 0, &word); - rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); - rt2x00_desc_write(rxd, 0, word); + return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)); + } } -static void rt2400pci_init_txentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry) +static void rt2400pci_clear_entry(struct queue_entry *entry) { - __le32 *txd = entry->priv; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); u32 word; - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, entry->data_dma); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_BUFFER_LENGTH, entry->ring->data_size); - rt2x00_desc_write(txd, 2, word); - - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_VALID, 0); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); - rt2x00_desc_write(txd, 0, word); + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 2, &word); + rt2x00_set_field32(&word, RXD_W2_BUFFER_LENGTH, entry->skb->len); + rt2x00_desc_write(entry_priv->desc, 2, word); + + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 1, word); + + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(entry_priv->desc, 0, word); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(entry_priv->desc, 0, word); + } } -static int rt2400pci_init_rings(struct rt2x00_dev *rt2x00dev) +static int rt2400pci_init_queues(struct rt2x00_dev *rt2x00dev) { + struct queue_entry_priv_mmio *entry_priv; u32 reg; /* * Initialize registers. */ - rt2x00pci_register_read(rt2x00dev, TXCSR2, ®); - rt2x00_set_field32(®, TXCSR2_TXD_SIZE, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].desc_size); - rt2x00_set_field32(®, TXCSR2_NUM_TXD, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA1].stats.limit); - rt2x00_set_field32(®, TXCSR2_NUM_ATIM, - rt2x00dev->bcn[1].stats.limit); - rt2x00_set_field32(®, TXCSR2_NUM_PRIO, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].stats.limit); - rt2x00pci_register_write(rt2x00dev, TXCSR2, reg); - - rt2x00pci_register_read(rt2x00dev, TXCSR3, ®); + rt2x00mmio_register_read(rt2x00dev, TXCSR2, ®); + rt2x00_set_field32(®, TXCSR2_TXD_SIZE, rt2x00dev->tx[0].desc_size); + rt2x00_set_field32(®, TXCSR2_NUM_TXD, rt2x00dev->tx[1].limit); + rt2x00_set_field32(®, TXCSR2_NUM_ATIM, rt2x00dev->atim->limit); + rt2x00_set_field32(®, TXCSR2_NUM_PRIO, rt2x00dev->tx[0].limit); + rt2x00mmio_register_write(rt2x00dev, TXCSR2, reg); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR3, ®); rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA1].data_dma); - rt2x00pci_register_write(rt2x00dev, TXCSR3, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR3, reg); - rt2x00pci_register_read(rt2x00dev, TXCSR5, ®); + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR5, ®); rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].data_dma); - rt2x00pci_register_write(rt2x00dev, TXCSR5, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR5, reg); - rt2x00pci_register_read(rt2x00dev, TXCSR4, ®); + entry_priv = rt2x00dev->atim->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR4, ®); rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, - rt2x00dev->bcn[1].data_dma); - rt2x00pci_register_write(rt2x00dev, TXCSR4, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR4, reg); - rt2x00pci_register_read(rt2x00dev, TXCSR6, ®); + entry_priv = rt2x00dev->bcn->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR6, ®); rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, - rt2x00dev->bcn[0].data_dma); - rt2x00pci_register_write(rt2x00dev, TXCSR6, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR6, reg); - rt2x00pci_register_read(rt2x00dev, RXCSR1, ®); + rt2x00mmio_register_read(rt2x00dev, RXCSR1, ®); rt2x00_set_field32(®, RXCSR1_RXD_SIZE, rt2x00dev->rx->desc_size); - rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->stats.limit); - rt2x00pci_register_write(rt2x00dev, RXCSR1, reg); + rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->limit); + rt2x00mmio_register_write(rt2x00dev, RXCSR1, reg); - rt2x00pci_register_read(rt2x00dev, RXCSR2, ®); + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, RXCSR2, ®); rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, - rt2x00dev->rx->data_dma); - rt2x00pci_register_write(rt2x00dev, RXCSR2, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RXCSR2, reg); return 0; } @@ -686,100 +819,118 @@ static int rt2400pci_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt2x00pci_register_write(rt2x00dev, PSCSR0, 0x00020002); - rt2x00pci_register_write(rt2x00dev, PSCSR1, 0x00000002); - rt2x00pci_register_write(rt2x00dev, PSCSR2, 0x00023f20); - rt2x00pci_register_write(rt2x00dev, PSCSR3, 0x00000002); + rt2x00mmio_register_write(rt2x00dev, PSCSR0, 0x00020002); + rt2x00mmio_register_write(rt2x00dev, PSCSR1, 0x00000002); + rt2x00mmio_register_write(rt2x00dev, PSCSR2, 0x00023f20); + rt2x00mmio_register_write(rt2x00dev, PSCSR3, 0x00000002); - rt2x00pci_register_read(rt2x00dev, TIMECSR, ®); + rt2x00mmio_register_read(rt2x00dev, TIMECSR, ®); rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); - rt2x00pci_register_write(rt2x00dev, TIMECSR, reg); + rt2x00mmio_register_write(rt2x00dev, TIMECSR, reg); - rt2x00pci_register_read(rt2x00dev, CSR9, ®); + rt2x00mmio_register_read(rt2x00dev, CSR9, ®); rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, (rt2x00dev->rx->data_size / 128)); - rt2x00pci_register_write(rt2x00dev, CSR9, reg); + rt2x00mmio_register_write(rt2x00dev, CSR9, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); + rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); + rt2x00_set_field32(®, CSR14_TBCN, 0); + rt2x00_set_field32(®, CSR14_TCFP, 0); + rt2x00_set_field32(®, CSR14_TATIMW, 0); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00_set_field32(®, CSR14_CFP_COUNT_PRELOAD, 0); + rt2x00_set_field32(®, CSR14_TBCM_PRELOAD, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - rt2x00pci_register_write(rt2x00dev, CNT3, 0x3f080000); + rt2x00mmio_register_write(rt2x00dev, CNT3, 0x3f080000); - rt2x00pci_register_read(rt2x00dev, ARCSR0, ®); + rt2x00mmio_register_read(rt2x00dev, ARCSR0, ®); rt2x00_set_field32(®, ARCSR0_AR_BBP_DATA0, 133); rt2x00_set_field32(®, ARCSR0_AR_BBP_ID0, 134); rt2x00_set_field32(®, ARCSR0_AR_BBP_DATA1, 136); rt2x00_set_field32(®, ARCSR0_AR_BBP_ID1, 135); - rt2x00pci_register_write(rt2x00dev, ARCSR0, reg); + rt2x00mmio_register_write(rt2x00dev, ARCSR0, reg); - rt2x00pci_register_read(rt2x00dev, RXCSR3, ®); + rt2x00mmio_register_read(rt2x00dev, RXCSR3, ®); rt2x00_set_field32(®, RXCSR3_BBP_ID0, 3); /* Tx power.*/ rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID1, 32); /* Signal */ rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID2, 36); /* Rssi */ rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); - rt2x00pci_register_write(rt2x00dev, RXCSR3, reg); + rt2x00mmio_register_write(rt2x00dev, RXCSR3, reg); - rt2x00pci_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); + rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; - rt2x00pci_register_write(rt2x00dev, MACCSR0, 0x00217223); - rt2x00pci_register_write(rt2x00dev, MACCSR1, 0x00235518); + rt2x00mmio_register_write(rt2x00dev, MACCSR0, 0x00217223); + rt2x00mmio_register_write(rt2x00dev, MACCSR1, 0x00235518); - rt2x00pci_register_read(rt2x00dev, MACCSR2, ®); + rt2x00mmio_register_read(rt2x00dev, MACCSR2, ®); rt2x00_set_field32(®, MACCSR2_DELAY, 64); - rt2x00pci_register_write(rt2x00dev, MACCSR2, reg); + rt2x00mmio_register_write(rt2x00dev, MACCSR2, reg); - rt2x00pci_register_read(rt2x00dev, RALINKCSR, ®); + rt2x00mmio_register_read(rt2x00dev, RALINKCSR, ®); rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 154); rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 154); - rt2x00pci_register_write(rt2x00dev, RALINKCSR, reg); + rt2x00mmio_register_write(rt2x00dev, RALINKCSR, reg); - rt2x00pci_register_read(rt2x00dev, CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, CSR1, ®); rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, CSR1_BBP_RESET, 0); rt2x00_set_field32(®, CSR1_HOST_READY, 0); - rt2x00pci_register_write(rt2x00dev, CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, CSR1, reg); - rt2x00pci_register_read(rt2x00dev, CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, CSR1, ®); rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, CSR1_HOST_READY, 1); - rt2x00pci_register_write(rt2x00dev, CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, CSR1, reg); /* * We must clear the FCS and FIFO error count. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ - rt2x00pci_register_read(rt2x00dev, CNT0, ®); - rt2x00pci_register_read(rt2x00dev, CNT4, ®); + rt2x00mmio_register_read(rt2x00dev, CNT0, ®); + rt2x00mmio_register_read(rt2x00dev, CNT4, ®); return 0; } -static int rt2400pci_init_bbp(struct rt2x00_dev *rt2x00dev) +static int rt2400pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; - u16 eeprom; - u8 reg_id; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2400pci_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) - goto continue_csr_init; - NOTICE(rt2x00dev, "Waiting for BBP register.\n"); + return 0; udelay(REGISTER_BUSY_DELAY); } - ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); return -EACCES; +} + +static int rt2400pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt2400pci_wait_bbp_ready(rt2x00dev))) + return -EACCES; -continue_csr_init: rt2400pci_bbp_write(rt2x00dev, 1, 0x00); rt2400pci_bbp_write(rt2x00dev, 3, 0x27); rt2400pci_bbp_write(rt2x00dev, 4, 0x08); @@ -795,19 +946,15 @@ continue_csr_init: rt2400pci_bbp_write(rt2x00dev, 30, 0x21); rt2400pci_bbp_write(rt2x00dev, 31, 0x00); - DEBUG(rt2x00dev, "Start initialization from EEPROM...\n"); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - DEBUG(rt2x00dev, "BBP: 0x%02x, value: 0x%02x.\n", - reg_id, value); rt2400pci_bbp_write(rt2x00dev, reg_id, value); } } - DEBUG(rt2x00dev, "...End initialization from EEPROM.\n"); return 0; } @@ -815,43 +962,47 @@ continue_csr_init: /* * Device state switch handlers. */ -static void rt2400pci_toggle_rx(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg; - - rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DISABLE_RX, - state == STATE_RADIO_RX_OFF); - rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); -} - static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int mask = (state == STATE_RADIO_IRQ_OFF); u32 reg; + unsigned long flags; /* * When interrupts are being enabled, the interrupt registers * should clear the register to assure a clean state. */ if (state == STATE_RADIO_IRQ_ON) { - rt2x00pci_register_read(rt2x00dev, CSR7, ®); - rt2x00pci_register_write(rt2x00dev, CSR7, reg); + rt2x00mmio_register_read(rt2x00dev, CSR7, ®); + rt2x00mmio_register_write(rt2x00dev, CSR7, reg); } /* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ - rt2x00pci_register_read(rt2x00dev, CSR8, ®); + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, mask); rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); rt2x00_set_field32(®, CSR8_RXDONE, mask); - rt2x00pci_register_write(rt2x00dev, CSR8, reg); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Ensure that all tasklets are finished before + * disabling the interrupts. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + } } static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev) @@ -859,59 +1010,26 @@ static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev) /* * Initialize all registers. */ - if (rt2400pci_init_rings(rt2x00dev) || - rt2400pci_init_registers(rt2x00dev) || - rt2400pci_init_bbp(rt2x00dev)) { - ERROR(rt2x00dev, "Register initialization failed.\n"); + if (unlikely(rt2400pci_init_queues(rt2x00dev) || + rt2400pci_init_registers(rt2x00dev) || + rt2400pci_init_bbp(rt2x00dev))) return -EIO; - } - - /* - * Enable interrupts. - */ - rt2400pci_toggle_irq(rt2x00dev, STATE_RADIO_IRQ_ON); - - /* - * Enable LED - */ - rt2400pci_enable_led(rt2x00dev); return 0; } static void rt2400pci_disable_radio(struct rt2x00_dev *rt2x00dev) { - u32 reg; - - /* - * Disable LED - */ - rt2400pci_disable_led(rt2x00dev); - - rt2x00pci_register_write(rt2x00dev, PWRCSR0, 0); - - /* - * Disable synchronisation. - */ - rt2x00pci_register_write(rt2x00dev, CSR14, 0); - - /* - * Cancel RX and TX. - */ - rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_ABORT, 1); - rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); - /* - * Disable interrupts. + * Disable power */ - rt2400pci_toggle_irq(rt2x00dev, STATE_RADIO_IRQ_OFF); + rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0); } static int rt2400pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - u32 reg; + u32 reg, reg2; unsigned int i; char put_to_sleep; char bbp_state; @@ -919,12 +1037,12 @@ static int rt2400pci_set_state(struct rt2x00_dev *rt2x00dev, put_to_sleep = (state != STATE_AWAKE); - rt2x00pci_register_read(rt2x00dev, PWRCSR1, ®); + rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®); rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); - rt2x00pci_register_write(rt2x00dev, PWRCSR1, reg); + rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); /* * Device is not guaranteed to be in the requested state yet. @@ -932,18 +1050,15 @@ static int rt2400pci_set_state(struct rt2x00_dev *rt2x00dev, * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, PWRCSR1, ®); - bbp_state = rt2x00_get_field32(reg, PWRCSR1_BBP_CURR_STATE); - rf_state = rt2x00_get_field32(reg, PWRCSR1_RF_CURR_STATE); + rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®2); + bbp_state = rt2x00_get_field32(reg2, PWRCSR1_BBP_CURR_STATE); + rf_state = rt2x00_get_field32(reg2, PWRCSR1_RF_CURR_STATE); if (bbp_state == state && rf_state == state) return 0; + rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); msleep(10); } - NOTICE(rt2x00dev, "Device failed to enter state %d, " - "current device state: bbp %d and rf %d.\n", - state, bbp_state, rf_state); - return -EBUSY; } @@ -959,13 +1074,9 @@ static int rt2400pci_set_device_state(struct rt2x00_dev *rt2x00dev, case STATE_RADIO_OFF: rt2400pci_disable_radio(rt2x00dev); break; - case STATE_RADIO_RX_ON: - case STATE_RADIO_RX_ON_LINK: - rt2400pci_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); - break; - case STATE_RADIO_RX_OFF: - case STATE_RADIO_RX_OFF_LINK: - rt2400pci_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2400pci_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: @@ -978,137 +1089,200 @@ static int rt2400pci_set_device_state(struct rt2x00_dev *rt2x00dev, break; } + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + return retval; } /* * TX descriptor initialization */ -static void rt2400pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txdata_entry_desc *desc, - struct ieee80211_tx_control *control) +static void rt2400pci_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) { - struct skb_desc *skbdesc = get_skb_desc(skb); - __le32 *txd = skbdesc->desc; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; u32 word; /* * Start writing the descriptor words. */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); + rt2x00_desc_write(txd, 1, word); + rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_DATABYTE_COUNT, skbdesc->data_len); + rt2x00_set_field32(&word, TXD_W2_BUFFER_LENGTH, txdesc->length); + rt2x00_set_field32(&word, TXD_W2_DATABYTE_COUNT, txdesc->length); rt2x00_desc_write(txd, 2, word); rt2x00_desc_read(txd, 3, &word); - rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, desc->signal); + rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, txdesc->u.plcp.signal); rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL_REGNUM, 5); rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL_BUSY, 1); - rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, desc->service); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, txdesc->u.plcp.service); rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE_REGNUM, 6); rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE_BUSY, 1); rt2x00_desc_write(txd, 3, word); rt2x00_desc_read(txd, 4, &word); - rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_LOW, desc->length_low); + rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW_REGNUM, 8); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW_BUSY, 1); - rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_HIGH, desc->length_high); + rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH_REGNUM, 7); rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH_BUSY, 1); rt2x00_desc_write(txd, 4, word); + /* + * Writing TXD word 0 must the last to prevent a race condition with + * the device, whereby the device may take hold of the TXD before we + * finished updating it. + */ rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); rt2x00_set_field32(&word, TXD_W0_VALID, 1); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &desc->flags)); + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &desc->flags)); + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &desc->flags)); + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_RTS, - test_bit(ENTRY_TXD_RTS_FRAME, &desc->flags)); - rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, - !!(control->flags & - IEEE80211_TXCTL_LONG_RETRY_LIMIT)); + test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); rt2x00_desc_write(txd, 0, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; } /* * TX data initialization */ -static void rt2400pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, - unsigned int queue) +static void rt2400pci_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) { + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; u32 reg; - if (queue == IEEE80211_TX_QUEUE_BEACON) { - rt2x00pci_register_read(rt2x00dev, CSR14, ®); - if (!rt2x00_get_field32(reg, CSR14_BEACON_GEN)) { - rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); - rt2x00pci_register_write(rt2x00dev, CSR14, reg); - } - return; + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + if (rt2x00queue_map_txskb(entry)) { + rt2x00_err(rt2x00dev, "Fail to map beacon, aborting\n"); + goto out; } + /* + * Enable beaconing again. + */ + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + /* + * Write the TX descriptor for the beacon. + */ + rt2400pci_write_tx_desc(entry, txdesc); - rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_KICK_PRIO, - (queue == IEEE80211_TX_QUEUE_DATA0)); - rt2x00_set_field32(®, TXCSR0_KICK_TX, - (queue == IEEE80211_TX_QUEUE_DATA1)); - rt2x00_set_field32(®, TXCSR0_KICK_ATIM, - (queue == IEEE80211_TX_QUEUE_AFTER_BEACON)); - rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); +out: + /* + * Enable beaconing again. + */ + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); } /* * RX control handlers */ -static void rt2400pci_fill_rxdone(struct data_entry *entry, - struct rxdata_entry_desc *desc) +static void rt2400pci_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) { - __le32 *rxd = entry->priv; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; u32 word0; u32 word2; + u32 word3; + u32 word4; + u64 tsf; + u32 rx_low; + u32 rx_high; - rt2x00_desc_read(rxd, 0, &word0); - rt2x00_desc_read(rxd, 2, &word2); + rt2x00_desc_read(entry_priv->desc, 0, &word0); + rt2x00_desc_read(entry_priv->desc, 2, &word2); + rt2x00_desc_read(entry_priv->desc, 3, &word3); + rt2x00_desc_read(entry_priv->desc, 4, &word4); - desc->flags = 0; if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - desc->flags |= RX_FLAG_FAILED_FCS_CRC; + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) - desc->flags |= RX_FLAG_FAILED_PLCP_CRC; + rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; + + /* + * We only get the lower 32bits from the timestamp, + * to get the full 64bits we must complement it with + * the timestamp from get_tsf(). + * Note that when a wraparound of the lower 32bits + * has occurred between the frame arrival and the get_tsf() + * call, we must decrease the higher 32bits with 1 to get + * to correct value. + */ + tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw, NULL); + rx_low = rt2x00_get_field32(word4, RXD_W4_RX_END_TIME); + rx_high = upper_32_bits(tsf); + + if ((u32)tsf <= rx_low) + rx_high--; /* * Obtain the status about this packet. + * The signal is the PLCP value, and needs to be stripped + * of the preamble bit (0x08). */ - desc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); - desc->rssi = rt2x00_get_field32(word2, RXD_W2_RSSI) - - entry->ring->rt2x00dev->rssi_offset; - desc->ofdm = 0; - desc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - desc->my_bss = !!rt2x00_get_field32(word0, RXD_W0_MY_BSS); + rxdesc->timestamp = ((u64)rx_high << 32) | rx_low; + rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL) & ~0x08; + rxdesc->rssi = rt2x00_get_field32(word3, RXD_W3_RSSI) - + entry->queue->rt2x00dev->rssi_offset; + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; } /* * Interrupt functions. */ -static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, const int queue) +static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, + const enum data_queue_qid queue_idx) { - struct data_ring *ring = rt2x00lib_get_ring(rt2x00dev, queue); - struct data_entry *entry; - __le32 *txd; + struct data_queue *queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + struct queue_entry_priv_mmio *entry_priv; + struct queue_entry *entry; + struct txdone_entry_desc txdesc; u32 word; - int tx_status; - int retry; - while (!rt2x00_ring_empty(ring)) { - entry = rt2x00_get_data_entry_done(ring); - txd = entry->priv; - rt2x00_desc_read(txd, 0, &word); + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + entry_priv = entry->priv_data; + rt2x00_desc_read(entry_priv->desc, 0, &word); if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || !rt2x00_get_field32(word, TXD_W0_VALID)) @@ -1117,66 +1291,141 @@ static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, const int queue) /* * Obtain the status about this packet. */ - tx_status = rt2x00_get_field32(word, TXD_W0_RESULT); - retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); + txdesc.flags = 0; + switch (rt2x00_get_field32(word, TXD_W0_RESULT)) { + case 0: /* Success */ + case 1: /* Success with retry */ + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + break; + case 2: /* Failure, excessive retries */ + __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); + /* Don't break, this is a failed frame! */ + default: /* Failure */ + __set_bit(TXDONE_FAILURE, &txdesc.flags); + } + txdesc.retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); - rt2x00pci_txdone(rt2x00dev, entry, tx_status, retry); + rt2x00lib_txdone(entry, &txdesc); } } +static inline void rt2400pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, irq_field, 0); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +static void rt2400pci_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + u32 reg; + + /* + * Handle all tx queues. + */ + rt2400pci_txdone(rt2x00dev, QID_ATIM); + rt2400pci_txdone(rt2x00dev, QID_AC_VO); + rt2400pci_txdone(rt2x00dev, QID_AC_VI); + + /* + * Enable all TXDONE interrupts again. + */ + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); + } +} + +static void rt2400pci_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_beacondone(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2400pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); +} + +static void rt2400pci_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2400pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); +} + static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) { struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg; + u32 reg, mask; /* * Get the interrupt sources & saved to local variable. * Write register value back to clear pending interrupts. */ - rt2x00pci_register_read(rt2x00dev, CSR7, ®); - rt2x00pci_register_write(rt2x00dev, CSR7, reg); + rt2x00mmio_register_read(rt2x00dev, CSR7, ®); + rt2x00mmio_register_write(rt2x00dev, CSR7, reg); if (!reg) return IRQ_NONE; - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return IRQ_HANDLED; - /* - * Handle interrupts, walk through all bits - * and run the tasks, the bits are checked in order of - * priority. - */ + mask = reg; /* - * 1 - Beacon timer expired interrupt. + * Schedule tasklets for interrupt handling. */ if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) - rt2x00lib_beacondone(rt2x00dev); + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); - /* - * 2 - Rx ring done interrupt. - */ if (rt2x00_get_field32(reg, CSR7_RXDONE)) - rt2x00pci_rxdone(rt2x00dev); + tasklet_schedule(&rt2x00dev->rxdone_tasklet); - /* - * 3 - Atim ring transmit done interrupt. - */ - if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING)) - rt2400pci_txdone(rt2x00dev, IEEE80211_TX_QUEUE_AFTER_BEACON); + if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || + rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || + rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + /* + * Mask out all txdone interrupts. + */ + rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); + rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); + rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); + } /* - * 4 - Priority ring transmit done interrupt. + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. */ - if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING)) - rt2400pci_txdone(rt2x00dev, IEEE80211_TX_QUEUE_DATA0); + spin_lock(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + reg |= mask; + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock(&rt2x00dev->irqmask_lock); + - /* - * 5 - Tx ring transmit done interrupt. - */ - if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) - rt2400pci_txdone(rt2x00dev, IEEE80211_TX_QUEUE_DATA1); return IRQ_HANDLED; } @@ -1191,7 +1440,7 @@ static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) u16 word; u8 *mac; - rt2x00pci_register_read(rt2x00dev, CSR21, ®); + rt2x00mmio_register_read(rt2x00dev, CSR21, ®); eeprom.data = rt2x00dev; eeprom.register_read = rt2400pci_eepromregister_read; @@ -1211,15 +1460,13 @@ static int rt2400pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { - DECLARE_MAC_BUF(macbuf); - - random_ether_addr(mac); - EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac)); + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); if (word == 0xffff) { - ERROR(rt2x00dev, "Invalid EEPROM data detected.\n"); + rt2x00_err(rt2x00dev, "Invalid EEPROM data detected\n"); return -EINVAL; } @@ -1241,12 +1488,12 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * Identify RF chipset. */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2x00pci_register_read(rt2x00dev, CSR0, ®); - rt2x00_set_chip(rt2x00dev, RT2460, value, reg); + rt2x00mmio_register_read(rt2x00dev, CSR0, ®); + rt2x00_set_chip(rt2x00dev, RT2460, value, + rt2x00_get_field32(reg, CSR0_REVISION)); - if (!rt2x00_rf(&rt2x00dev->chip, RF2420) && - !rt2x00_rf(&rt2x00dev->chip, RF2421)) { - ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); + if (!rt2x00_rf(rt2x00dev, RF2420) && !rt2x00_rf(rt2x00dev, RF2421)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); return -ENODEV; } @@ -1272,22 +1519,28 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Store led mode, for correct led behaviour. */ - rt2x00dev->led_mode = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); +#ifdef CONFIG_RT2X00_LIB_LEDS + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); + + rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + if (value == LED_MODE_TXRX_ACTIVITY || + value == LED_MODE_DEFAULT || + value == LED_MODE_ASUS) + rt2400pci_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_ACTIVITY); +#endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Detect if this device has an hardware controlled radio. */ -#ifdef CONFIG_RT2400PCI_RFKILL if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) - __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT2400PCI_RFKILL */ + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Check if the BBP tuning should be enabled. */ - if (!rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_AGCVGC_TUNING)) - __set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags); + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_AGCVGC_TUNING)) + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); return 0; } @@ -1296,7 +1549,7 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * RF value list for RF2420 & RF2421 * Supports: 2.4 GHz */ -static const struct rf_channel rf_vals_bg[] = { +static const struct rf_channel rf_vals_b[] = { { 1, 0x00022058, 0x000c1fda, 0x00000101, 0 }, { 2, 0x00022058, 0x000c1fee, 0x00000101, 0 }, { 3, 0x00022058, 0x000c2002, 0x00000101, 0 }, @@ -1313,49 +1566,57 @@ static const struct rf_channel rf_vals_bg[] = { { 14, 0x00022058, 0x000c20fa, 0x00000101, 0 }, }; -static void rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* * Initialize all hw fields. */ - rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; - rt2x00dev->hw->extra_tx_headroom = 0; - rt2x00dev->hw->max_signal = MAX_SIGNAL; - rt2x00dev->hw->max_rssi = MAX_RX_SSI; - rt2x00dev->hw->queues = 2; + rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; - SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_pci(rt2x00dev)->dev); + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* - * Convert tx_power array in eeprom. + * Initialize hw_mode information. */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK; + + spec->num_channels = ARRAY_SIZE(rf_vals_b); + spec->channels = rf_vals_b; /* - * Initialize hw_mode information. + * Create channel information array */ - spec->num_modes = 1; - spec->num_rates = 4; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; - - spec->num_channels = ARRAY_SIZE(rf_vals_bg); - spec->channels = rf_vals_bg; + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) { + info[i].max_power = TXPOWER_FROM_DEV(MAX_TXPOWER); + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + } + + return 0; } static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; + u32 reg; /* * Allocate eeprom data. @@ -1369,14 +1630,26 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) return retval; /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); + rt2x00_set_field32(®, GPIOCSR_DIR0, 1); + rt2x00mmio_register_write(rt2x00dev, GPIOCSR, reg); + + /* * Initialize hw specifications. */ - rt2400pci_probe_hw_mode(rt2x00dev); + retval = rt2400pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* - * This device requires the beacon ring + * This device requires the atim queue and DMA-mapped skbs. */ - __set_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags); + __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); /* * Set the rssi offset. @@ -1389,80 +1662,8 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static void rt2400pci_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; - - /* - * Mask off any flags we are going to ignore from - * the total_flags field. - */ - *total_flags &= - FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_PROMISC_IN_BSS; - - /* - * Apply some rules to the filters: - * - Some filters imply different filters to be set. - * - Some things we can't filter out at all. - */ - *total_flags |= FIF_ALLMULTI; - if (*total_flags & FIF_OTHER_BSS || - *total_flags & FIF_PROMISC_IN_BSS) - *total_flags |= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS; - - /* - * Check if there is any work left for us. - */ - if (rt2x00dev->packet_filter == *total_flags) - return; - rt2x00dev->packet_filter = *total_flags; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * since there is no filter for it at this time. - */ - rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DROP_CRC, - !(*total_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, - !(*total_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, - !(*total_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, RXCSR0_DROP_TODS, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); - rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); -} - -static int rt2400pci_set_retry_limit(struct ieee80211_hw *hw, - u32 short_retry, u32 long_retry) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; - - rt2x00pci_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_LONG_RETRY, long_retry); - rt2x00_set_field32(®, CSR11_SHORT_RETRY, short_retry); - rt2x00pci_register_write(rt2x00dev, CSR11, reg); - - return 0; -} - static int rt2400pci_conf_tx(struct ieee80211_hw *hw, - int queue, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -1472,48 +1673,42 @@ static int rt2400pci_conf_tx(struct ieee80211_hw *hw, * per queue. So by default we only configure the TX queue, * and ignore all other configurations. */ - if (queue != IEEE80211_TX_QUEUE_DATA0) + if (queue != 0) return -EINVAL; - if (rt2x00mac_conf_tx(hw, queue, params)) + if (rt2x00mac_conf_tx(hw, vif, queue, params)) return -EINVAL; /* * Write configuration to register. */ - rt2400pci_config_cw(rt2x00dev, &rt2x00dev->tx->tx_params); + rt2400pci_config_cw(rt2x00dev, + rt2x00dev->tx->cw_min, rt2x00dev->tx->cw_max); return 0; } -static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; - rt2x00pci_register_read(rt2x00dev, CSR17, ®); + rt2x00mmio_register_read(rt2x00dev, CSR17, ®); tsf = (u64) rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; - rt2x00pci_register_read(rt2x00dev, CSR16, ®); + rt2x00mmio_register_read(rt2x00dev, CSR16, ®); tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); return tsf; } -static void rt2400pci_reset_tsf(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - rt2x00pci_register_write(rt2x00dev, CSR16, 0); - rt2x00pci_register_write(rt2x00dev, CSR17, 0); -} - static int rt2400pci_tx_last_beacon(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; u32 reg; - rt2x00pci_register_read(rt2x00dev, CSR15, ®); + rt2x00mmio_register_read(rt2x00dev, CSR15, ®); return rt2x00_get_field32(reg, CSR15_BEACON_SENT); } @@ -1524,63 +1719,114 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, - .configure_filter = rt2400pci_configure_filter, + .configure_filter = rt2x00mac_configure_filter, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, - .set_retry_limit = rt2400pci_set_retry_limit, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2400pci_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt2400pci_get_tsf, - .reset_tsf = rt2400pci_reset_tsf, - .beacon_update = rt2x00pci_beacon_update, .tx_last_beacon = rt2400pci_tx_last_beacon, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { .irq_handler = rt2400pci_interrupt, + .txstatus_tasklet = rt2400pci_txstatus_tasklet, + .tbtt_tasklet = rt2400pci_tbtt_tasklet, + .rxdone_tasklet = rt2400pci_rxdone_tasklet, .probe_hw = rt2400pci_probe_hw, - .initialize = rt2x00pci_initialize, - .uninitialize = rt2x00pci_uninitialize, - .init_rxentry = rt2400pci_init_rxentry, - .init_txentry = rt2400pci_init_txentry, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2400pci_get_entry_state, + .clear_entry = rt2400pci_clear_entry, .set_device_state = rt2400pci_set_device_state, .rfkill_poll = rt2400pci_rfkill_poll, .link_stats = rt2400pci_link_stats, .reset_tuner = rt2400pci_reset_tuner, .link_tuner = rt2400pci_link_tuner, + .start_queue = rt2400pci_start_queue, + .kick_queue = rt2400pci_kick_queue, + .stop_queue = rt2400pci_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, .write_tx_desc = rt2400pci_write_tx_desc, - .write_tx_data = rt2x00pci_write_tx_data, - .kick_tx_queue = rt2400pci_kick_tx_queue, + .write_beacon = rt2400pci_write_beacon, .fill_rxdone = rt2400pci_fill_rxdone, - .config_mac_addr = rt2400pci_config_mac_addr, - .config_bssid = rt2400pci_config_bssid, - .config_type = rt2400pci_config_type, - .config_preamble = rt2400pci_config_preamble, + .config_filter = rt2400pci_config_filter, + .config_intf = rt2400pci_config_intf, + .config_erp = rt2400pci_config_erp, + .config_ant = rt2400pci_config_ant, .config = rt2400pci_config, }; +static void rt2400pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 24; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 24; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + default: + BUG(); + break; + } +} + static const struct rt2x00_ops rt2400pci_ops = { - .name = KBUILD_MODNAME, - .rxd_size = RXD_DESC_SIZE, - .txd_size = TXD_DESC_SIZE, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .lib = &rt2400pci_rt2x00_ops, - .hw = &rt2400pci_mac80211_ops, + .name = KBUILD_MODNAME, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2400pci_queue_init, + .lib = &rt2400pci_rt2x00_ops, + .hw = &rt2400pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2400pci_rt2x00debug, + .debugfs = &rt2400pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * RT2400pci module information. */ -static struct pci_device_id rt2400pci_device_table[] = { - { PCI_DEVICE(0x1814, 0x0101), PCI_DEVICE_DATA(&rt2400pci_ops) }, +static DEFINE_PCI_DEVICE_TABLE(rt2400pci_device_table) = { + { PCI_DEVICE(0x1814, 0x0101) }, { 0, } }; + MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2400 PCI & PCMCIA Wireless LAN driver."); @@ -1588,24 +1834,19 @@ MODULE_SUPPORTED_DEVICE("Ralink RT2460 PCI & PCMCIA chipset based cards"); MODULE_DEVICE_TABLE(pci, rt2400pci_device_table); MODULE_LICENSE("GPL"); +static int rt2400pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + return rt2x00pci_probe(pci_dev, &rt2400pci_ops); +} + static struct pci_driver rt2400pci_driver = { .name = KBUILD_MODNAME, .id_table = rt2400pci_device_table, - .probe = rt2x00pci_probe, - .remove = __devexit_p(rt2x00pci_remove), + .probe = rt2400pci_probe, + .remove = rt2x00pci_remove, .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; -static int __init rt2400pci_init(void) -{ - return pci_register_driver(&rt2400pci_driver); -} - -static void __exit rt2400pci_exit(void) -{ - pci_unregister_driver(&rt2400pci_driver); -} - -module_init(rt2400pci_init); -module_exit(rt2400pci_exit); +module_pci_driver(rt2400pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h index 369aac6d033..0fd3a9d01a6 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.h +++ b/drivers/net/wireless/rt2x00/rt2400pci.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -35,10 +33,8 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ -#define MAX_SIGNAL 100 -#define MAX_RX_SSI -1 #define DEFAULT_RSSI_OFFSET 100 /* @@ -48,8 +44,15 @@ #define CSR_REG_SIZE 0x014c #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0100 +#define BBP_BASE 0x0000 #define BBP_SIZE 0x0020 -#define RF_SIZE 0x0010 +#define RF_BASE 0x0004 +#define RF_SIZE 0x000c + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 2 /* * Control/Status Registers(CSR). @@ -60,6 +63,7 @@ * CSR0: ASIC revision number. */ #define CSR0 0x0000 +#define CSR0_REVISION FIELD32(0x0000ffff) /* * CSR1: System control register. @@ -654,16 +658,26 @@ /* * GPIOCSR: GPIO control register. + * GPIOCSR_VALx: Actual GPIO pin x value + * GPIOCSR_DIRx: GPIO direction: 0 = output; 1 = input */ #define GPIOCSR 0x0120 -#define GPIOCSR_BIT0 FIELD32(0x00000001) -#define GPIOCSR_BIT1 FIELD32(0x00000002) -#define GPIOCSR_BIT2 FIELD32(0x00000004) -#define GPIOCSR_BIT3 FIELD32(0x00000008) -#define GPIOCSR_BIT4 FIELD32(0x00000010) -#define GPIOCSR_BIT5 FIELD32(0x00000020) -#define GPIOCSR_BIT6 FIELD32(0x00000040) -#define GPIOCSR_BIT7 FIELD32(0x00000080) +#define GPIOCSR_VAL0 FIELD32(0x00000001) +#define GPIOCSR_VAL1 FIELD32(0x00000002) +#define GPIOCSR_VAL2 FIELD32(0x00000004) +#define GPIOCSR_VAL3 FIELD32(0x00000008) +#define GPIOCSR_VAL4 FIELD32(0x00000010) +#define GPIOCSR_VAL5 FIELD32(0x00000020) +#define GPIOCSR_VAL6 FIELD32(0x00000040) +#define GPIOCSR_VAL7 FIELD32(0x00000080) +#define GPIOCSR_DIR0 FIELD32(0x00000100) +#define GPIOCSR_DIR1 FIELD32(0x00000200) +#define GPIOCSR_DIR2 FIELD32(0x00000400) +#define GPIOCSR_DIR3 FIELD32(0x00000800) +#define GPIOCSR_DIR4 FIELD32(0x00001000) +#define GPIOCSR_DIR5 FIELD32(0x00002000) +#define GPIOCSR_DIR6 FIELD32(0x00004000) +#define GPIOCSR_DIR7 FIELD32(0x00008000) /* * BBPPCSR: BBP Pin control register. @@ -803,8 +817,8 @@ /* * DMA descriptor defines. */ -#define TXD_DESC_SIZE ( 8 * sizeof(__le32) ) -#define RXD_DESC_SIZE ( 8 * sizeof(__le32) ) +#define TXD_DESC_SIZE (8 * sizeof(__le32)) +#define RXD_DESC_SIZE (8 * sizeof(__le32)) /* * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. @@ -899,13 +913,13 @@ * Word2 */ #define RXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) -#define RXD_W2_SIGNAL FIELD32(0x00ff0000) -#define RXD_W2_RSSI FIELD32(0xff000000) +#define RXD_W2_BBR0 FIELD32(0x00ff0000) +#define RXD_W2_SIGNAL FIELD32(0xff000000) /* * Word3 */ -#define RXD_W3_BBR2 FIELD32(0x000000ff) +#define RXD_W3_RSSI FIELD32(0x000000ff) #define RXD_W3_BBR3 FIELD32(0x0000ff00) #define RXD_W3_BBR4 FIELD32(0x00ff0000) #define RXD_W3_BBR5 FIELD32(0xff000000) @@ -923,31 +937,25 @@ #define RXD_W7_RESERVED FIELD32(0xffffffff) /* - * Macro's for converting txpower from EEPROM to dscape value - * and from dscape value to register value. + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. * NOTE: Logics in rt2400pci for txpower are reversed * compared to the other rt2x00 drivers. A higher txpower * value means that the txpower must be lowered. This is * important when converting the value coming from the - * dscape stack to the rt2400 acceptable value. + * mac80211 stack to the rt2400 acceptable value. */ #define MIN_TXPOWER 31 #define MAX_TXPOWER 62 #define DEFAULT_TXPOWER 39 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? DEFAULT_TXPOWER - MIN_TXPOWER : \ - ((__txpower) < MIN_TXPOWER) ? DEFAULT_TXPOWER - MIN_TXPOWER : \ - (((__txpower) - MAX_TXPOWER) + MIN_TXPOWER); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - (__txpower) += MIN_TXPOWER; \ - ((__txpower) <= MIN_TXPOWER) ? MAX_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MIN_TXPOWER : \ - (MAX_TXPOWER - ((__txpower) - MIN_TXPOWER))); \ -}) +#define __CLAMP_TX(__txpower) \ + clamp_t(char, (__txpower), MIN_TXPOWER, MAX_TXPOWER) + +#define TXPOWER_FROM_DEV(__txpower) \ + ((__CLAMP_TX(__txpower) - MAX_TXPOWER) + MIN_TXPOWER) + +#define TXPOWER_TO_DEV(__txpower) \ + (MAX_TXPOWER - (__CLAMP_TX(__txpower) - MIN_TXPOWER)) #endif /* RT2400PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 91e87b53374..a511cccc9f0 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -26,20 +24,21 @@ #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/eeprom_93cx6.h> +#include <linux/slab.h> #include "rt2x00.h" +#include "rt2x00mmio.h" #include "rt2x00pci.h" #include "rt2500pci.h" /* * Register access. * All access to the CSR registers will go through the methods - * rt2x00pci_register_read and rt2x00pci_register_write. + * rt2x00mmio_register_read and rt2x00mmio_register_write. * BBP and RF register require indirect register access, * and use the CSR registers BBPCSR and RFCSR to achieve this. * These indirect registers work with busy bits, @@ -49,45 +48,33 @@ * the access attempt is considered to have failed, * and we will print an error. */ -static u32 rt2500pci_bbp_check(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - unsigned int i; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, BBPCSR, ®); - if (!rt2x00_get_field32(reg, BBPCSR_BUSY)) - break; - udelay(REGISTER_BUSY_DELAY); - } - - return reg; -} +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), BBPCSR, BBPCSR_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), RFCSR, RFCSR_BUSY, (__reg)) static void rt2500pci_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; - /* - * Wait until the BBP becomes ready. - */ - reg = rt2500pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, BBPCSR_BUSY)) { - ERROR(rt2x00dev, "BBPCSR register busy. Write failed.\n"); - return; - } + mutex_lock(&rt2x00dev->csr_mutex); /* - * Write the data into the BBP. + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. */ - reg = 0; - rt2x00_set_field32(®, BBPCSR_VALUE, value); - rt2x00_set_field32(®, BBPCSR_REGNUM, word); - rt2x00_set_field32(®, BBPCSR_BUSY, 1); - rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBPCSR_VALUE, value); + rt2x00_set_field32(®, BBPCSR_REGNUM, word); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); + + rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); + } - rt2x00pci_register_write(rt2x00dev, BBPCSR, reg); + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500pci_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -95,66 +82,55 @@ static void rt2500pci_bbp_read(struct rt2x00_dev *rt2x00dev, { u32 reg; - /* - * Wait until the BBP becomes ready. - */ - reg = rt2500pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, BBPCSR_BUSY)) { - ERROR(rt2x00dev, "BBPCSR register busy. Read failed.\n"); - return; - } + mutex_lock(&rt2x00dev->csr_mutex); /* - * Write the request into the BBP. + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. */ - reg = 0; - rt2x00_set_field32(®, BBPCSR_REGNUM, word); - rt2x00_set_field32(®, BBPCSR_BUSY, 1); - rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBPCSR_REGNUM, word); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); - rt2x00pci_register_write(rt2x00dev, BBPCSR, reg); + rt2x00mmio_register_write(rt2x00dev, BBPCSR, reg); - /* - * Wait until the BBP becomes ready. - */ - reg = rt2500pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, BBPCSR_BUSY)) { - ERROR(rt2x00dev, "BBPCSR register busy. Read failed.\n"); - *value = 0xff; - return; + WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, BBPCSR_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500pci_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; - unsigned int i; - - if (!word) - return; - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, RFCSR, ®); - if (!rt2x00_get_field32(reg, RFCSR_BUSY)) - goto rf_write; - udelay(REGISTER_BUSY_DELAY); - } + mutex_lock(&rt2x00dev->csr_mutex); - ERROR(rt2x00dev, "RFCSR register busy. Write failed.\n"); - return; + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RFCSR_VALUE, value); + rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); + rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); + rt2x00_set_field32(®, RFCSR_BUSY, 1); -rf_write: - reg = 0; - rt2x00_set_field32(®, RFCSR_VALUE, value); - rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); - rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); - rt2x00_set_field32(®, RFCSR_BUSY, 1); + rt2x00mmio_register_write(rt2x00dev, RFCSR, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } - rt2x00pci_register_write(rt2x00dev, RFCSR, reg); - rt2x00_rf_write(rt2x00dev, word, value); + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500pci_eepromregister_read(struct eeprom_93cx6 *eeprom) @@ -162,7 +138,7 @@ static void rt2500pci_eepromregister_read(struct eeprom_93cx6 *eeprom) struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg; - rt2x00pci_register_read(rt2x00dev, CSR21, ®); + rt2x00mmio_register_read(rt2x00dev, CSR21, ®); eeprom->reg_data_in = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_IN); eeprom->reg_data_out = !!rt2x00_get_field32(reg, CSR21_EEPROM_DATA_OUT); @@ -184,116 +160,171 @@ static void rt2500pci_eepromregister_write(struct eeprom_93cx6 *eeprom) rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, !!eeprom->reg_chip_select); - rt2x00pci_register_write(rt2x00dev, CSR21, reg); + rt2x00mmio_register_write(rt2x00dev, CSR21, reg); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS -#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) ) - -static void rt2500pci_read_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 *data) -{ - rt2x00pci_register_read(rt2x00dev, CSR_OFFSET(word), data); -} - -static void rt2500pci_write_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 data) -{ - rt2x00pci_register_write(rt2x00dev, CSR_OFFSET(word), data); -} - static const struct rt2x00debug rt2500pci_rt2x00debug = { .owner = THIS_MODULE, .csr = { - .read = rt2500pci_read_csr, - .write = rt2500pci_write_csr, + .read = rt2x00mmio_register_read, + .write = rt2x00mmio_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt2500pci_bbp_read, .write = rt2500pci_bbp_write, + .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt2500pci_rf_write, + .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT2500PCI_RFKILL static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt2x00pci_register_read(rt2x00dev, GPIOCSR, ®); - return rt2x00_get_field32(reg, GPIOCSR_BIT0); + rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); + return rt2x00_get_field32(reg, GPIOCSR_VAL0); } -#else -#define rt2500pci_rfkill_poll NULL -#endif /* CONFIG_RT2500PCI_RFKILL */ -/* - * Configuration handlers. - */ -static void rt2500pci_config_mac_addr(struct rt2x00_dev *rt2x00dev, - __le32 *mac) +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2500pci_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); + + if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) + rt2x00_set_field32(®, LEDCSR_LINK, enabled); + else if (led->type == LED_TYPE_ACTIVITY) + rt2x00_set_field32(®, LEDCSR_ACTIVITY, enabled); + + rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); +} + +static int rt2500pci_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) { - rt2x00pci_register_multiwrite(rt2x00dev, CSR3, mac, - (2 * sizeof(__le32))); + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; + + rt2x00mmio_register_read(led->rt2x00dev, LEDCSR, ®); + rt2x00_set_field32(®, LEDCSR_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, *delay_off); + rt2x00mmio_register_write(led->rt2x00dev, LEDCSR, reg); + + return 0; } -static void rt2500pci_config_bssid(struct rt2x00_dev *rt2x00dev, - __le32 *bssid) +static void rt2500pci_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) { - rt2x00pci_register_multiwrite(rt2x00dev, CSR5, bssid, - (2 * sizeof(__le32))); + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2500pci_brightness_set; + led->led_dev.blink_set = rt2500pci_blink_set; + led->flags = LED_INITIALIZED; } +#endif /* CONFIG_RT2X00_LIB_LEDS */ -static void rt2500pci_config_type(struct rt2x00_dev *rt2x00dev, const int type, - const int tsf_sync) +/* + * Configuration handlers. + */ +static void rt2500pci_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) { u32 reg; - rt2x00pci_register_write(rt2x00dev, CSR14, 0); - /* - * Enable beacon config + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. */ - rt2x00pci_register_read(rt2x00dev, BCNCSR1, ®); - rt2x00_set_field32(®, BCNCSR1_PRELOAD, - PREAMBLE + get_duration(IEEE80211_HEADER, 20)); - rt2x00_set_field32(®, BCNCSR1_BEACON_CWMIN, - rt2x00lib_get_ring(rt2x00dev, - IEEE80211_TX_QUEUE_BEACON) - ->tx_params.cw_min); - rt2x00pci_register_write(rt2x00dev, BCNCSR1, reg); + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, + !(filter_flags & FIF_PROMISC_IN_BSS)); + rt2x00_set_field32(®, RXCSR0_DROP_TODS, + !(filter_flags & FIF_PROMISC_IN_BSS) && + !rt2x00dev->intf_ap_count); + rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); + rt2x00_set_field32(®, RXCSR0_DROP_MCAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, RXCSR0_DROP_BCAST, 0); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); +} - /* - * Enable synchronisation. - */ - rt2x00pci_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); - rt2x00_set_field32(®, CSR14_TBCN, (tsf_sync == TSF_SYNC_BEACON)); - rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); - rt2x00_set_field32(®, CSR14_TSF_SYNC, tsf_sync); - rt2x00pci_register_write(rt2x00dev, CSR14, reg); +static void rt2500pci_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) +{ + struct data_queue *queue = rt2x00dev->bcn; + unsigned int bcn_preload; + u32 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable beacon config + */ + bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); + rt2x00mmio_register_read(rt2x00dev, BCNCSR1, ®); + rt2x00_set_field32(®, BCNCSR1_PRELOAD, bcn_preload); + rt2x00_set_field32(®, BCNCSR1_BEACON_CWMIN, queue->cw_min); + rt2x00mmio_register_write(rt2x00dev, BCNCSR1, reg); + + /* + * Enable synchronisation. + */ + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_SYNC, conf->sync); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + } + + if (flags & CONFIG_UPDATE_MAC) + rt2x00mmio_register_multiwrite(rt2x00dev, CSR3, + conf->mac, sizeof(conf->mac)); + + if (flags & CONFIG_UPDATE_BSSID) + rt2x00mmio_register_multiwrite(rt2x00dev, CSR5, + conf->bssid, sizeof(conf->bssid)); } -static void rt2500pci_config_preamble(struct rt2x00_dev *rt2x00dev, - const int short_preamble, - const int ack_timeout, - const int ack_consume_time) +static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) { int preamble_mask; u32 reg; @@ -301,42 +332,144 @@ static void rt2500pci_config_preamble(struct rt2x00_dev *rt2x00dev, /* * When short preamble is enabled, we should set bit 0x08 */ - preamble_mask = short_preamble << 3; - - rt2x00pci_register_read(rt2x00dev, TXCSR1, ®); - rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, ack_timeout); - rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, ack_consume_time); - rt2x00pci_register_write(rt2x00dev, TXCSR1, reg); - - rt2x00pci_register_read(rt2x00dev, ARCSR2, ®); - rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00 | preamble_mask); - rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, get_duration(ACK_SIZE, 10)); - rt2x00pci_register_write(rt2x00dev, ARCSR2, reg); - - rt2x00pci_register_read(rt2x00dev, ARCSR3, ®); - rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); - rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, get_duration(ACK_SIZE, 20)); - rt2x00pci_register_write(rt2x00dev, ARCSR3, reg); - - rt2x00pci_register_read(rt2x00dev, ARCSR4, ®); - rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); - rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); - rt2x00_set_field32(®, ARCSR2_LENGTH, get_duration(ACK_SIZE, 55)); - rt2x00pci_register_write(rt2x00dev, ARCSR4, reg); - - rt2x00pci_register_read(rt2x00dev, ARCSR5, ®); - rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); - rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); - rt2x00_set_field32(®, ARCSR2_LENGTH, get_duration(ACK_SIZE, 110)); - rt2x00pci_register_write(rt2x00dev, ARCSR5, reg); + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + preamble_mask = erp->short_preamble << 3; + + rt2x00mmio_register_read(rt2x00dev, TXCSR1, ®); + rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, 0x162); + rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, 0xa2); + rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR1, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR2, ®); + rt2x00_set_field32(®, ARCSR2_SIGNAL, 0x00); + rt2x00_set_field32(®, ARCSR2_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 10)); + rt2x00mmio_register_write(rt2x00dev, ARCSR2, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR3, ®); + rt2x00_set_field32(®, ARCSR3_SIGNAL, 0x01 | preamble_mask); + rt2x00_set_field32(®, ARCSR3_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 20)); + rt2x00mmio_register_write(rt2x00dev, ARCSR3, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR4, ®); + rt2x00_set_field32(®, ARCSR4_SIGNAL, 0x02 | preamble_mask); + rt2x00_set_field32(®, ARCSR4_SERVICE, 0x04); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 55)); + rt2x00mmio_register_write(rt2x00dev, ARCSR4, reg); + + rt2x00mmio_register_read(rt2x00dev, ARCSR5, ®); + rt2x00_set_field32(®, ARCSR5_SIGNAL, 0x03 | preamble_mask); + rt2x00_set_field32(®, ARCSR5_SERVICE, 0x84); + rt2x00_set_field32(®, ARCSR2_LENGTH, + GET_DURATION(ACK_SIZE, 110)); + rt2x00mmio_register_write(rt2x00dev, ARCSR5, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) + rt2x00mmio_register_write(rt2x00dev, ARCSR1, erp->basic_rates); + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR18, ®); + rt2x00_set_field32(®, CSR18_SIFS, erp->sifs); + rt2x00_set_field32(®, CSR18_PIFS, erp->pifs); + rt2x00mmio_register_write(rt2x00dev, CSR18, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR19, ®); + rt2x00_set_field32(®, CSR19_DIFS, erp->difs); + rt2x00_set_field32(®, CSR19_EIFS, erp->eifs); + rt2x00mmio_register_write(rt2x00dev, CSR19, reg); + } + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2x00mmio_register_read(rt2x00dev, CSR12, ®); + rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, + erp->beacon_int * 16); + rt2x00mmio_register_write(rt2x00dev, CSR12, reg); + } + } -static void rt2500pci_config_phymode(struct rt2x00_dev *rt2x00dev, - const int basic_rate_mask) +static void rt2500pci_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) { - rt2x00pci_register_write(rt2x00dev, ARCSR1, basic_rate_mask); + u32 reg; + u8 r14; + u8 r2; + + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + rt2x00mmio_register_read(rt2x00dev, BBPCSR1, ®); + rt2500pci_bbp_read(rt2x00dev, 14, &r14); + rt2500pci_bbp_read(rt2x00dev, 2, &r2); + + /* + * Configure the TX antenna. + */ + switch (ant->tx) { + case ANTENNA_A: + rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0); + rt2x00_set_field32(®, BBPCSR1_CCK, 0); + rt2x00_set_field32(®, BBPCSR1_OFDM, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); + rt2x00_set_field32(®, BBPCSR1_CCK, 2); + rt2x00_set_field32(®, BBPCSR1_OFDM, 2); + break; + } + + /* + * Configure the RX antenna. + */ + switch (ant->rx) { + case ANTENNA_A: + rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); + break; + case ANTENNA_B: + default: + rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); + break; + } + + /* + * RT2525E and RT5222 need to flip TX I/Q + */ + if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { + rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); + rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 1); + rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 1); + + /* + * RT2525E does not need RX I/Q Flip. + */ + if (rt2x00_rf(rt2x00dev, RF2525E)) + rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); + } else { + rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 0); + rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 0); + } + + rt2x00mmio_register_write(rt2x00dev, BBPCSR1, reg); + rt2500pci_bbp_write(rt2x00dev, 14, r14); + rt2500pci_bbp_write(rt2x00dev, 2, r2); } static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, @@ -353,14 +486,14 @@ static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, * Switch on tuning bits. * For RT2523 devices we do not need to update the R1 register. */ - if (!rt2x00_rf(&rt2x00dev->chip, RF2523)) + if (!rt2x00_rf(rt2x00dev, RF2523)) rt2x00_set_field32(&rf->rf1, RF1_TUNER, 1); rt2x00_set_field32(&rf->rf3, RF3_TUNER, 1); /* * For RT2525 we should first set the channel to half band higher. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525)) { + if (rt2x00_rf(rt2x00dev, RF2525)) { static const u32 vals[] = { 0x00080cbe, 0x00080d02, 0x00080d06, 0x00080d0a, 0x00080d0e, 0x00080d12, 0x00080d16, 0x00080d1a, @@ -394,7 +527,7 @@ static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, * Switch off tuning bits. * For RT2523 devices we do not need to update the R1 register. */ - if (!rt2x00_rf(&rt2x00dev->chip, RF2523)) { + if (!rt2x00_rf(rt2x00dev, RF2523)) { rt2x00_set_field32(&rf->rf1, RF1_TUNER, 0); rt2500pci_rf_write(rt2x00dev, 1, rf->rf1); } @@ -405,7 +538,7 @@ static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, /* * Clear false CRC during channel switch. */ - rt2x00pci_register_read(rt2x00dev, CNT0, &rf->rf1); + rt2x00mmio_register_read(rt2x00dev, CNT0, &rf->rf1); } static void rt2500pci_config_txpower(struct rt2x00_dev *rt2x00dev, @@ -418,161 +551,64 @@ static void rt2500pci_config_txpower(struct rt2x00_dev *rt2x00dev, rt2500pci_rf_write(rt2x00dev, 3, rf3); } -static void rt2500pci_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) +static void rt2500pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) { u32 reg; - u8 r14; - u8 r2; - rt2x00pci_register_read(rt2x00dev, BBPCSR1, ®); - rt2500pci_bbp_read(rt2x00dev, 14, &r14); - rt2500pci_bbp_read(rt2x00dev, 2, &r2); + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_LONG_RETRY, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, CSR11_SHORT_RETRY, + libconf->conf->short_frame_max_tx_count); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); +} - /* - * Configure the TX antenna. - */ - switch (ant->tx) { - case ANTENNA_A: - rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 0); - rt2x00_set_field32(®, BBPCSR1_CCK, 0); - rt2x00_set_field32(®, BBPCSR1_OFDM, 0); - break; - case ANTENNA_HW_DIVERSITY: - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ - case ANTENNA_B: - rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); - rt2x00_set_field32(®, BBPCSR1_CCK, 2); - rt2x00_set_field32(®, BBPCSR1_OFDM, 2); - break; - } +static void rt2500pci_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; - /* - * Configure the RX antenna. - */ - switch (ant->rx) { - case ANTENNA_A: - rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); - break; - case ANTENNA_HW_DIVERSITY: - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ - case ANTENNA_B: - rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); - break; - } + if (state == STATE_SLEEP) { + rt2x00mmio_register_read(rt2x00dev, CSR20, ®); + rt2x00_set_field32(®, CSR20_DELAY_AFTER_TBCN, + (rt2x00dev->beacon_int - 20) * 16); + rt2x00_set_field32(®, CSR20_TBCN_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); - /* - * RT2525E and RT5222 need to flip TX I/Q - */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E) || - rt2x00_rf(&rt2x00dev->chip, RF5222)) { - rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); - rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 1); - rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 1); + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); - /* - * RT2525E does not need RX I/Q Flip. - */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) - rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); + rt2x00_set_field32(®, CSR20_AUTOWAKE, 1); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); } else { - rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 0); - rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 0); + rt2x00mmio_register_read(rt2x00dev, CSR20, ®); + rt2x00_set_field32(®, CSR20_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, CSR20, reg); } - rt2x00pci_register_write(rt2x00dev, BBPCSR1, reg); - rt2500pci_bbp_write(rt2x00dev, 14, r14); - rt2500pci_bbp_write(rt2x00dev, 2, r2); -} - -static void rt2500pci_config_duration(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) -{ - u32 reg; - - rt2x00pci_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_SLOT_TIME, libconf->slot_time); - rt2x00pci_register_write(rt2x00dev, CSR11, reg); - - rt2x00pci_register_read(rt2x00dev, CSR18, ®); - rt2x00_set_field32(®, CSR18_SIFS, libconf->sifs); - rt2x00_set_field32(®, CSR18_PIFS, libconf->pifs); - rt2x00pci_register_write(rt2x00dev, CSR18, reg); - - rt2x00pci_register_read(rt2x00dev, CSR19, ®); - rt2x00_set_field32(®, CSR19_DIFS, libconf->difs); - rt2x00_set_field32(®, CSR19_EIFS, libconf->eifs); - rt2x00pci_register_write(rt2x00dev, CSR19, reg); - - rt2x00pci_register_read(rt2x00dev, TXCSR1, ®); - rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); - rt2x00pci_register_write(rt2x00dev, TXCSR1, reg); - - rt2x00pci_register_read(rt2x00dev, CSR12, ®); - rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, - libconf->conf->beacon_int * 16); - rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, - libconf->conf->beacon_int * 16); - rt2x00pci_register_write(rt2x00dev, CSR12, reg); + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); } static void rt2500pci_config(struct rt2x00_dev *rt2x00dev, - const unsigned int flags, - struct rt2x00lib_conf *libconf) + struct rt2x00lib_conf *libconf, + const unsigned int flags) { - if (flags & CONFIG_UPDATE_PHYMODE) - rt2500pci_config_phymode(rt2x00dev, libconf->basic_rates); - if (flags & CONFIG_UPDATE_CHANNEL) + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) rt2500pci_config_channel(rt2x00dev, &libconf->rf, libconf->conf->power_level); - if ((flags & CONFIG_UPDATE_TXPOWER) && !(flags & CONFIG_UPDATE_CHANNEL)) + if ((flags & IEEE80211_CONF_CHANGE_POWER) && + !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) rt2500pci_config_txpower(rt2x00dev, libconf->conf->power_level); - if (flags & CONFIG_UPDATE_ANTENNA) - rt2500pci_config_antenna(rt2x00dev, &libconf->ant); - if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT)) - rt2500pci_config_duration(rt2x00dev, libconf); -} - -/* - * LED functions. - */ -static void rt2500pci_enable_led(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00pci_register_read(rt2x00dev, LEDCSR, ®); - - rt2x00_set_field32(®, LEDCSR_ON_PERIOD, 70); - rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, 30); - rt2x00_set_field32(®, LEDCSR_LINK, - (rt2x00dev->led_mode != LED_MODE_ASUS)); - rt2x00_set_field32(®, LEDCSR_ACTIVITY, - (rt2x00dev->led_mode != LED_MODE_TXRX_ACTIVITY)); - rt2x00pci_register_write(rt2x00dev, LEDCSR, reg); -} - -static void rt2500pci_disable_led(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - rt2x00pci_register_read(rt2x00dev, LEDCSR, ®); - rt2x00_set_field32(®, LEDCSR_LINK, 0); - rt2x00_set_field32(®, LEDCSR_ACTIVITY, 0); - rt2x00pci_register_write(rt2x00dev, LEDCSR, reg); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt2500pci_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2500pci_config_ps(rt2x00dev, libconf); } /* @@ -586,43 +622,52 @@ static void rt2500pci_link_stats(struct rt2x00_dev *rt2x00dev, /* * Update FCS error count from register. */ - rt2x00pci_register_read(rt2x00dev, CNT0, ®); + rt2x00mmio_register_read(rt2x00dev, CNT0, ®); qual->rx_failed = rt2x00_get_field32(reg, CNT0_FCS_ERROR); /* * Update False CCA count from register. */ - rt2x00pci_register_read(rt2x00dev, CNT3, ®); + rt2x00mmio_register_read(rt2x00dev, CNT3, ®); qual->false_cca = rt2x00_get_field32(reg, CNT3_FALSE_CCA); } -static void rt2500pci_reset_tuner(struct rt2x00_dev *rt2x00dev) +static inline void rt2500pci_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) { - rt2500pci_bbp_write(rt2x00dev, 17, 0x48); - rt2x00dev->link.vgc_level = 0x48; + if (qual->vgc_level_reg != vgc_level) { + rt2500pci_bbp_write(rt2x00dev, 17, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } } -static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev) +static void rt2500pci_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) { - int rssi = rt2x00_get_link_rssi(&rt2x00dev->link); - u8 r17; + rt2500pci_set_vgc(rt2x00dev, qual, 0x48); +} +static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count) +{ /* * To prevent collisions with MAC ASIC on chipsets * up to version C the link tuning should halt after 20 - * seconds. + * seconds while being associated. */ - if (rt2x00_rev(&rt2x00dev->chip) < RT2560_VERSION_D && - rt2x00dev->link.count > 20) + if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D && + rt2x00dev->intf_associated && count > 20) return; - rt2500pci_bbp_read(rt2x00dev, 17, &r17); - /* * Chipset versions C and lower should directly continue - * to the dynamic CCA tuning. + * to the dynamic CCA tuning. Chipset version D and higher + * should go straight to dynamic CCA tuning when they + * are not associated. */ - if (rt2x00_rev(&rt2x00dev->chip) < RT2560_VERSION_D) + if (rt2x00_rev(rt2x00dev) < RT2560_VERSION_D || + !rt2x00dev->intf_associated) goto dynamic_cca_tune; /* @@ -630,29 +675,25 @@ static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev) * then corrupt the R17 tuning. To remidy this the tuning should * be stopped (While making sure the R17 value will not exceed limits) */ - if (rssi < -80 && rt2x00dev->link.count > 20) { - if (r17 >= 0x41) { - r17 = rt2x00dev->link.vgc_level; - rt2500pci_bbp_write(rt2x00dev, 17, r17); - } + if (qual->rssi < -80 && count > 20) { + if (qual->vgc_level_reg >= 0x41) + rt2500pci_set_vgc(rt2x00dev, qual, qual->vgc_level); return; } /* * Special big-R17 for short distance */ - if (rssi >= -58) { - if (r17 != 0x50) - rt2500pci_bbp_write(rt2x00dev, 17, 0x50); + if (qual->rssi >= -58) { + rt2500pci_set_vgc(rt2x00dev, qual, 0x50); return; } /* * Special mid-R17 for middle distance */ - if (rssi >= -74) { - if (r17 != 0x41) - rt2500pci_bbp_write(rt2x00dev, 17, 0x41); + if (qual->rssi >= -74) { + rt2500pci_set_vgc(rt2x00dev, qual, 0x41); return; } @@ -660,8 +701,8 @@ static void rt2500pci_link_tuner(struct rt2x00_dev *rt2x00dev) * Leave short or middle distance condition, restore r17 * to the dynamic tuning range. */ - if (r17 >= 0x41) { - rt2500pci_bbp_write(rt2x00dev, 17, rt2x00dev->link.vgc_level); + if (qual->vgc_level_reg >= 0x41) { + rt2500pci_set_vgc(rt2x00dev, qual, qual->vgc_level); return; } @@ -671,96 +712,190 @@ dynamic_cca_tune: * R17 is inside the dynamic tuning range, * start tuning the link based on the false cca counter. */ - if (rt2x00dev->link.qual.false_cca > 512 && r17 < 0x40) { - rt2500pci_bbp_write(rt2x00dev, 17, ++r17); - rt2x00dev->link.vgc_level = r17; - } else if (rt2x00dev->link.qual.false_cca < 100 && r17 > 0x32) { - rt2500pci_bbp_write(rt2x00dev, 17, --r17); - rt2x00dev->link.vgc_level = r17; + if (qual->false_cca > 512 && qual->vgc_level_reg < 0x40) + rt2500pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level_reg); + else if (qual->false_cca < 100 && qual->vgc_level_reg > 0x32) + rt2500pci_set_vgc(rt2x00dev, qual, --qual->vgc_level_reg); +} + +/* + * Queue handlers. + */ +static void rt2500pci_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); + rt2x00_set_field32(®, CSR14_TBCN, 1); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + break; + default: + break; + } +} + +static void rt2500pci_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_AC_VI: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_ATIM: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + default: + break; + } +} + +static void rt2500pci_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_ATIM: + rt2x00mmio_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_ABORT, 1); + rt2x00mmio_register_write(rt2x00dev, TXCSR0, reg); + break; + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, RXCSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); + rt2x00_set_field32(®, CSR14_TBCN, 0); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + /* + * Wait for possibly running tbtt tasklets. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + break; + default: + break; } } /* * Initialization functions. */ -static void rt2500pci_init_rxentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry) +static bool rt2500pci_get_entry_state(struct queue_entry *entry) { - __le32 *rxd = entry->priv; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; u32 word; - rt2x00_desc_read(rxd, 1, &word); - rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, entry->data_dma); - rt2x00_desc_write(rxd, 1, word); + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_desc_read(rxd, 0, &word); - rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); - rt2x00_desc_write(rxd, 0, word); + return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + + return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)); + } } -static void rt2500pci_init_txentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry) +static void rt2500pci_clear_entry(struct queue_entry *entry) { - __le32 *txd = entry->priv; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); u32 word; - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, entry->data_dma); - rt2x00_desc_write(txd, 1, word); + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 1, word); - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_VALID, 0); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); - rt2x00_desc_write(txd, 0, word); + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(entry_priv->desc, 0, word); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(entry_priv->desc, 0, word); + } } -static int rt2500pci_init_rings(struct rt2x00_dev *rt2x00dev) +static int rt2500pci_init_queues(struct rt2x00_dev *rt2x00dev) { + struct queue_entry_priv_mmio *entry_priv; u32 reg; /* * Initialize registers. */ - rt2x00pci_register_read(rt2x00dev, TXCSR2, ®); - rt2x00_set_field32(®, TXCSR2_TXD_SIZE, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].desc_size); - rt2x00_set_field32(®, TXCSR2_NUM_TXD, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA1].stats.limit); - rt2x00_set_field32(®, TXCSR2_NUM_ATIM, - rt2x00dev->bcn[1].stats.limit); - rt2x00_set_field32(®, TXCSR2_NUM_PRIO, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].stats.limit); - rt2x00pci_register_write(rt2x00dev, TXCSR2, reg); - - rt2x00pci_register_read(rt2x00dev, TXCSR3, ®); + rt2x00mmio_register_read(rt2x00dev, TXCSR2, ®); + rt2x00_set_field32(®, TXCSR2_TXD_SIZE, rt2x00dev->tx[0].desc_size); + rt2x00_set_field32(®, TXCSR2_NUM_TXD, rt2x00dev->tx[1].limit); + rt2x00_set_field32(®, TXCSR2_NUM_ATIM, rt2x00dev->atim->limit); + rt2x00_set_field32(®, TXCSR2_NUM_PRIO, rt2x00dev->tx[0].limit); + rt2x00mmio_register_write(rt2x00dev, TXCSR2, reg); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR3, ®); rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA1].data_dma); - rt2x00pci_register_write(rt2x00dev, TXCSR3, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR3, reg); - rt2x00pci_register_read(rt2x00dev, TXCSR5, ®); + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR5, ®); rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].data_dma); - rt2x00pci_register_write(rt2x00dev, TXCSR5, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR5, reg); - rt2x00pci_register_read(rt2x00dev, TXCSR4, ®); + entry_priv = rt2x00dev->atim->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR4, ®); rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, - rt2x00dev->bcn[1].data_dma); - rt2x00pci_register_write(rt2x00dev, TXCSR4, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR4, reg); - rt2x00pci_register_read(rt2x00dev, TXCSR6, ®); + entry_priv = rt2x00dev->bcn->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, TXCSR6, ®); rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, - rt2x00dev->bcn[0].data_dma); - rt2x00pci_register_write(rt2x00dev, TXCSR6, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TXCSR6, reg); - rt2x00pci_register_read(rt2x00dev, RXCSR1, ®); + rt2x00mmio_register_read(rt2x00dev, RXCSR1, ®); rt2x00_set_field32(®, RXCSR1_RXD_SIZE, rt2x00dev->rx->desc_size); - rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->stats.limit); - rt2x00pci_register_write(rt2x00dev, RXCSR1, reg); + rt2x00_set_field32(®, RXCSR1_NUM_RXD, rt2x00dev->rx->limit); + rt2x00mmio_register_write(rt2x00dev, RXCSR1, reg); - rt2x00pci_register_read(rt2x00dev, RXCSR2, ®); + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, RXCSR2, ®); rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, - rt2x00dev->rx->data_dma); - rt2x00pci_register_write(rt2x00dev, RXCSR2, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RXCSR2, reg); return 0; } @@ -769,32 +904,43 @@ static int rt2500pci_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt2x00pci_register_write(rt2x00dev, PSCSR0, 0x00020002); - rt2x00pci_register_write(rt2x00dev, PSCSR1, 0x00000002); - rt2x00pci_register_write(rt2x00dev, PSCSR2, 0x00020002); - rt2x00pci_register_write(rt2x00dev, PSCSR3, 0x00000002); + rt2x00mmio_register_write(rt2x00dev, PSCSR0, 0x00020002); + rt2x00mmio_register_write(rt2x00dev, PSCSR1, 0x00000002); + rt2x00mmio_register_write(rt2x00dev, PSCSR2, 0x00020002); + rt2x00mmio_register_write(rt2x00dev, PSCSR3, 0x00000002); - rt2x00pci_register_read(rt2x00dev, TIMECSR, ®); + rt2x00mmio_register_read(rt2x00dev, TIMECSR, ®); rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); - rt2x00pci_register_write(rt2x00dev, TIMECSR, reg); + rt2x00mmio_register_write(rt2x00dev, TIMECSR, reg); - rt2x00pci_register_read(rt2x00dev, CSR9, ®); + rt2x00mmio_register_read(rt2x00dev, CSR9, ®); rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, rt2x00dev->rx->data_size / 128); - rt2x00pci_register_write(rt2x00dev, CSR9, reg); + rt2x00mmio_register_write(rt2x00dev, CSR9, reg); /* * Always use CWmin and CWmax set in descriptor. */ - rt2x00pci_register_read(rt2x00dev, CSR11, ®); + rt2x00mmio_register_read(rt2x00dev, CSR11, ®); rt2x00_set_field32(®, CSR11_CW_SELECT, 0); - rt2x00pci_register_write(rt2x00dev, CSR11, reg); + rt2x00mmio_register_write(rt2x00dev, CSR11, reg); + + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); + rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); + rt2x00_set_field32(®, CSR14_TBCN, 0); + rt2x00_set_field32(®, CSR14_TCFP, 0); + rt2x00_set_field32(®, CSR14_TATIMW, 0); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00_set_field32(®, CSR14_CFP_COUNT_PRELOAD, 0); + rt2x00_set_field32(®, CSR14_TBCM_PRELOAD, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); - rt2x00pci_register_write(rt2x00dev, CNT3, 0); + rt2x00mmio_register_write(rt2x00dev, CNT3, 0); - rt2x00pci_register_read(rt2x00dev, TXCSR8, ®); + rt2x00mmio_register_read(rt2x00dev, TXCSR8, ®); rt2x00_set_field32(®, TXCSR8_BBP_ID0, 10); rt2x00_set_field32(®, TXCSR8_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXCSR8_BBP_ID1, 11); @@ -803,30 +949,30 @@ static int rt2500pci_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, TXCSR8_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXCSR8_BBP_ID3, 12); rt2x00_set_field32(®, TXCSR8_BBP_ID3_VALID, 1); - rt2x00pci_register_write(rt2x00dev, TXCSR8, reg); + rt2x00mmio_register_write(rt2x00dev, TXCSR8, reg); - rt2x00pci_register_read(rt2x00dev, ARTCSR0, ®); + rt2x00mmio_register_read(rt2x00dev, ARTCSR0, ®); rt2x00_set_field32(®, ARTCSR0_ACK_CTS_1MBS, 112); rt2x00_set_field32(®, ARTCSR0_ACK_CTS_2MBS, 56); rt2x00_set_field32(®, ARTCSR0_ACK_CTS_5_5MBS, 20); rt2x00_set_field32(®, ARTCSR0_ACK_CTS_11MBS, 10); - rt2x00pci_register_write(rt2x00dev, ARTCSR0, reg); + rt2x00mmio_register_write(rt2x00dev, ARTCSR0, reg); - rt2x00pci_register_read(rt2x00dev, ARTCSR1, ®); + rt2x00mmio_register_read(rt2x00dev, ARTCSR1, ®); rt2x00_set_field32(®, ARTCSR1_ACK_CTS_6MBS, 45); rt2x00_set_field32(®, ARTCSR1_ACK_CTS_9MBS, 37); rt2x00_set_field32(®, ARTCSR1_ACK_CTS_12MBS, 33); rt2x00_set_field32(®, ARTCSR1_ACK_CTS_18MBS, 29); - rt2x00pci_register_write(rt2x00dev, ARTCSR1, reg); + rt2x00mmio_register_write(rt2x00dev, ARTCSR1, reg); - rt2x00pci_register_read(rt2x00dev, ARTCSR2, ®); + rt2x00mmio_register_read(rt2x00dev, ARTCSR2, ®); rt2x00_set_field32(®, ARTCSR2_ACK_CTS_24MBS, 29); rt2x00_set_field32(®, ARTCSR2_ACK_CTS_36MBS, 25); rt2x00_set_field32(®, ARTCSR2_ACK_CTS_48MBS, 25); rt2x00_set_field32(®, ARTCSR2_ACK_CTS_54MBS, 25); - rt2x00pci_register_write(rt2x00dev, ARTCSR2, reg); + rt2x00mmio_register_write(rt2x00dev, ARTCSR2, reg); - rt2x00pci_register_read(rt2x00dev, RXCSR3, ®); + rt2x00mmio_register_read(rt2x00dev, RXCSR3, ®); rt2x00_set_field32(®, RXCSR3_BBP_ID0, 47); /* CCK Signal */ rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID1, 51); /* Rssi */ @@ -835,9 +981,9 @@ static int rt2500pci_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); rt2x00_set_field32(®, RXCSR3_BBP_ID3, 51); /* RSSI */ rt2x00_set_field32(®, RXCSR3_BBP_ID3_VALID, 1); - rt2x00pci_register_write(rt2x00dev, RXCSR3, reg); + rt2x00mmio_register_write(rt2x00dev, RXCSR3, reg); - rt2x00pci_register_read(rt2x00dev, PCICSR, ®); + rt2x00mmio_register_read(rt2x00dev, PCICSR, ®); rt2x00_set_field32(®, PCICSR_BIG_ENDIAN, 0); rt2x00_set_field32(®, PCICSR_RX_TRESHOLD, 0); rt2x00_set_field32(®, PCICSR_TX_TRESHOLD, 3); @@ -845,77 +991,84 @@ static int rt2500pci_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, PCICSR_ENABLE_CLK, 1); rt2x00_set_field32(®, PCICSR_READ_MULTIPLE, 1); rt2x00_set_field32(®, PCICSR_WRITE_INVALID, 1); - rt2x00pci_register_write(rt2x00dev, PCICSR, reg); + rt2x00mmio_register_write(rt2x00dev, PCICSR, reg); - rt2x00pci_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); + rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); - rt2x00pci_register_write(rt2x00dev, GPIOCSR, 0x0000ff00); - rt2x00pci_register_write(rt2x00dev, TESTCSR, 0x000000f0); + rt2x00mmio_register_write(rt2x00dev, GPIOCSR, 0x0000ff00); + rt2x00mmio_register_write(rt2x00dev, TESTCSR, 0x000000f0); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; - rt2x00pci_register_write(rt2x00dev, MACCSR0, 0x00213223); - rt2x00pci_register_write(rt2x00dev, MACCSR1, 0x00235518); + rt2x00mmio_register_write(rt2x00dev, MACCSR0, 0x00213223); + rt2x00mmio_register_write(rt2x00dev, MACCSR1, 0x00235518); - rt2x00pci_register_read(rt2x00dev, MACCSR2, ®); + rt2x00mmio_register_read(rt2x00dev, MACCSR2, ®); rt2x00_set_field32(®, MACCSR2_DELAY, 64); - rt2x00pci_register_write(rt2x00dev, MACCSR2, reg); + rt2x00mmio_register_write(rt2x00dev, MACCSR2, reg); - rt2x00pci_register_read(rt2x00dev, RALINKCSR, ®); + rt2x00mmio_register_read(rt2x00dev, RALINKCSR, ®); rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 26); rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID0, 1); rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 26); rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID1, 1); - rt2x00pci_register_write(rt2x00dev, RALINKCSR, reg); + rt2x00mmio_register_write(rt2x00dev, RALINKCSR, reg); - rt2x00pci_register_write(rt2x00dev, BBPCSR1, 0x82188200); + rt2x00mmio_register_write(rt2x00dev, BBPCSR1, 0x82188200); - rt2x00pci_register_write(rt2x00dev, TXACKCSR0, 0x00000020); + rt2x00mmio_register_write(rt2x00dev, TXACKCSR0, 0x00000020); - rt2x00pci_register_read(rt2x00dev, CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, CSR1, ®); rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, CSR1_BBP_RESET, 0); rt2x00_set_field32(®, CSR1_HOST_READY, 0); - rt2x00pci_register_write(rt2x00dev, CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, CSR1, reg); - rt2x00pci_register_read(rt2x00dev, CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, CSR1, ®); rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, CSR1_HOST_READY, 1); - rt2x00pci_register_write(rt2x00dev, CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, CSR1, reg); /* * We must clear the FCS and FIFO error count. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ - rt2x00pci_register_read(rt2x00dev, CNT0, ®); - rt2x00pci_register_read(rt2x00dev, CNT4, ®); + rt2x00mmio_register_read(rt2x00dev, CNT0, ®); + rt2x00mmio_register_read(rt2x00dev, CNT4, ®); return 0; } -static int rt2500pci_init_bbp(struct rt2x00_dev *rt2x00dev) +static int rt2500pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; - u16 eeprom; - u8 reg_id; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2500pci_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) - goto continue_csr_init; - NOTICE(rt2x00dev, "Waiting for BBP register.\n"); + return 0; udelay(REGISTER_BUSY_DELAY); } - ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); return -EACCES; +} + +static int rt2500pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt2500pci_wait_bbp_ready(rt2x00dev))) + return -EACCES; -continue_csr_init: rt2500pci_bbp_write(rt2x00dev, 3, 0x02); rt2500pci_bbp_write(rt2x00dev, 4, 0x19); rt2500pci_bbp_write(rt2x00dev, 14, 0x1c); @@ -947,19 +1100,15 @@ continue_csr_init: rt2500pci_bbp_write(rt2x00dev, 61, 0x6d); rt2500pci_bbp_write(rt2x00dev, 62, 0x10); - DEBUG(rt2x00dev, "Start initialization from EEPROM...\n"); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - DEBUG(rt2x00dev, "BBP: 0x%02x, value: 0x%02x.\n", - reg_id, value); rt2500pci_bbp_write(rt2x00dev, reg_id, value); } } - DEBUG(rt2x00dev, "...End initialization from EEPROM.\n"); return 0; } @@ -967,43 +1116,46 @@ continue_csr_init: /* * Device state switch handlers. */ -static void rt2500pci_toggle_rx(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg; - - rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DISABLE_RX, - state == STATE_RADIO_RX_OFF); - rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); -} - static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int mask = (state == STATE_RADIO_IRQ_OFF); u32 reg; + unsigned long flags; /* * When interrupts are being enabled, the interrupt registers * should clear the register to assure a clean state. */ if (state == STATE_RADIO_IRQ_ON) { - rt2x00pci_register_read(rt2x00dev, CSR7, ®); - rt2x00pci_register_write(rt2x00dev, CSR7, reg); + rt2x00mmio_register_read(rt2x00dev, CSR7, ®); + rt2x00mmio_register_write(rt2x00dev, CSR7, reg); } /* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ - rt2x00pci_register_read(rt2x00dev, CSR8, ®); + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, mask); rt2x00_set_field32(®, CSR8_TXDONE_TXRING, mask); rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, mask); rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, mask); rt2x00_set_field32(®, CSR8_RXDONE, mask); - rt2x00pci_register_write(rt2x00dev, CSR8, reg); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Ensure that all tasklets are finished. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + } } static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev) @@ -1011,59 +1163,26 @@ static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev) /* * Initialize all registers. */ - if (rt2500pci_init_rings(rt2x00dev) || - rt2500pci_init_registers(rt2x00dev) || - rt2500pci_init_bbp(rt2x00dev)) { - ERROR(rt2x00dev, "Register initialization failed.\n"); + if (unlikely(rt2500pci_init_queues(rt2x00dev) || + rt2500pci_init_registers(rt2x00dev) || + rt2500pci_init_bbp(rt2x00dev))) return -EIO; - } - - /* - * Enable interrupts. - */ - rt2500pci_toggle_irq(rt2x00dev, STATE_RADIO_IRQ_ON); - - /* - * Enable LED - */ - rt2500pci_enable_led(rt2x00dev); return 0; } static void rt2500pci_disable_radio(struct rt2x00_dev *rt2x00dev) { - u32 reg; - - /* - * Disable LED - */ - rt2500pci_disable_led(rt2x00dev); - - rt2x00pci_register_write(rt2x00dev, PWRCSR0, 0); - - /* - * Disable synchronisation. - */ - rt2x00pci_register_write(rt2x00dev, CSR14, 0); - - /* - * Cancel RX and TX. - */ - rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_ABORT, 1); - rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); - /* - * Disable interrupts. + * Disable power */ - rt2500pci_toggle_irq(rt2x00dev, STATE_RADIO_IRQ_OFF); + rt2x00mmio_register_write(rt2x00dev, PWRCSR0, 0); } static int rt2500pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - u32 reg; + u32 reg, reg2; unsigned int i; char put_to_sleep; char bbp_state; @@ -1071,12 +1190,12 @@ static int rt2500pci_set_state(struct rt2x00_dev *rt2x00dev, put_to_sleep = (state != STATE_AWAKE); - rt2x00pci_register_read(rt2x00dev, PWRCSR1, ®); + rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®); rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); - rt2x00pci_register_write(rt2x00dev, PWRCSR1, reg); + rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); /* * Device is not guaranteed to be in the requested state yet. @@ -1084,18 +1203,15 @@ static int rt2500pci_set_state(struct rt2x00_dev *rt2x00dev, * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, PWRCSR1, ®); - bbp_state = rt2x00_get_field32(reg, PWRCSR1_BBP_CURR_STATE); - rf_state = rt2x00_get_field32(reg, PWRCSR1_RF_CURR_STATE); + rt2x00mmio_register_read(rt2x00dev, PWRCSR1, ®2); + bbp_state = rt2x00_get_field32(reg2, PWRCSR1_BBP_CURR_STATE); + rf_state = rt2x00_get_field32(reg2, PWRCSR1_RF_CURR_STATE); if (bbp_state == state && rf_state == state) return 0; + rt2x00mmio_register_write(rt2x00dev, PWRCSR1, reg); msleep(10); } - NOTICE(rt2x00dev, "Device failed to enter state %d, " - "current device state: bbp %d and rf %d.\n", - state, bbp_state, rf_state); - return -EBUSY; } @@ -1111,13 +1227,9 @@ static int rt2500pci_set_device_state(struct rt2x00_dev *rt2x00dev, case STATE_RADIO_OFF: rt2500pci_disable_radio(rt2x00dev); break; - case STATE_RADIO_RX_ON: - case STATE_RADIO_RX_ON_LINK: - rt2500pci_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); - break; - case STATE_RADIO_RX_OFF: - case STATE_RADIO_RX_OFF_LINK: - rt2500pci_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2500pci_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: @@ -1130,134 +1242,175 @@ static int rt2500pci_set_device_state(struct rt2x00_dev *rt2x00dev, break; } + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + return retval; } /* * TX descriptor initialization */ -static void rt2500pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txdata_entry_desc *desc, - struct ieee80211_tx_control *control) +static void rt2500pci_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) { - struct skb_desc *skbdesc = get_skb_desc(skb); - __le32 *txd = skbdesc->desc; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; u32 word; /* * Start writing the descriptor words. */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, skbdesc->skb_dma); + rt2x00_desc_write(txd, 1, word); + rt2x00_desc_read(txd, 2, &word); rt2x00_set_field32(&word, TXD_W2_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(&word, TXD_W2_AIFS, desc->aifs); - rt2x00_set_field32(&word, TXD_W2_CWMIN, desc->cw_min); - rt2x00_set_field32(&word, TXD_W2_CWMAX, desc->cw_max); + rt2x00_set_field32(&word, TXD_W2_AIFS, entry->queue->aifs); + rt2x00_set_field32(&word, TXD_W2_CWMIN, entry->queue->cw_min); + rt2x00_set_field32(&word, TXD_W2_CWMAX, entry->queue->cw_max); rt2x00_desc_write(txd, 2, word); rt2x00_desc_read(txd, 3, &word); - rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, desc->signal); - rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, desc->service); - rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW, desc->length_low); - rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH, desc->length_high); + rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); rt2x00_desc_write(txd, 3, word); rt2x00_desc_read(txd, 10, &word); rt2x00_set_field32(&word, TXD_W10_RTS, - test_bit(ENTRY_TXD_RTS_FRAME, &desc->flags)); + test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)); rt2x00_desc_write(txd, 10, word); + /* + * Writing TXD word 0 must the last to prevent a race condition with + * the device, whereby the device may take hold of the TXD before we + * finished updating it. + */ rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); rt2x00_set_field32(&word, TXD_W0_VALID, 1); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &desc->flags)); + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &desc->flags)); + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &desc->flags)); + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_OFDM, - test_bit(ENTRY_TXD_OFDM_RATE, &desc->flags)); + (txdesc->rate_mode == RATE_MODE_OFDM)); rt2x00_set_field32(&word, TXD_W0_CIPHER_OWNER, 1); - rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, - !!(control->flags & - IEEE80211_TXCTL_LONG_RETRY_LIMIT)); - rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skbdesc->data_len); + test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); rt2x00_desc_write(txd, 0, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; } /* * TX data initialization */ -static void rt2500pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, - unsigned int queue) +static void rt2500pci_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) { + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; u32 reg; - if (queue == IEEE80211_TX_QUEUE_BEACON) { - rt2x00pci_register_read(rt2x00dev, CSR14, ®); - if (!rt2x00_get_field32(reg, CSR14_BEACON_GEN)) { - rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); - rt2x00pci_register_write(rt2x00dev, CSR14, reg); - } - return; + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00mmio_register_read(rt2x00dev, CSR14, ®); + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); + + if (rt2x00queue_map_txskb(entry)) { + rt2x00_err(rt2x00dev, "Fail to map beacon, aborting\n"); + goto out; } - rt2x00pci_register_read(rt2x00dev, TXCSR0, ®); - rt2x00_set_field32(®, TXCSR0_KICK_PRIO, - (queue == IEEE80211_TX_QUEUE_DATA0)); - rt2x00_set_field32(®, TXCSR0_KICK_TX, - (queue == IEEE80211_TX_QUEUE_DATA1)); - rt2x00_set_field32(®, TXCSR0_KICK_ATIM, - (queue == IEEE80211_TX_QUEUE_AFTER_BEACON)); - rt2x00pci_register_write(rt2x00dev, TXCSR0, reg); + /* + * Write the TX descriptor for the beacon. + */ + rt2500pci_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); +out: + /* + * Enable beaconing again. + */ + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, CSR14, reg); } /* * RX control handlers */ -static void rt2500pci_fill_rxdone(struct data_entry *entry, - struct rxdata_entry_desc *desc) +static void rt2500pci_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) { - __le32 *rxd = entry->priv; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; u32 word0; u32 word2; - rt2x00_desc_read(rxd, 0, &word0); - rt2x00_desc_read(rxd, 2, &word2); + rt2x00_desc_read(entry_priv->desc, 0, &word0); + rt2x00_desc_read(entry_priv->desc, 2, &word2); - desc->flags = 0; if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - desc->flags |= RX_FLAG_FAILED_FCS_CRC; + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) - desc->flags |= RX_FLAG_FAILED_PLCP_CRC; - - desc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); - desc->rssi = rt2x00_get_field32(word2, RXD_W2_RSSI) - - entry->ring->rt2x00dev->rssi_offset; - desc->ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); - desc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - desc->my_bss = !!rt2x00_get_field32(word0, RXD_W0_MY_BSS); + rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; + + /* + * Obtain the status about this packet. + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 100kbit/s. + */ + rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); + rxdesc->rssi = rt2x00_get_field32(word2, RXD_W2_RSSI) - + entry->queue->rt2x00dev->rssi_offset; + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + if (rt2x00_get_field32(word0, RXD_W0_OFDM)) + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + else + rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; } /* * Interrupt functions. */ -static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, const int queue) +static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, + const enum data_queue_qid queue_idx) { - struct data_ring *ring = rt2x00lib_get_ring(rt2x00dev, queue); - struct data_entry *entry; - __le32 *txd; + struct data_queue *queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + struct queue_entry_priv_mmio *entry_priv; + struct queue_entry *entry; + struct txdone_entry_desc txdesc; u32 word; - int tx_status; - int retry; - while (!rt2x00_ring_empty(ring)) { - entry = rt2x00_get_data_entry_done(ring); - txd = entry->priv; - rt2x00_desc_read(txd, 0, &word); + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + entry_priv = entry->priv_data; + rt2x00_desc_read(entry_priv->desc, 0, &word); if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || !rt2x00_get_field32(word, TXD_W0_VALID)) @@ -1266,66 +1419,139 @@ static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, const int queue) /* * Obtain the status about this packet. */ - tx_status = rt2x00_get_field32(word, TXD_W0_RESULT); - retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); + txdesc.flags = 0; + switch (rt2x00_get_field32(word, TXD_W0_RESULT)) { + case 0: /* Success */ + case 1: /* Success with retry */ + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + break; + case 2: /* Failure, excessive retries */ + __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); + /* Don't break, this is a failed frame! */ + default: /* Failure */ + __set_bit(TXDONE_FAILURE, &txdesc.flags); + } + txdesc.retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); - rt2x00pci_txdone(rt2x00dev, entry, tx_status, retry); + rt2x00lib_txdone(entry, &txdesc); } } +static inline void rt2500pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, irq_field, 0); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +static void rt2500pci_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + u32 reg; + + /* + * Handle all tx queues. + */ + rt2500pci_txdone(rt2x00dev, QID_ATIM); + rt2500pci_txdone(rt2x00dev, QID_AC_VO); + rt2500pci_txdone(rt2x00dev, QID_AC_VI); + + /* + * Enable all TXDONE interrupts again. + */ + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) { + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); + } +} + +static void rt2500pci_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_beacondone(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2500pci_enable_interrupt(rt2x00dev, CSR8_TBCN_EXPIRE); +} + +static void rt2500pci_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2500pci_enable_interrupt(rt2x00dev, CSR8_RXDONE); +} + static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) { struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg; + u32 reg, mask; /* * Get the interrupt sources & saved to local variable. * Write register value back to clear pending interrupts. */ - rt2x00pci_register_read(rt2x00dev, CSR7, ®); - rt2x00pci_register_write(rt2x00dev, CSR7, reg); + rt2x00mmio_register_read(rt2x00dev, CSR7, ®); + rt2x00mmio_register_write(rt2x00dev, CSR7, reg); if (!reg) return IRQ_NONE; - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return IRQ_HANDLED; - /* - * Handle interrupts, walk through all bits - * and run the tasks, the bits are checked in order of - * priority. - */ + mask = reg; /* - * 1 - Beacon timer expired interrupt. + * Schedule tasklets for interrupt handling. */ if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) - rt2x00lib_beacondone(rt2x00dev); + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); - /* - * 2 - Rx ring done interrupt. - */ if (rt2x00_get_field32(reg, CSR7_RXDONE)) - rt2x00pci_rxdone(rt2x00dev); + tasklet_schedule(&rt2x00dev->rxdone_tasklet); - /* - * 3 - Atim ring transmit done interrupt. - */ - if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING)) - rt2500pci_txdone(rt2x00dev, IEEE80211_TX_QUEUE_AFTER_BEACON); + if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING) || + rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING) || + rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) { + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + /* + * Mask out all txdone interrupts. + */ + rt2x00_set_field32(&mask, CSR8_TXDONE_TXRING, 1); + rt2x00_set_field32(&mask, CSR8_TXDONE_ATIMRING, 1); + rt2x00_set_field32(&mask, CSR8_TXDONE_PRIORING, 1); + } /* - * 4 - Priority ring transmit done interrupt. + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. */ - if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING)) - rt2500pci_txdone(rt2x00dev, IEEE80211_TX_QUEUE_DATA0); + spin_lock(&rt2x00dev->irqmask_lock); - /* - * 5 - Tx ring transmit done interrupt. - */ - if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) - rt2500pci_txdone(rt2x00dev, IEEE80211_TX_QUEUE_DATA1); + rt2x00mmio_register_read(rt2x00dev, CSR8, ®); + reg |= mask; + rt2x00mmio_register_write(rt2x00dev, CSR8, reg); + + spin_unlock(&rt2x00dev->irqmask_lock); return IRQ_HANDLED; } @@ -1340,7 +1566,7 @@ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) u16 word; u8 *mac; - rt2x00pci_register_read(rt2x00dev, CSR21, ®); + rt2x00mmio_register_read(rt2x00dev, CSR21, ®); eeprom.data = rt2x00dev; eeprom.register_read = rt2500pci_eepromregister_read; @@ -1360,11 +1586,8 @@ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { - DECLARE_MAC_BUF(macbuf); - - random_ether_addr(mac); - EEPROM(rt2x00dev, "MAC: %s\n", - print_mac(macbuf, mac)); + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); @@ -1380,7 +1603,7 @@ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); @@ -1389,7 +1612,7 @@ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0); rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word); @@ -1397,7 +1620,8 @@ static int rt2500pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI, DEFAULT_RSSI_OFFSET); rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word); - EEPROM(rt2x00dev, "Calibrate offset: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Calibrate offset: 0x%04x\n", + word); } return 0; @@ -1418,16 +1642,17 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * Identify RF chipset. */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2x00pci_register_read(rt2x00dev, CSR0, ®); - rt2x00_set_chip(rt2x00dev, RT2560, value, reg); - - if (!rt2x00_rf(&rt2x00dev->chip, RF2522) && - !rt2x00_rf(&rt2x00dev->chip, RF2523) && - !rt2x00_rf(&rt2x00dev->chip, RF2524) && - !rt2x00_rf(&rt2x00dev->chip, RF2525) && - !rt2x00_rf(&rt2x00dev->chip, RF2525E) && - !rt2x00_rf(&rt2x00dev->chip, RF5222)) { - ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); + rt2x00mmio_register_read(rt2x00dev, CSR0, ®); + rt2x00_set_chip(rt2x00dev, RT2560, value, + rt2x00_get_field32(reg, CSR0_REVISION)); + + if (!rt2x00_rf(rt2x00dev, RF2522) && + !rt2x00_rf(rt2x00dev, RF2523) && + !rt2x00_rf(rt2x00dev, RF2524) && + !rt2x00_rf(rt2x00dev, RF2525) && + !rt2x00_rf(rt2x00dev, RF2525E) && + !rt2x00_rf(rt2x00dev, RF5222)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); return -ENODEV; } @@ -1442,24 +1667,34 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Store led mode, for correct led behaviour. */ - rt2x00dev->led_mode = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); +#ifdef CONFIG_RT2X00_LIB_LEDS + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); + + rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + if (value == LED_MODE_TXRX_ACTIVITY || + value == LED_MODE_DEFAULT || + value == LED_MODE_ASUS) + rt2500pci_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_ACTIVITY); +#endif /* CONFIG_RT2X00_LIB_LEDS */ /* * Detect if this device has an hardware controlled radio. */ -#ifdef CONFIG_RT2500PCI_RFKILL - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) - __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT2500PCI_RFKILL */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) { + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + /* + * On this device RFKILL initialized during probe does not work. + */ + __set_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags); + } /* * Check if the BBP tuning should be enabled. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); - - if (rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) - __set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags); + if (!rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); /* * Read the RSSI <-> dBm offset information. @@ -1626,67 +1861,87 @@ static const struct rf_channel rf_vals_5222[] = { { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, }; -static void rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* * Initialize all hw fields. */ - rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; - rt2x00dev->hw->extra_tx_headroom = 0; - rt2x00dev->hw->max_signal = MAX_SIGNAL; - rt2x00dev->hw->max_rssi = MAX_RX_SSI; - rt2x00dev->hw->queues = 2; + rt2x00dev->hw->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; - SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_pci(rt2x00dev)->dev); + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* - * Convert tx_power array in eeprom. + * Disable powersaving as default. */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; /* * Initialize hw_mode information. */ - spec->num_modes = 2; - spec->num_rates = 12; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(&rt2x00dev->chip, RF2522)) { + if (rt2x00_rf(rt2x00dev, RF2522)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); spec->channels = rf_vals_bg_2522; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2523)) { + } else if (rt2x00_rf(rt2x00dev, RF2523)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); spec->channels = rf_vals_bg_2523; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2524)) { + } else if (rt2x00_rf(rt2x00dev, RF2524)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); spec->channels = rf_vals_bg_2524; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2525)) { + } else if (rt2x00_rf(rt2x00dev, RF2525)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); spec->channels = rf_vals_bg_2525; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { + } else if (rt2x00_rf(rt2x00dev, RF2525E)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); spec->channels = rf_vals_bg_2525e; - } else if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + } else if (rt2x00_rf(rt2x00dev, RF5222)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5222); spec->channels = rf_vals_5222; - spec->num_modes = 3; } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + } + + if (spec->num_channels > 14) { + for (i = 14; i < spec->num_channels; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = DEFAULT_TXPOWER; + } + } + + return 0; } static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; + u32 reg; /* * Allocate eeprom data. @@ -1700,14 +1955,26 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) return retval; /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2x00mmio_register_read(rt2x00dev, GPIOCSR, ®); + rt2x00_set_field32(®, GPIOCSR_DIR0, 1); + rt2x00mmio_register_write(rt2x00dev, GPIOCSR, reg); + + /* * Initialize hw specifications. */ - rt2500pci_probe_hw_mode(rt2x00dev); + retval = rt2500pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* - * This device requires the beacon ring + * This device requires the atim queue and DMA-mapped skbs. */ - __set_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags); + __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); /* * Set the rssi offset. @@ -1720,111 +1987,27 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static void rt2500pci_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; - - /* - * Mask off any flags we are going to ignore from - * the total_flags field. - */ - *total_flags &= - FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_PROMISC_IN_BSS; - - /* - * Apply some rules to the filters: - * - Some filters imply different filters to be set. - * - Some things we can't filter out at all. - */ - if (mc_count) - *total_flags |= FIF_ALLMULTI; - if (*total_flags & FIF_OTHER_BSS || - *total_flags & FIF_PROMISC_IN_BSS) - *total_flags |= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS; - - /* - * Check if there is any work left for us. - */ - if (rt2x00dev->packet_filter == *total_flags) - return; - rt2x00dev->packet_filter = *total_flags; - - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2x00pci_register_read(rt2x00dev, RXCSR0, ®); - rt2x00_set_field32(®, RXCSR0_DROP_CRC, - !(*total_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, - !(*total_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, - !(*total_flags & FIF_CONTROL)); - rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, RXCSR0_DROP_TODS, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); - rt2x00_set_field32(®, RXCSR0_DROP_MCAST, - !(*total_flags & FIF_ALLMULTI)); - rt2x00_set_field32(®, RXCSR0_DROP_BCAST, 0); - rt2x00pci_register_write(rt2x00dev, RXCSR0, reg); -} - -static int rt2500pci_set_retry_limit(struct ieee80211_hw *hw, - u32 short_retry, u32 long_retry) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; - - rt2x00pci_register_read(rt2x00dev, CSR11, ®); - rt2x00_set_field32(®, CSR11_LONG_RETRY, long_retry); - rt2x00_set_field32(®, CSR11_SHORT_RETRY, short_retry); - rt2x00pci_register_write(rt2x00dev, CSR11, reg); - - return 0; -} - -static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; - rt2x00pci_register_read(rt2x00dev, CSR17, ®); + rt2x00mmio_register_read(rt2x00dev, CSR17, ®); tsf = (u64) rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; - rt2x00pci_register_read(rt2x00dev, CSR16, ®); + rt2x00mmio_register_read(rt2x00dev, CSR16, ®); tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); return tsf; } -static void rt2500pci_reset_tsf(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - rt2x00pci_register_write(rt2x00dev, CSR16, 0); - rt2x00pci_register_write(rt2x00dev, CSR17, 0); -} - static int rt2500pci_tx_last_beacon(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; u32 reg; - rt2x00pci_register_read(rt2x00dev, CSR15, ®); + rt2x00mmio_register_read(rt2x00dev, CSR15, ®); return rt2x00_get_field32(reg, CSR15_BEACON_SENT); } @@ -1835,60 +2018,110 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, - .configure_filter = rt2500pci_configure_filter, + .configure_filter = rt2x00mac_configure_filter, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, - .set_retry_limit = rt2500pci_set_retry_limit, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2x00mac_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt2500pci_get_tsf, - .reset_tsf = rt2500pci_reset_tsf, - .beacon_update = rt2x00pci_beacon_update, .tx_last_beacon = rt2500pci_tx_last_beacon, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { .irq_handler = rt2500pci_interrupt, + .txstatus_tasklet = rt2500pci_txstatus_tasklet, + .tbtt_tasklet = rt2500pci_tbtt_tasklet, + .rxdone_tasklet = rt2500pci_rxdone_tasklet, .probe_hw = rt2500pci_probe_hw, - .initialize = rt2x00pci_initialize, - .uninitialize = rt2x00pci_uninitialize, - .init_rxentry = rt2500pci_init_rxentry, - .init_txentry = rt2500pci_init_txentry, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2500pci_get_entry_state, + .clear_entry = rt2500pci_clear_entry, .set_device_state = rt2500pci_set_device_state, .rfkill_poll = rt2500pci_rfkill_poll, .link_stats = rt2500pci_link_stats, .reset_tuner = rt2500pci_reset_tuner, .link_tuner = rt2500pci_link_tuner, + .start_queue = rt2500pci_start_queue, + .kick_queue = rt2500pci_kick_queue, + .stop_queue = rt2500pci_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, .write_tx_desc = rt2500pci_write_tx_desc, - .write_tx_data = rt2x00pci_write_tx_data, - .kick_tx_queue = rt2500pci_kick_tx_queue, + .write_beacon = rt2500pci_write_beacon, .fill_rxdone = rt2500pci_fill_rxdone, - .config_mac_addr = rt2500pci_config_mac_addr, - .config_bssid = rt2500pci_config_bssid, - .config_type = rt2500pci_config_type, - .config_preamble = rt2500pci_config_preamble, + .config_filter = rt2500pci_config_filter, + .config_intf = rt2500pci_config_intf, + .config_erp = rt2500pci_config_erp, + .config_ant = rt2500pci_config_ant, .config = rt2500pci_config, }; +static void rt2500pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + default: + BUG(); + break; + } +} + static const struct rt2x00_ops rt2500pci_ops = { - .name = KBUILD_MODNAME, - .rxd_size = RXD_DESC_SIZE, - .txd_size = TXD_DESC_SIZE, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .lib = &rt2500pci_rt2x00_ops, - .hw = &rt2500pci_mac80211_ops, + .name = KBUILD_MODNAME, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2500pci_queue_init, + .lib = &rt2500pci_rt2x00_ops, + .hw = &rt2500pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2500pci_rt2x00debug, + .debugfs = &rt2500pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * RT2500pci module information. */ -static struct pci_device_id rt2500pci_device_table[] = { - { PCI_DEVICE(0x1814, 0x0201), PCI_DEVICE_DATA(&rt2500pci_ops) }, +static DEFINE_PCI_DEVICE_TABLE(rt2500pci_device_table) = { + { PCI_DEVICE(0x1814, 0x0201) }, { 0, } }; @@ -1899,24 +2132,19 @@ MODULE_SUPPORTED_DEVICE("Ralink RT2560 PCI & PCMCIA chipset based cards"); MODULE_DEVICE_TABLE(pci, rt2500pci_device_table); MODULE_LICENSE("GPL"); +static int rt2500pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + return rt2x00pci_probe(pci_dev, &rt2500pci_ops); +} + static struct pci_driver rt2500pci_driver = { .name = KBUILD_MODNAME, .id_table = rt2500pci_device_table, - .probe = rt2x00pci_probe, - .remove = __devexit_p(rt2x00pci_remove), + .probe = rt2500pci_probe, + .remove = rt2x00pci_remove, .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; -static int __init rt2500pci_init(void) -{ - return pci_register_driver(&rt2500pci_driver); -} - -static void __exit rt2500pci_exit(void) -{ - pci_unregister_driver(&rt2500pci_driver); -} - -module_init(rt2500pci_init); -module_exit(rt2500pci_exit); +module_pci_driver(rt2500pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h index 92ba0902d10..573e87bcc55 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.h +++ b/drivers/net/wireless/rt2x00/rt2500pci.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -46,10 +44,8 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ -#define MAX_SIGNAL 100 -#define MAX_RX_SSI -1 #define DEFAULT_RSSI_OFFSET 121 /* @@ -59,8 +55,15 @@ #define CSR_REG_SIZE 0x0174 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0200 +#define BBP_BASE 0x0000 #define BBP_SIZE 0x0040 -#define RF_SIZE 0x0014 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 2 /* * Control/Status Registers(CSR). @@ -71,6 +74,7 @@ * CSR0: ASIC revision number. */ #define CSR0 0x0000 +#define CSR0_REVISION FIELD32(0x0000ffff) /* * CSR1: System control register. @@ -748,7 +752,7 @@ #define LEDCSR_LED_DEFAULT FIELD32(0x00100000) /* - * AES control register. + * SECCSR3: AES control register. */ #define SECCSR3 0x00fc @@ -783,16 +787,18 @@ /* * GPIOCSR: GPIO control register. + * GPIOCSR_VALx: GPIO value + * GPIOCSR_DIRx: GPIO direction: 0 = output; 1 = input */ #define GPIOCSR 0x0120 -#define GPIOCSR_BIT0 FIELD32(0x00000001) -#define GPIOCSR_BIT1 FIELD32(0x00000002) -#define GPIOCSR_BIT2 FIELD32(0x00000004) -#define GPIOCSR_BIT3 FIELD32(0x00000008) -#define GPIOCSR_BIT4 FIELD32(0x00000010) -#define GPIOCSR_BIT5 FIELD32(0x00000020) -#define GPIOCSR_BIT6 FIELD32(0x00000040) -#define GPIOCSR_BIT7 FIELD32(0x00000080) +#define GPIOCSR_VAL0 FIELD32(0x00000001) +#define GPIOCSR_VAL1 FIELD32(0x00000002) +#define GPIOCSR_VAL2 FIELD32(0x00000004) +#define GPIOCSR_VAL3 FIELD32(0x00000008) +#define GPIOCSR_VAL4 FIELD32(0x00000010) +#define GPIOCSR_VAL5 FIELD32(0x00000020) +#define GPIOCSR_VAL6 FIELD32(0x00000040) +#define GPIOCSR_VAL7 FIELD32(0x00000080) #define GPIOCSR_DIR0 FIELD32(0x00000100) #define GPIOCSR_DIR1 FIELD32(0x00000200) #define GPIOCSR_DIR2 FIELD32(0x00000400) @@ -892,7 +898,7 @@ #define ARTCSR2_ACK_CTS_54MBS FIELD32(0xff000000) /* - * SECCSR1_RT2509: WEP control register. + * SECCSR1: WEP control register. * KICK_ENCRYPT: Kick encryption engine, self-clear. * ONE_SHOT: 0: ring mode, 1: One shot only mode. * DESC_ADDRESS: Descriptor physical address of frame. @@ -1082,8 +1088,8 @@ /* * DMA descriptor defines. */ -#define TXD_DESC_SIZE ( 11 * sizeof(__le32) ) -#define RXD_DESC_SIZE ( 11 * sizeof(__le32) ) +#define TXD_DESC_SIZE (11 * sizeof(__le32)) +#define RXD_DESC_SIZE (11 * sizeof(__le32)) /* * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. @@ -1213,24 +1219,17 @@ #define RXD_W10_DROP FIELD32(0x00000001) /* - * Macro's for converting txpower from EEPROM to dscape value - * and from dscape value to register value. + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? \ - DEFAULT_TXPOWER : (__txpower); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ - (__txpower)); \ -}) +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT2500PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 638c3d24310..d849d590de2 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -26,9 +24,9 @@ #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/usb.h> #include "rt2x00.h" @@ -36,6 +34,13 @@ #include "rt2500usb.h" /* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +/* * Register access. * All access to the CSR registers will go through the methods * rt2500usb_register_read and rt2500usb_register_write. @@ -47,7 +52,7 @@ * between each attampt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. - * If the usb_cache_mutex is already held then the _lock variants must + * If the csr_mutex is already held then the _lock variants must * be used instead. */ static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, @@ -57,7 +62,7 @@ static inline void rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, __le16 reg; rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, - ®, sizeof(u16), REGISTER_TIMEOUT); + ®, sizeof(reg), REGISTER_TIMEOUT); *value = le16_to_cpu(reg); } @@ -68,7 +73,7 @@ static inline void rt2500usb_register_read_lock(struct rt2x00_dev *rt2x00dev, __le16 reg; rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, - ®, sizeof(u16), REGISTER_TIMEOUT); + ®, sizeof(reg), REGISTER_TIMEOUT); *value = le16_to_cpu(reg); } @@ -76,10 +81,10 @@ static inline void rt2500usb_register_multiread(struct rt2x00_dev *rt2x00dev, const unsigned int offset, void *value, const u16 length) { - int timeout = REGISTER_TIMEOUT * (length / sizeof(u16)); rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, offset, - value, length, timeout); + value, length, + REGISTER_TIMEOUT16(length)); } static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, @@ -89,7 +94,7 @@ static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, __le16 reg = cpu_to_le16(value); rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, - ®, sizeof(u16), REGISTER_TIMEOUT); + ®, sizeof(reg), REGISTER_TIMEOUT); } static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev, @@ -99,62 +104,66 @@ static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev, __le16 reg = cpu_to_le16(value); rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, - ®, sizeof(u16), REGISTER_TIMEOUT); + ®, sizeof(reg), REGISTER_TIMEOUT); } static inline void rt2500usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, const unsigned int offset, void *value, const u16 length) { - int timeout = REGISTER_TIMEOUT * (length / sizeof(u16)); rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, offset, - value, length, timeout); + value, length, + REGISTER_TIMEOUT16(length)); } -static u16 rt2500usb_bbp_check(struct rt2x00_dev *rt2x00dev) +static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + struct rt2x00_field16 field, + u16 *reg) { - u16 reg; unsigned int i; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2500usb_register_read_lock(rt2x00dev, PHY_CSR8, ®); - if (!rt2x00_get_field16(reg, PHY_CSR8_BUSY)) - break; + rt2500usb_register_read_lock(rt2x00dev, offset, reg); + if (!rt2x00_get_field16(*reg, field)) + return 1; udelay(REGISTER_BUSY_DELAY); } - return reg; + rt2x00_err(rt2x00dev, "Indirect register access failed: offset=0x%.08x, value=0x%.08x\n", + offset, *reg); + *reg = ~0; + + return 0; } +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2500usb_regbusy_read((__dev), PHY_CSR8, PHY_CSR8_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2500usb_regbusy_read((__dev), PHY_CSR10, PHY_CSR10_RF_BUSY, (__reg)) + static void rt2500usb_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u16 reg; - mutex_lock(&rt2x00dev->usb_cache_mutex); + mutex_lock(&rt2x00dev->csr_mutex); /* - * Wait until the BBP becomes ready. + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. */ - reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Write failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } - - /* - * Write the data into the BBP. - */ - reg = 0; - rt2x00_set_field16(®, PHY_CSR7_DATA, value); - rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); - rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 0); + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field16(®, PHY_CSR7_DATA, value); + rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); + rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 0); - rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); + rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); + } - mutex_unlock(&rt2x00dev->usb_cache_mutex); + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -162,268 +171,369 @@ static void rt2500usb_bbp_read(struct rt2x00_dev *rt2x00dev, { u16 reg; - mutex_lock(&rt2x00dev->usb_cache_mutex); + mutex_lock(&rt2x00dev->csr_mutex); /* - * Wait until the BBP becomes ready. + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. */ - reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n"); - return; - } - - /* - * Write the request into the BBP. - */ - reg = 0; - rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); - rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 1); + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field16(®, PHY_CSR7_REG_ID, word); + rt2x00_set_field16(®, PHY_CSR7_READ_CONTROL, 1); - rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); + rt2500usb_register_write_lock(rt2x00dev, PHY_CSR7, reg); - /* - * Wait until the BBP becomes ready. - */ - reg = rt2500usb_bbp_check(rt2x00dev); - if (rt2x00_get_field16(reg, PHY_CSR8_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR8 register busy. Read failed.\n"); - *value = 0xff; - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; + if (WAIT_FOR_BBP(rt2x00dev, ®)) + rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, ®); } - rt2500usb_register_read_lock(rt2x00dev, PHY_CSR7, ®); *value = rt2x00_get_field16(reg, PHY_CSR7_DATA); - mutex_unlock(&rt2x00dev->usb_cache_mutex); + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt2500usb_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u16 reg; - unsigned int i; - - if (!word) - return; - - mutex_lock(&rt2x00dev->usb_cache_mutex); - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2500usb_register_read_lock(rt2x00dev, PHY_CSR10, ®); - if (!rt2x00_get_field16(reg, PHY_CSR10_RF_BUSY)) - goto rf_write; - udelay(REGISTER_BUSY_DELAY); - } - mutex_unlock(&rt2x00dev->usb_cache_mutex); - ERROR(rt2x00dev, "PHY_CSR10 register busy. Write failed.\n"); - return; + mutex_lock(&rt2x00dev->csr_mutex); -rf_write: - reg = 0; - rt2x00_set_field16(®, PHY_CSR9_RF_VALUE, value); - rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg); + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field16(®, PHY_CSR9_RF_VALUE, value); + rt2500usb_register_write_lock(rt2x00dev, PHY_CSR9, reg); - reg = 0; - rt2x00_set_field16(®, PHY_CSR10_RF_VALUE, value >> 16); - rt2x00_set_field16(®, PHY_CSR10_RF_NUMBER_OF_BITS, 20); - rt2x00_set_field16(®, PHY_CSR10_RF_IF_SELECT, 0); - rt2x00_set_field16(®, PHY_CSR10_RF_BUSY, 1); + reg = 0; + rt2x00_set_field16(®, PHY_CSR10_RF_VALUE, value >> 16); + rt2x00_set_field16(®, PHY_CSR10_RF_NUMBER_OF_BITS, 20); + rt2x00_set_field16(®, PHY_CSR10_RF_IF_SELECT, 0); + rt2x00_set_field16(®, PHY_CSR10_RF_BUSY, 1); - rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg); - rt2x00_rf_write(rt2x00dev, word, value); + rt2500usb_register_write_lock(rt2x00dev, PHY_CSR10, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } - mutex_unlock(&rt2x00dev->usb_cache_mutex); + mutex_unlock(&rt2x00dev->csr_mutex); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS -#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u16)) ) - -static void rt2500usb_read_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 *data) +static void _rt2500usb_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) { - rt2500usb_register_read(rt2x00dev, CSR_OFFSET(word), (u16 *) data); + rt2500usb_register_read(rt2x00dev, offset, (u16 *)value); } -static void rt2500usb_write_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 data) +static void _rt2500usb_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) { - rt2500usb_register_write(rt2x00dev, CSR_OFFSET(word), data); + rt2500usb_register_write(rt2x00dev, offset, value); } static const struct rt2x00debug rt2500usb_rt2x00debug = { .owner = THIS_MODULE, .csr = { - .read = rt2500usb_read_csr, - .write = rt2500usb_write_csr, + .read = _rt2500usb_register_read, + .write = _rt2500usb_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, .word_size = sizeof(u16), .word_count = CSR_REG_SIZE / sizeof(u16), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt2500usb_bbp_read, .write = rt2500usb_bbp_write, + .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt2500usb_rf_write, + .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -/* - * Configuration handlers. - */ -static void rt2500usb_config_mac_addr(struct rt2x00_dev *rt2x00dev, - __le32 *mac) +static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) { - rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR2, mac, - (3 * sizeof(__le16))); + u16 reg; + + rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); + return rt2x00_get_field16(reg, MAC_CSR19_VAL7); } -static void rt2500usb_config_bssid(struct rt2x00_dev *rt2x00dev, - __le32 *bssid) +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2500usb_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { - rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR5, bssid, - (3 * sizeof(__le16))); + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + u16 reg; + + rt2500usb_register_read(led->rt2x00dev, MAC_CSR20, ®); + + if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) + rt2x00_set_field16(®, MAC_CSR20_LINK, enabled); + else if (led->type == LED_TYPE_ACTIVITY) + rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, enabled); + + rt2500usb_register_write(led->rt2x00dev, MAC_CSR20, reg); } -static void rt2500usb_config_type(struct rt2x00_dev *rt2x00dev, const int type, - const int tsf_sync) +static int rt2500usb_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) { + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); u16 reg; - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0); - - /* - * Enable beacon config - */ - rt2500usb_register_read(rt2x00dev, TXRX_CSR20, ®); - rt2x00_set_field16(®, TXRX_CSR20_OFFSET, - (PREAMBLE + get_duration(IEEE80211_HEADER, 20)) >> 6); - if (type == IEEE80211_IF_TYPE_STA) - rt2x00_set_field16(®, TXRX_CSR20_BCN_EXPECT_WINDOW, 0); - else - rt2x00_set_field16(®, TXRX_CSR20_BCN_EXPECT_WINDOW, 2); - rt2500usb_register_write(rt2x00dev, TXRX_CSR20, reg); + rt2500usb_register_read(led->rt2x00dev, MAC_CSR21, ®); + rt2x00_set_field16(®, MAC_CSR21_ON_PERIOD, *delay_on); + rt2x00_set_field16(®, MAC_CSR21_OFF_PERIOD, *delay_off); + rt2500usb_register_write(led->rt2x00dev, MAC_CSR21, reg); - /* - * Enable synchronisation. - */ - rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); - rt2x00_set_field16(®, TXRX_CSR18_OFFSET, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); + return 0; +} - rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); - rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); - rt2x00_set_field16(®, TXRX_CSR19_TBCN, - (tsf_sync == TSF_SYNC_BEACON)); - rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); - rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, tsf_sync); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); +static void rt2500usb_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2500usb_brightness_set; + led->led_dev.blink_set = rt2500usb_blink_set; + led->flags = LED_INITIALIZED; } +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ -static void rt2500usb_config_preamble(struct rt2x00_dev *rt2x00dev, - const int short_preamble, - const int ack_timeout, - const int ack_consume_time) +/* + * rt2500usb does not differentiate between shared and pairwise + * keys, so we should use the same function for both key types. + */ +static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) { + u32 mask; u16 reg; + enum cipher curr_cipher; + + if (crypto->cmd == SET_KEY) { + /* + * Disallow to set WEP key other than with index 0, + * it is known that not work at least on some hardware. + * SW crypto will be used in that case. + */ + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && + key->keyidx != 0) + return -EOPNOTSUPP; + + /* + * Pairwise key will always be entry 0, but this + * could collide with a shared key on the same + * position... + */ + mask = TXRX_CSR0_KEY_ID.bit_mask; + + rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); + curr_cipher = rt2x00_get_field16(reg, TXRX_CSR0_ALGORITHM); + reg &= mask; + + if (reg && reg == mask) + return -ENOSPC; + + reg = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID); + + key->hw_key_idx += reg ? ffz(reg) : 0; + /* + * Hardware requires that all keys use the same cipher + * (e.g. TKIP-only, AES-only, but not TKIP+AES). + * If this is not the first key, compare the cipher with the + * first one and fall back to SW crypto if not the same. + */ + if (key->hw_key_idx > 0 && crypto->cipher != curr_cipher) + return -EOPNOTSUPP; + + rt2500usb_register_multiwrite(rt2x00dev, KEY_ENTRY(key->hw_key_idx), + crypto->key, sizeof(crypto->key)); + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it demands the data to be provided + * both separately as well as inside the frame. + * We already provided the CONFIG_CRYPTO_COPY_IV to rt2x00lib + * to ensure rt2x00lib will not strip the data from the + * frame after the copy, now we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + } /* - * When in atomic context, reschedule and let rt2x00lib - * call this function again. + * TXRX_CSR0_KEY_ID contains only single-bit fields to indicate + * a particular key is valid. */ - if (in_atomic()) { - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->config_work); - return; - } + rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field16(®, TXRX_CSR0_ALGORITHM, crypto->cipher); + rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); - rt2500usb_register_read(rt2x00dev, TXRX_CSR1, ®); - rt2x00_set_field16(®, TXRX_CSR1_ACK_TIMEOUT, ack_timeout); - rt2500usb_register_write(rt2x00dev, TXRX_CSR1, reg); + mask = rt2x00_get_field16(reg, TXRX_CSR0_KEY_ID); + if (crypto->cmd == SET_KEY) + mask |= 1 << key->hw_key_idx; + else if (crypto->cmd == DISABLE_KEY) + mask &= ~(1 << key->hw_key_idx); + rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, mask); + rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg); - rt2500usb_register_read(rt2x00dev, TXRX_CSR10, ®); - rt2x00_set_field16(®, TXRX_CSR10_AUTORESPOND_PREAMBLE, - !!short_preamble); - rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg); + return 0; } -static void rt2500usb_config_phymode(struct rt2x00_dev *rt2x00dev, - const int phymode, - const int basic_rate_mask) +static void rt2500usb_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) { - rt2500usb_register_write(rt2x00dev, TXRX_CSR11, basic_rate_mask); + u16 reg; - if (phymode == HWMODE_B) { - rt2500usb_register_write(rt2x00dev, MAC_CSR11, 0x000b); - rt2500usb_register_write(rt2x00dev, MAC_CSR12, 0x0040); - } else { - rt2500usb_register_write(rt2x00dev, MAC_CSR11, 0x0005); - rt2500usb_register_write(rt2x00dev, MAC_CSR12, 0x016c); - } + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field16(®, TXRX_CSR2_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_CONTROL, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_NOT_TO_ME, + !(filter_flags & FIF_PROMISC_IN_BSS)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_TODS, + !(filter_flags & FIF_PROMISC_IN_BSS) && + !rt2x00dev->intf_ap_count); + rt2x00_set_field16(®, TXRX_CSR2_DROP_VERSION_ERROR, 1); + rt2x00_set_field16(®, TXRX_CSR2_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field16(®, TXRX_CSR2_DROP_BROADCAST, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); } -static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev, - struct rf_channel *rf, const int txpower) +static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) { - /* - * Set TXpower. - */ - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + unsigned int bcn_preload; + u16 reg; - /* - * For RT2525E we should first set the channel to half band higher. - */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { - static const u32 vals[] = { - 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2, - 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba, - 0x000008ba, 0x000008be, 0x000008b7, 0x00000902, - 0x00000902, 0x00000906 - }; + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable beacon config + */ + bcn_preload = PREAMBLE + GET_DURATION(IEEE80211_HEADER, 20); + rt2500usb_register_read(rt2x00dev, TXRX_CSR20, ®); + rt2x00_set_field16(®, TXRX_CSR20_OFFSET, bcn_preload >> 6); + rt2x00_set_field16(®, TXRX_CSR20_BCN_EXPECT_WINDOW, + 2 * (conf->type != NL80211_IFTYPE_STATION)); + rt2500usb_register_write(rt2x00dev, TXRX_CSR20, reg); - rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]); - if (rf->rf4) - rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); + /* + * Enable synchronisation. + */ + rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); + rt2x00_set_field16(®, TXRX_CSR18_OFFSET, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); + + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, conf->sync); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); } - rt2500usb_rf_write(rt2x00dev, 1, rf->rf1); - rt2500usb_rf_write(rt2x00dev, 2, rf->rf2); - rt2500usb_rf_write(rt2x00dev, 3, rf->rf3); - if (rf->rf4) - rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); + if (flags & CONFIG_UPDATE_MAC) + rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR2, conf->mac, + (3 * sizeof(__le16))); + + if (flags & CONFIG_UPDATE_BSSID) + rt2500usb_register_multiwrite(rt2x00dev, MAC_CSR5, conf->bssid, + (3 * sizeof(__le16))); } -static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev, - const int txpower) +static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) { - u32 rf3; + u16 reg; - rt2x00_rf_read(rt2x00dev, 3, &rf3); - rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - rt2500usb_rf_write(rt2x00dev, 3, rf3); + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rt2500usb_register_read(rt2x00dev, TXRX_CSR10, ®); + rt2x00_set_field16(®, TXRX_CSR10_AUTORESPOND_PREAMBLE, + !!erp->short_preamble); + rt2500usb_register_write(rt2x00dev, TXRX_CSR10, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) + rt2500usb_register_write(rt2x00dev, TXRX_CSR11, + erp->basic_rates); + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); + rt2x00_set_field16(®, TXRX_CSR18_INTERVAL, + erp->beacon_int * 4); + rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time); + rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs); + rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs); + } } -static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) +static void rt2500usb_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) { u8 r2; u8 r14; u16 csr5; u16 csr6; + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + rt2500usb_bbp_read(rt2x00dev, 2, &r2); rt2500usb_bbp_read(rt2x00dev, 14, &r14); rt2500usb_register_read(rt2x00dev, PHY_CSR5, &csr5); @@ -443,14 +553,8 @@ static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev, rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 0); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 0); break; - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ case ANTENNA_B: + default: rt2x00_set_field8(&r2, BBP_R2_TX_ANTENNA, 2); rt2x00_set_field16(&csr5, PHY_CSR5_CCK, 2); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM, 2); @@ -467,14 +571,8 @@ static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev, case ANTENNA_A: rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 0); break; - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ case ANTENNA_B: + default: rt2x00_set_field8(&r14, BBP_R14_RX_ANTENNA, 2); break; } @@ -482,8 +580,7 @@ static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev, /* * RT2525E and RT5222 need to flip TX I/Q */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E) || - rt2x00_rf(&rt2x00dev->chip, RF5222)) { + if (rt2x00_rf(rt2x00dev, RF2525E) || rt2x00_rf(rt2x00dev, RF5222)) { rt2x00_set_field8(&r2, BBP_R2_TX_IQ_FLIP, 1); rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 1); rt2x00_set_field16(&csr6, PHY_CSR6_OFDM_FLIP, 1); @@ -491,7 +588,7 @@ static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev, /* * RT2525E does not need RX I/Q Flip. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) + if (rt2x00_rf(rt2x00dev, RF2525E)) rt2x00_set_field8(&r14, BBP_R14_RX_IQ_FLIP, 0); } else { rt2x00_set_field16(&csr5, PHY_CSR5_CCK_FLIP, 0); @@ -504,66 +601,90 @@ static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev, rt2500usb_register_write(rt2x00dev, PHY_CSR6, csr6); } -static void rt2500usb_config_duration(struct rt2x00_dev *rt2x00dev, - struct rt2x00lib_conf *libconf) +static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev, + struct rf_channel *rf, const int txpower) { - u16 reg; + /* + * Set TXpower. + */ + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + + /* + * For RT2525E we should first set the channel to half band higher. + */ + if (rt2x00_rf(rt2x00dev, RF2525E)) { + static const u32 vals[] = { + 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2, + 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba, + 0x000008ba, 0x000008be, 0x000008b7, 0x00000902, + 0x00000902, 0x00000906 + }; - rt2500usb_register_write(rt2x00dev, MAC_CSR10, libconf->slot_time); + rt2500usb_rf_write(rt2x00dev, 2, vals[rf->channel - 1]); + if (rf->rf4) + rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); + } - rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®); - rt2x00_set_field16(®, TXRX_CSR18_INTERVAL, - libconf->conf->beacon_int * 4); - rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg); + rt2500usb_rf_write(rt2x00dev, 1, rf->rf1); + rt2500usb_rf_write(rt2x00dev, 2, rf->rf2); + rt2500usb_rf_write(rt2x00dev, 3, rf->rf3); + if (rf->rf4) + rt2500usb_rf_write(rt2x00dev, 4, rf->rf4); } -static void rt2500usb_config(struct rt2x00_dev *rt2x00dev, - const unsigned int flags, - struct rt2x00lib_conf *libconf) +static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev, + const int txpower) { - if (flags & CONFIG_UPDATE_PHYMODE) - rt2500usb_config_phymode(rt2x00dev, libconf->phymode, - libconf->basic_rates); - if (flags & CONFIG_UPDATE_CHANNEL) - rt2500usb_config_channel(rt2x00dev, &libconf->rf, - libconf->conf->power_level); - if ((flags & CONFIG_UPDATE_TXPOWER) && !(flags & CONFIG_UPDATE_CHANNEL)) - rt2500usb_config_txpower(rt2x00dev, - libconf->conf->power_level); - if (flags & CONFIG_UPDATE_ANTENNA) - rt2500usb_config_antenna(rt2x00dev, &libconf->ant); - if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT)) - rt2500usb_config_duration(rt2x00dev, libconf); + u32 rf3; + + rt2x00_rf_read(rt2x00dev, 3, &rf3); + rt2x00_set_field32(&rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + rt2500usb_rf_write(rt2x00dev, 3, rf3); } -/* - * LED functions. - */ -static void rt2500usb_enable_led(struct rt2x00_dev *rt2x00dev) +static void rt2500usb_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) { + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; u16 reg; - rt2500usb_register_read(rt2x00dev, MAC_CSR21, ®); - rt2x00_set_field16(®, MAC_CSR21_ON_PERIOD, 70); - rt2x00_set_field16(®, MAC_CSR21_OFF_PERIOD, 30); - rt2500usb_register_write(rt2x00dev, MAC_CSR21, reg); - - rt2500usb_register_read(rt2x00dev, MAC_CSR20, ®); - rt2x00_set_field16(®, MAC_CSR20_LINK, - (rt2x00dev->led_mode != LED_MODE_ASUS)); - rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, - (rt2x00dev->led_mode != LED_MODE_TXRX_ACTIVITY)); - rt2500usb_register_write(rt2x00dev, MAC_CSR20, reg); + if (state == STATE_SLEEP) { + rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); + rt2x00_set_field16(®, MAC_CSR18_DELAY_AFTER_BEACON, + rt2x00dev->beacon_int - 20); + rt2x00_set_field16(®, MAC_CSR18_BEACONS_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 0); + rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); + + rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 1); + rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); + } else { + rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); + rt2x00_set_field16(®, MAC_CSR18_AUTO_WAKE, 0); + rt2500usb_register_write(rt2x00dev, MAC_CSR18, reg); + } + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); } -static void rt2500usb_disable_led(struct rt2x00_dev *rt2x00dev) +static void rt2500usb_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) { - u16 reg; - - rt2500usb_register_read(rt2x00dev, MAC_CSR20, ®); - rt2x00_set_field16(®, MAC_CSR20_LINK, 0); - rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, 0); - rt2500usb_register_write(rt2x00dev, MAC_CSR20, reg); + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt2500usb_config_channel(rt2x00dev, &libconf->rf, + libconf->conf->power_level); + if ((flags & IEEE80211_CONF_CHANGE_POWER) && + !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) + rt2500usb_config_txpower(rt2x00dev, + libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2500usb_config_ps(rt2x00dev, libconf); } /* @@ -587,7 +708,8 @@ static void rt2500usb_link_stats(struct rt2x00_dev *rt2x00dev, qual->false_cca = rt2x00_get_field16(reg, STA_CSR3_FALSE_CCA_ERROR); } -static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev) +static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) { u16 eeprom; u16 value; @@ -608,111 +730,55 @@ static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev) value = rt2x00_get_field16(eeprom, EEPROM_BBPTUNE_VGCUPPER); rt2500usb_bbp_write(rt2x00dev, 17, value); - rt2x00dev->link.vgc_level = value; + qual->vgc_level = value; } -static void rt2500usb_link_tuner(struct rt2x00_dev *rt2x00dev) +/* + * Queue handlers. + */ +static void rt2500usb_start_queue(struct data_queue *queue) { - int rssi = rt2x00_get_link_rssi(&rt2x00dev->link); - u16 bbp_thresh; - u16 vgc_bound; - u16 sens; - u16 r24; - u16 r25; - u16 r61; - u16 r17_sens; - u8 r17; - u8 up_bound; - u8 low_bound; - - /* - * Determine the BBP tuning threshold and correctly - * set BBP 24, 25 and 61. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &bbp_thresh); - bbp_thresh = rt2x00_get_field16(bbp_thresh, EEPROM_BBPTUNE_THRESHOLD); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &r24); - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &r25); - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &r61); - - if ((rssi + bbp_thresh) > 0) { - r24 = rt2x00_get_field16(r24, EEPROM_BBPTUNE_R24_HIGH); - r25 = rt2x00_get_field16(r25, EEPROM_BBPTUNE_R25_HIGH); - r61 = rt2x00_get_field16(r61, EEPROM_BBPTUNE_R61_HIGH); - } else { - r24 = rt2x00_get_field16(r24, EEPROM_BBPTUNE_R24_LOW); - r25 = rt2x00_get_field16(r25, EEPROM_BBPTUNE_R25_LOW); - r61 = rt2x00_get_field16(r61, EEPROM_BBPTUNE_R61_LOW); - } - - rt2500usb_bbp_write(rt2x00dev, 24, r24); - rt2500usb_bbp_write(rt2x00dev, 25, r25); - rt2500usb_bbp_write(rt2x00dev, 61, r61); - - /* - * Read current r17 value, as well as the sensitivity values - * for the r17 register. - */ - rt2500usb_bbp_read(rt2x00dev, 17, &r17); - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &r17_sens); - - /* - * A too low RSSI will cause too much false CCA which will - * then corrupt the R17 tuning. To remidy this the tuning should - * be stopped (While making sure the R17 value will not exceed limits) - */ - if (rssi >= -40) { - if (r17 != 0x60) - rt2500usb_bbp_write(rt2x00dev, 17, 0x60); - return; - } - - /* - * Special big-R17 for short distance - */ - if (rssi >= -58) { - sens = rt2x00_get_field16(r17_sens, EEPROM_BBPTUNE_R17_LOW); - if (r17 != sens) - rt2500usb_bbp_write(rt2x00dev, 17, sens); - return; - } + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u16 reg; - /* - * Special mid-R17 for middle distance - */ - if (rssi >= -74) { - sens = rt2x00_get_field16(r17_sens, EEPROM_BBPTUNE_R17_HIGH); - if (r17 != sens) - rt2500usb_bbp_write(rt2x00dev, 17, sens); - return; + switch (queue->qid) { + case QID_RX: + rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); + break; + case QID_BEACON: + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + break; + default: + break; } +} - /* - * Leave short or middle distance condition, restore r17 - * to the dynamic tuning range. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &vgc_bound); - vgc_bound = rt2x00_get_field16(vgc_bound, EEPROM_BBPTUNE_VGCUPPER); +static void rt2500usb_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u16 reg; - low_bound = 0x32; - if (rssi >= -77) - up_bound = vgc_bound; - else - up_bound = vgc_bound - (-77 - rssi); - - if (up_bound < low_bound) - up_bound = low_bound; - - if (r17 > up_bound) { - rt2500usb_bbp_write(rt2x00dev, 17, up_bound); - rt2x00dev->link.vgc_level = up_bound; - } else if (rt2x00dev->link.qual.false_cca > 512 && r17 < up_bound) { - rt2500usb_bbp_write(rt2x00dev, 17, ++r17); - rt2x00dev->link.vgc_level = r17; - } else if (rt2x00dev->link.qual.false_cca < 100 && r17 > low_bound) { - rt2500usb_bbp_write(rt2x00dev, 17, --r17); - rt2x00dev->link.vgc_level = r17; + switch (queue->qid) { + case QID_RX: + rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, 1); + rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); + break; + case QID_BEACON: + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + break; + default: + break; } } @@ -775,6 +841,13 @@ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(®, TXRX_CSR8_BBP_ID1_VALID, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR8, reg); + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); + rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, 0); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + rt2500usb_register_write(rt2x00dev, TXRX_CSR21, 0xe78f); rt2500usb_register_write(rt2x00dev, MAC_CSR9, 0xff1d); @@ -787,7 +860,7 @@ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(®, MAC_CSR1_HOST_READY, 1); rt2500usb_register_write(rt2x00dev, MAC_CSR1, reg); - if (rt2x00_rev(&rt2x00dev->chip) >= RT2570_VERSION_C) { + if (rt2x00_rev(rt2x00dev) >= RT2570_VERSION_C) { rt2500usb_register_read(rt2x00dev, PHY_CSR2, ®); rt2x00_set_field16(®, PHY_CSR2_LNA, 0); } else { @@ -808,8 +881,9 @@ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2500usb_register_write(rt2x00dev, MAC_CSR8, reg); rt2500usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field16(®, TXRX_CSR0_ALGORITHM, CIPHER_NONE); rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, 0xff); + rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR0, reg); rt2500usb_register_read(rt2x00dev, MAC_CSR18, ®); @@ -827,25 +901,32 @@ static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) return 0; } -static int rt2500usb_init_bbp(struct rt2x00_dev *rt2x00dev) +static int rt2500usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; - u16 eeprom; u8 value; - u8 reg_id; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt2500usb_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) - goto continue_csr_init; - NOTICE(rt2x00dev, "Waiting for BBP register.\n"); + return 0; udelay(REGISTER_BUSY_DELAY); } - ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); return -EACCES; +} + +static int rt2500usb_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 value; + u8 reg_id; + + if (unlikely(rt2500usb_wait_bbp_ready(rt2x00dev))) + return -EACCES; -continue_csr_init: rt2500usb_bbp_write(rt2x00dev, 3, 0x02); rt2500usb_bbp_write(rt2x00dev, 4, 0x19); rt2500usb_bbp_write(rt2x00dev, 14, 0x1c); @@ -878,19 +959,15 @@ continue_csr_init: rt2500usb_bbp_write(rt2x00dev, 62, 0x10); rt2500usb_bbp_write(rt2x00dev, 75, 0xff); - DEBUG(rt2x00dev, "Start initialization from EEPROM...\n"); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - DEBUG(rt2x00dev, "BBP: 0x%02x, value: 0x%02x.\n", - reg_id, value); rt2500usb_bbp_write(rt2x00dev, reg_id, value); } } - DEBUG(rt2x00dev, "...End initialization from EEPROM.\n"); return 0; } @@ -898,43 +975,20 @@ continue_csr_init: /* * Device state switch handlers. */ -static void rt2500usb_toggle_rx(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u16 reg; - - rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); - rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, - state == STATE_RADIO_RX_OFF); - rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); -} - static int rt2500usb_enable_radio(struct rt2x00_dev *rt2x00dev) { /* * Initialize all registers. */ - if (rt2500usb_init_registers(rt2x00dev) || - rt2500usb_init_bbp(rt2x00dev)) { - ERROR(rt2x00dev, "Register initialization failed.\n"); + if (unlikely(rt2500usb_init_registers(rt2x00dev) || + rt2500usb_init_bbp(rt2x00dev))) return -EIO; - } - - /* - * Enable LED - */ - rt2500usb_enable_led(rt2x00dev); return 0; } static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev) { - /* - * Disable LED - */ - rt2500usb_disable_led(rt2x00dev); - rt2500usb_register_write(rt2x00dev, MAC_CSR13, 0x2121); rt2500usb_register_write(rt2x00dev, MAC_CSR14, 0x2121); @@ -981,10 +1035,6 @@ static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev, msleep(30); } - NOTICE(rt2x00dev, "Device failed to enter state %d, " - "current device state: bbp %d and rf %d.\n", - state, bbp_state, rf_state); - return -EBUSY; } @@ -1000,13 +1050,9 @@ static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev, case STATE_RADIO_OFF: rt2500usb_disable_radio(rt2x00dev); break; - case STATE_RADIO_RX_ON: - case STATE_RADIO_RX_ON_LINK: - rt2500usb_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); - break; - case STATE_RADIO_RX_OFF: - case STATE_RADIO_RX_OFF_LINK: - rt2500usb_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + /* No support, but no error either */ break; case STATE_DEEP_SLEEP: case STATE_SLEEP: @@ -1019,137 +1065,247 @@ static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev, break; } + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + return retval; } /* * TX descriptor initialization */ -static void rt2500usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txdata_entry_desc *desc, - struct ieee80211_tx_control *control) +static void rt2500usb_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) { - struct skb_desc *skbdesc = get_skb_desc(skb); - __le32 *txd = skbdesc->desc; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *txd = (__le32 *) entry->skb->data; u32 word; /* * Start writing the descriptor words. */ - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(&word, TXD_W1_AIFS, desc->aifs); - rt2x00_set_field32(&word, TXD_W1_CWMIN, desc->cw_min); - rt2x00_set_field32(&word, TXD_W1_CWMAX, desc->cw_max); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, desc->signal); - rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, desc->service); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, desc->length_low); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, desc->length_high); - rt2x00_desc_write(txd, 2, word); - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, control->retry_limit); + rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, txdesc->retry_limit); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &desc->flags)); + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &desc->flags)); + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &desc->flags)); + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_OFDM, - test_bit(ENTRY_TXD_OFDM_RATE, &desc->flags)); + (txdesc->rate_mode == RATE_MODE_OFDM)); rt2x00_set_field32(&word, TXD_W0_NEW_SEQ, - !!(control->flags & IEEE80211_TXCTL_FIRST_FRAGMENT)); - rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); - rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skbdesc->data_len); - rt2x00_set_field32(&word, TXD_W0_CIPHER, CIPHER_NONE); + test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); + rt2x00_set_field32(&word, TXD_W0_CIPHER, !!txdesc->cipher); + rt2x00_set_field32(&word, TXD_W0_KEY_ID, txdesc->key_idx); rt2x00_desc_write(txd, 0, word); + + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); + rt2x00_set_field32(&word, TXD_W1_AIFS, entry->queue->aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); + rt2x00_desc_write(txd, 2, word); + + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { + _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); + _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); + } + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->flags |= SKBDESC_DESC_IN_SKB; + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; } -static int rt2500usb_get_tx_data_len(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb) +/* + * TX data initialization + */ +static void rt2500usb_beacondone(struct urb *urb); + +static void rt2500usb_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) { + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; + int pipe = usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint); int length; + u16 reg, reg0; /* - * The length _must_ be a multiple of 2, - * but it must _not_ be a multiple of the USB packet size. + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. */ - length = roundup(skb->len, 2); - length += (2 * !(length % rt2x00dev->usb_maxpacket)); + rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - return length; + /* + * Add space for the descriptor in front of the skb. + */ + skb_push(entry->skb, TXD_DESC_SIZE); + memset(entry->skb->data, 0, TXD_DESC_SIZE); + + /* + * Write the TX descriptor for the beacon. + */ + rt2500usb_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); + + /* + * USB devices cannot blindly pass the skb->len as the + * length of the data to usb_fill_bulk_urb. Pass the skb + * to the driver to determine what the length should be. + */ + length = rt2x00dev->ops->lib->get_tx_data_len(entry); + + usb_fill_bulk_urb(bcn_priv->urb, usb_dev, pipe, + entry->skb->data, length, rt2500usb_beacondone, + entry); + + /* + * Second we need to create the guardian byte. + * We only need a single byte, so lets recycle + * the 'flags' field we are not using for beacons. + */ + bcn_priv->guardian_data = 0; + usb_fill_bulk_urb(bcn_priv->guardian_urb, usb_dev, pipe, + &bcn_priv->guardian_data, 1, rt2500usb_beacondone, + entry); + + /* + * Send out the guardian byte. + */ + usb_submit_urb(bcn_priv->guardian_urb, GFP_ATOMIC); + + /* + * Enable beaconing again. + */ + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); + reg0 = reg; + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); + /* + * Beacon generation will fail initially. + * To prevent this we need to change the TXRX_CSR19 + * register several times (reg0 is the same as reg + * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0 + * and 1 in reg). + */ + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); } -/* - * TX data initialization - */ -static void rt2500usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, - unsigned int queue) +static int rt2500usb_get_tx_data_len(struct queue_entry *entry) { - u16 reg; + int length; - if (queue != IEEE80211_TX_QUEUE_BEACON) - return; + /* + * The length _must_ be a multiple of 2, + * but it must _not_ be a multiple of the USB packet size. + */ + length = roundup(entry->skb->len, 2); + length += (2 * !(length % entry->queue->usb_maxpacket)); - rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); - if (!rt2x00_get_field16(reg, TXRX_CSR19_BEACON_GEN)) { - rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); - /* - * Beacon generation will fail initially. - * To prevent this we need to register the TXRX_CSR19 - * register several times. - */ - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - } + return length; } /* * RX control handlers */ -static void rt2500usb_fill_rxdone(struct data_entry *entry, - struct rxdata_entry_desc *desc) +static void rt2500usb_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) { - struct skb_desc *skbdesc = get_skb_desc(entry->skb); - struct urb *urb = entry->priv; - __le32 *rxd = (__le32 *)(entry->skb->data + - (urb->actual_length - entry->ring->desc_size)); + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *rxd = + (__le32 *)(entry->skb->data + + (entry_priv->urb->actual_length - + entry->queue->desc_size)); u32 word0; u32 word1; + /* + * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of + * frame data in rt2x00usb. + */ + memcpy(skbdesc->desc, rxd, skbdesc->desc_len); + rxd = (__le32 *)skbdesc->desc; + + /* + * It is now safe to read the descriptor on all architectures. + */ rt2x00_desc_read(rxd, 0, &word0); rt2x00_desc_read(rxd, 1, &word1); - desc->flags = 0; if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - desc->flags |= RX_FLAG_FAILED_FCS_CRC; + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; if (rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) - desc->flags |= RX_FLAG_FAILED_PLCP_CRC; + rxdesc->flags |= RX_FLAG_FAILED_PLCP_CRC; + + rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER); + if (rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR)) + rxdesc->cipher_status = RX_CRYPTO_FAIL_KEY; + + if (rxdesc->cipher != CIPHER_NONE) { + _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]); + _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]); + rxdesc->dev_flags |= RXDONE_CRYPTO_IV; + + /* ICV is located at the end of frame */ + + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } /* * Obtain the status about this packet. + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 100kbit/s. */ - desc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); - desc->rssi = rt2x00_get_field32(word1, RXD_W1_RSSI) - - entry->ring->rt2x00dev->rssi_offset; - desc->ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); - desc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - desc->my_bss = !!rt2x00_get_field32(word0, RXD_W0_MY_BSS); + rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rxdesc->rssi = + rt2x00_get_field32(word1, RXD_W1_RSSI) - rt2x00dev->rssi_offset; + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + if (rt2x00_get_field32(word0, RXD_W0_OFDM)) + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + else + rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; /* - * Set descriptor and data pointer. + * Adjust the skb memory window to the frame boundaries. */ - skbdesc->desc = entry->skb->data + desc->size; - skbdesc->desc_len = entry->ring->desc_size; - skbdesc->data = entry->skb->data; - skbdesc->data_len = desc->size; + skb_trim(entry->skb, rxdesc->size); } /* @@ -1157,10 +1313,10 @@ static void rt2500usb_fill_rxdone(struct data_entry *entry, */ static void rt2500usb_beacondone(struct urb *urb) { - struct data_entry *entry = (struct data_entry *)urb->context; - struct data_ring *ring = entry->ring; + struct queue_entry *entry = (struct queue_entry *)urb->context; + struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; - if (!test_bit(DEVICE_ENABLED_RADIO, &ring->rt2x00dev->flags)) + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &entry->queue->rt2x00dev->flags)) return; /* @@ -1169,18 +1325,11 @@ static void rt2500usb_beacondone(struct urb *urb) * Otherwise we should free the sk_buffer, the device * should be doing the rest of the work now. */ - if (ring->index == 1) { - rt2x00_ring_index_done_inc(ring); - entry = rt2x00_get_data_entry(ring); - usb_submit_urb(entry->priv, GFP_ATOMIC); - rt2x00_ring_index_inc(ring); - } else if (ring->index_done == 1) { - entry = rt2x00_get_data_entry_done(ring); - if (entry->skb) { - dev_kfree_skb(entry->skb); - entry->skb = NULL; - } - rt2x00_ring_index_done_inc(ring); + if (bcn_priv->guardian_urb == urb) { + usb_submit_urb(bcn_priv->urb, GFP_ATOMIC); + } else if (bcn_priv->urb == urb) { + dev_kfree_skb(entry->skb); + entry->skb = NULL; } } @@ -1191,6 +1340,7 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) { u16 word; u8 *mac; + u8 bbp; rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, EEPROM_SIZE); @@ -1199,10 +1349,8 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { - DECLARE_MAC_BUF(macbuf); - - random_ether_addr(mac); - EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac)); + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); @@ -1218,7 +1366,7 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF2522); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); @@ -1227,7 +1375,7 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_NIC_DYN_BBP_TUNE, 0); rt2x00_set_field16(&word, EEPROM_NIC_CCK_TX_POWER, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &word); @@ -1235,21 +1383,33 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_CALIBRATE_OFFSET_RSSI, DEFAULT_RSSI_OFFSET); rt2x00_eeprom_write(rt2x00dev, EEPROM_CALIBRATE_OFFSET, word); - EEPROM(rt2x00dev, "Calibrate offset: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Calibrate offset: 0x%04x\n", + word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_BBPTUNE_THRESHOLD, 45); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE, word); - EEPROM(rt2x00dev, "BBPtune: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune: 0x%04x\n", word); } + /* + * Switch lower vgc bound to current BBP R17 value, + * lower the value a bit for better quality. + */ + rt2500usb_bbp_read(rt2x00dev, 17, &bbp); + bbp -= 6; + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCUPPER, 0x40); + rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune vgc: 0x%04x\n", word); + } else { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); - EEPROM(rt2x00dev, "BBPtune vgc: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &word); @@ -1257,7 +1417,7 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_LOW, 0x48); rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_HIGH, 0x41); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R17, word); - EEPROM(rt2x00dev, "BBPtune r17: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r17: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &word); @@ -1265,7 +1425,7 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_LOW, 0x40); rt2x00_set_field16(&word, EEPROM_BBPTUNE_R24_HIGH, 0x80); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R24, word); - EEPROM(rt2x00dev, "BBPtune r24: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r24: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &word); @@ -1273,7 +1433,7 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_LOW, 0x40); rt2x00_set_field16(&word, EEPROM_BBPTUNE_R25_HIGH, 0x50); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R25, word); - EEPROM(rt2x00dev, "BBPtune r25: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r25: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &word); @@ -1281,7 +1441,7 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_LOW, 0x60); rt2x00_set_field16(&word, EEPROM_BBPTUNE_R61_HIGH, 0x6d); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R61, word); - EEPROM(rt2x00dev, "BBPtune r61: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "BBPtune r61: 0x%04x\n", word); } return 0; @@ -1305,18 +1465,18 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) rt2500usb_register_read(rt2x00dev, MAC_CSR0, ®); rt2x00_set_chip(rt2x00dev, RT2570, value, reg); - if (!rt2x00_check_rev(&rt2x00dev->chip, 0)) { - ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); + if (((reg & 0xfff0) != 0) || ((reg & 0x0000000f) == 0)) { + rt2x00_err(rt2x00dev, "Invalid RT chipset detected\n"); return -ENODEV; } - if (!rt2x00_rf(&rt2x00dev->chip, RF2522) && - !rt2x00_rf(&rt2x00dev->chip, RF2523) && - !rt2x00_rf(&rt2x00dev->chip, RF2524) && - !rt2x00_rf(&rt2x00dev->chip, RF2525) && - !rt2x00_rf(&rt2x00dev->chip, RF2525E) && - !rt2x00_rf(&rt2x00dev->chip, RF5222)) { - ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); + if (!rt2x00_rf(rt2x00dev, RF2522) && + !rt2x00_rf(rt2x00dev, RF2523) && + !rt2x00_rf(rt2x00dev, RF2524) && + !rt2x00_rf(rt2x00dev, RF2525) && + !rt2x00_rf(rt2x00dev, RF2525E) && + !rt2x00_rf(rt2x00dev, RF5222)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); return -ENODEV; } @@ -1342,15 +1502,22 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Store led mode, for correct led behaviour. */ - rt2x00dev->led_mode = - rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); +#ifdef CONFIG_RT2X00_LIB_LEDS + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_LED_MODE); + + rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + if (value == LED_MODE_TXRX_ACTIVITY || + value == LED_MODE_DEFAULT || + value == LED_MODE_ASUS) + rt2500usb_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_ACTIVITY); +#endif /* CONFIG_RT2X00_LIB_LEDS */ /* - * Check if the BBP tuning should be disabled. + * Detect if this device has an hardware controlled radio. */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) - __set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags); + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Read the RSSI <-> dBm offset information. @@ -1517,70 +1684,94 @@ static const struct rf_channel rf_vals_5222[] = { { 161, 0x00022020, 0x000090be, 0x00000101, 0x00000a07 }, }; -static void rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* * Initialize all hw fields. + * + * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are + * capable of sending the buffered frames out after the DTIM + * transmission using rt2x00lib_beacondone. This will send out + * multicast and broadcast traffic immediately instead of buffering it + * infinitly and thus dropping it after some time. */ rt2x00dev->hw->flags = - IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; - rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; - rt2x00dev->hw->max_signal = MAX_SIGNAL; - rt2x00dev->hw->max_rssi = MAX_RX_SSI; - rt2x00dev->hw->queues = 2; + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; - SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_usb(rt2x00dev)->dev); + /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* - * Convert tx_power array in eeprom. - */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); - - /* * Initialize hw_mode information. */ - spec->num_modes = 2; - spec->num_rates = 12; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(&rt2x00dev->chip, RF2522)) { + if (rt2x00_rf(rt2x00dev, RF2522)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2522); spec->channels = rf_vals_bg_2522; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2523)) { + } else if (rt2x00_rf(rt2x00dev, RF2523)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2523); spec->channels = rf_vals_bg_2523; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2524)) { + } else if (rt2x00_rf(rt2x00dev, RF2524)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2524); spec->channels = rf_vals_bg_2524; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2525)) { + } else if (rt2x00_rf(rt2x00dev, RF2525)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525); spec->channels = rf_vals_bg_2525; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { + } else if (rt2x00_rf(rt2x00dev, RF2525E)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2525e); spec->channels = rf_vals_bg_2525e; - } else if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + } else if (rt2x00_rf(rt2x00dev, RF5222)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5222); spec->channels = rf_vals_5222; - spec->num_modes = 3; } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); + } + + if (spec->num_channels > 14) { + for (i = 14; i < spec->num_channels; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = DEFAULT_TXPOWER; + } + } + + return 0; } static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; + u16 reg; /* * Allocate eeprom data. @@ -1594,175 +1785,36 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) return retval; /* - * Initialize hw specifications. - */ - rt2500usb_probe_hw_mode(rt2x00dev); - - /* - * This device requires the beacon ring + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. */ - __set_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags); + rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); + rt2x00_set_field16(®, MAC_CSR19_DIR0, 0); + rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg); /* - * Set the rssi offset. - */ - rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; - - return 0; -} - -/* - * IEEE80211 stack callback functions. - */ -static void rt2500usb_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u16 reg; - - /* - * Mask off any flags we are going to ignore from - * the total_flags field. - */ - *total_flags &= - FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_PROMISC_IN_BSS; - - /* - * Apply some rules to the filters: - * - Some filters imply different filters to be set. - * - Some things we can't filter out at all. - */ - if (mc_count) - *total_flags |= FIF_ALLMULTI; - if (*total_flags & FIF_OTHER_BSS || - *total_flags & FIF_PROMISC_IN_BSS) - *total_flags |= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS; - - /* - * Check if there is any work left for us. + * Initialize hw specifications. */ - if (rt2x00dev->packet_filter == *total_flags) - return; - rt2x00dev->packet_filter = *total_flags; + retval = rt2500usb_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* - * When in atomic context, reschedule and let rt2x00lib - * call this function again. + * This device requires the atim queue */ - if (in_atomic()) { - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->filter_work); - return; + __set_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags); + if (!modparam_nohwcrypt) { + __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_COPY_IV, &rt2x00dev->cap_flags); } + __set_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2500usb_register_read(rt2x00dev, TXRX_CSR2, ®); - rt2x00_set_field16(®, TXRX_CSR2_DROP_CRC, - !(*total_flags & FIF_FCSFAIL)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_PHYSICAL, - !(*total_flags & FIF_PLCPFAIL)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_CONTROL, - !(*total_flags & FIF_CONTROL)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_NOT_TO_ME, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_TODS, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_VERSION_ERROR, 1); - rt2x00_set_field16(®, TXRX_CSR2_DROP_MULTICAST, - !(*total_flags & FIF_ALLMULTI)); - rt2x00_set_field16(®, TXRX_CSR2_DROP_BROADCAST, 0); - rt2500usb_register_write(rt2x00dev, TXRX_CSR2, reg); -} - -static int rt2500usb_beacon_update(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_tx_control *control) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct usb_device *usb_dev = - interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); - struct skb_desc *desc; - struct data_ring *ring; - struct data_entry *beacon; - struct data_entry *guardian; - int pipe = usb_sndbulkpipe(usb_dev, 1); - int length; - - /* - * Just in case the ieee80211 doesn't set this, - * but we need this queue set for the descriptor - * initialization. - */ - control->queue = IEEE80211_TX_QUEUE_BEACON; - ring = rt2x00lib_get_ring(rt2x00dev, control->queue); - - /* - * Obtain 2 entries, one for the guardian byte, - * the second for the actual beacon. - */ - guardian = rt2x00_get_data_entry(ring); - rt2x00_ring_index_inc(ring); - beacon = rt2x00_get_data_entry(ring); - - /* - * Add the descriptor in front of the skb. - */ - skb_push(skb, ring->desc_size); - memset(skb->data, 0, ring->desc_size); - - /* - * Fill in skb descriptor - */ - desc = get_skb_desc(skb); - desc->desc_len = ring->desc_size; - desc->data_len = skb->len - ring->desc_size; - desc->desc = skb->data; - desc->data = skb->data + ring->desc_size; - desc->ring = ring; - desc->entry = beacon; - - rt2x00lib_write_tx_desc(rt2x00dev, skb, control); - - /* - * USB devices cannot blindly pass the skb->len as the - * length of the data to usb_fill_bulk_urb. Pass the skb - * to the driver to determine what the length should be. - */ - length = rt2500usb_get_tx_data_len(rt2x00dev, skb); - - usb_fill_bulk_urb(beacon->priv, usb_dev, pipe, - skb->data, length, rt2500usb_beacondone, beacon); - - /* - * Second we need to create the guardian byte. - * We only need a single byte, so lets recycle - * the 'flags' field we are not using for beacons. - */ - guardian->flags = 0; - usb_fill_bulk_urb(guardian->priv, usb_dev, pipe, - &guardian->flags, 1, rt2500usb_beacondone, guardian); - - /* - * Send out the guardian byte. - */ - usb_submit_urb(guardian->priv, GFP_ATOMIC); - - /* - * Enable beacon generation. + * Set the rssi offset. */ - rt2500usb_kick_tx_queue(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; return 0; } @@ -1774,47 +1826,100 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, - .configure_filter = rt2500usb_configure_filter, + .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2x00mac_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, - .beacon_update = rt2500usb_beacon_update, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { .probe_hw = rt2500usb_probe_hw, .initialize = rt2x00usb_initialize, .uninitialize = rt2x00usb_uninitialize, - .init_rxentry = rt2x00usb_init_rxentry, - .init_txentry = rt2x00usb_init_txentry, + .clear_entry = rt2x00usb_clear_entry, .set_device_state = rt2500usb_set_device_state, + .rfkill_poll = rt2500usb_rfkill_poll, .link_stats = rt2500usb_link_stats, .reset_tuner = rt2500usb_reset_tuner, - .link_tuner = rt2500usb_link_tuner, + .watchdog = rt2x00usb_watchdog, + .start_queue = rt2500usb_start_queue, + .kick_queue = rt2x00usb_kick_queue, + .stop_queue = rt2500usb_stop_queue, + .flush_queue = rt2x00usb_flush_queue, .write_tx_desc = rt2500usb_write_tx_desc, - .write_tx_data = rt2x00usb_write_tx_data, + .write_beacon = rt2500usb_write_beacon, .get_tx_data_len = rt2500usb_get_tx_data_len, - .kick_tx_queue = rt2500usb_kick_tx_queue, .fill_rxdone = rt2500usb_fill_rxdone, - .config_mac_addr = rt2500usb_config_mac_addr, - .config_bssid = rt2500usb_config_bssid, - .config_type = rt2500usb_config_type, - .config_preamble = rt2500usb_config_preamble, + .config_shared_key = rt2500usb_config_key, + .config_pairwise_key = rt2500usb_config_key, + .config_filter = rt2500usb_config_filter, + .config_intf = rt2500usb_config_intf, + .config_erp = rt2500usb_config_erp, + .config_ant = rt2500usb_config_ant, .config = rt2500usb_config, }; +static void rt2500usb_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb_bcn); + break; + + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + default: + BUG(); + break; + } +} + static const struct rt2x00_ops rt2500usb_ops = { - .name = KBUILD_MODNAME, - .rxd_size = RXD_DESC_SIZE, - .txd_size = TXD_DESC_SIZE, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .lib = &rt2500usb_rt2x00_ops, - .hw = &rt2500usb_mac80211_ops, + .name = KBUILD_MODNAME, + .max_ap_intf = 1, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2500usb_queue_init, + .lib = &rt2500usb_rt2x00_ops, + .hw = &rt2500usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2500usb_rt2x00debug, + .debugfs = &rt2500usb_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; @@ -1823,50 +1928,54 @@ static const struct rt2x00_ops rt2500usb_ops = { */ static struct usb_device_id rt2500usb_device_table[] = { /* ASUS */ - { USB_DEVICE(0x0b05, 0x1706), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x0b05, 0x1707), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0b05, 0x1706) }, + { USB_DEVICE(0x0b05, 0x1707) }, /* Belkin */ - { USB_DEVICE(0x050d, 0x7050), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x050d, 0x7051), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x050d, 0x705a), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050A ver. 2.x */ + { USB_DEVICE(0x050d, 0x7051) }, /* Cisco Systems */ - { USB_DEVICE(0x13b1, 0x000d), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x13b1, 0x0011), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x13b1, 0x001a), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x13b1, 0x000d) }, + { USB_DEVICE(0x13b1, 0x0011) }, + { USB_DEVICE(0x13b1, 0x001a) }, /* Conceptronic */ - { USB_DEVICE(0x14b2, 0x3c02), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x14b2, 0x3c02) }, /* D-LINK */ - { USB_DEVICE(0x2001, 0x3c00), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x2001, 0x3c00) }, /* Gigabyte */ - { USB_DEVICE(0x1044, 0x8001), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x1044, 0x8007), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x1044, 0x8001) }, + { USB_DEVICE(0x1044, 0x8007) }, /* Hercules */ - { USB_DEVICE(0x06f8, 0xe000), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x06f8, 0xe000) }, /* Melco */ - { USB_DEVICE(0x0411, 0x005e), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x0411, 0x0066), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x0411, 0x0067), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x0411, 0x008b), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x0411, 0x0097), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0411, 0x005e) }, + { USB_DEVICE(0x0411, 0x0066) }, + { USB_DEVICE(0x0411, 0x0067) }, + { USB_DEVICE(0x0411, 0x008b) }, + { USB_DEVICE(0x0411, 0x0097) }, /* MSI */ - { USB_DEVICE(0x0db0, 0x6861), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x0db0, 0x6865), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x0db0, 0x6869), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0db0, 0x6861) }, + { USB_DEVICE(0x0db0, 0x6865) }, + { USB_DEVICE(0x0db0, 0x6869) }, /* Ralink */ - { USB_DEVICE(0x148f, 0x1706), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x148f, 0x2570), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x148f, 0x2573), USB_DEVICE_DATA(&rt2500usb_ops) }, - { USB_DEVICE(0x148f, 0x9020), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x148f, 0x1706) }, + { USB_DEVICE(0x148f, 0x2570) }, + { USB_DEVICE(0x148f, 0x9020) }, + /* Sagem */ + { USB_DEVICE(0x079b, 0x004b) }, /* Siemens */ - { USB_DEVICE(0x0681, 0x3c06), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0681, 0x3c06) }, /* SMC */ - { USB_DEVICE(0x0707, 0xee13), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0707, 0xee13) }, /* Spairon */ - { USB_DEVICE(0x114b, 0x0110), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x114b, 0x0110) }, + /* SURECOM */ + { USB_DEVICE(0x0769, 0x11f3) }, /* Trust */ - { USB_DEVICE(0x0eb0, 0x9020), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0eb0, 0x9020) }, + /* VTech */ + { USB_DEVICE(0x0f88, 0x3012) }, /* Zinwell */ - { USB_DEVICE(0x5a57, 0x0260), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x5a57, 0x0260) }, { 0, } }; @@ -1877,24 +1986,21 @@ MODULE_SUPPORTED_DEVICE("Ralink RT2570 USB chipset based cards"); MODULE_DEVICE_TABLE(usb, rt2500usb_device_table); MODULE_LICENSE("GPL"); +static int rt2500usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + return rt2x00usb_probe(usb_intf, &rt2500usb_ops); +} + static struct usb_driver rt2500usb_driver = { .name = KBUILD_MODNAME, .id_table = rt2500usb_device_table, - .probe = rt2x00usb_probe, + .probe = rt2500usb_probe, .disconnect = rt2x00usb_disconnect, .suspend = rt2x00usb_suspend, .resume = rt2x00usb_resume, + .reset_resume = rt2x00usb_resume, + .disable_hub_initiated_lpm = 1, }; -static int __init rt2500usb_init(void) -{ - return usb_register(&rt2500usb_driver); -} - -static void __exit rt2500usb_exit(void) -{ - usb_deregister(&rt2500usb_driver); -} - -module_init(rt2500usb_init); -module_exit(rt2500usb_exit); +module_usb_driver(rt2500usb_driver); diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h index 9e0433722e3..afba0739c3b 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.h +++ b/drivers/net/wireless/rt2x00/rt2500usb.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -46,10 +44,8 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ -#define MAX_SIGNAL 100 -#define MAX_RX_SSI -1 #define DEFAULT_RSSI_OFFSET 120 /* @@ -59,8 +55,15 @@ #define CSR_REG_SIZE 0x0100 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x006a +#define BBP_BASE 0x0000 #define BBP_SIZE 0x0060 -#define RF_SIZE 0x0014 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 2 /* * Control/Status Registers(CSR). @@ -135,7 +138,7 @@ * Misc MAC_CSR registers. * MAC_CSR9: Timer control. * MAC_CSR10: Slot time. - * MAC_CSR11: IFS. + * MAC_CSR11: SIFS. * MAC_CSR12: EIFS. * MAC_CSR13: Power mode0. * MAC_CSR14: Power mode1. @@ -182,8 +185,26 @@ /* * MAC_CSR19: GPIO control register. + * MAC_CSR19_VALx: GPIO value + * MAC_CSR19_DIRx: GPIO direction: 0 = input; 1 = output */ #define MAC_CSR19 0x0426 +#define MAC_CSR19_VAL0 FIELD16(0x0001) +#define MAC_CSR19_VAL1 FIELD16(0x0002) +#define MAC_CSR19_VAL2 FIELD16(0x0004) +#define MAC_CSR19_VAL3 FIELD16(0x0008) +#define MAC_CSR19_VAL4 FIELD16(0x0010) +#define MAC_CSR19_VAL5 FIELD16(0x0020) +#define MAC_CSR19_VAL6 FIELD16(0x0040) +#define MAC_CSR19_VAL7 FIELD16(0x0080) +#define MAC_CSR19_DIR0 FIELD16(0x0100) +#define MAC_CSR19_DIR1 FIELD16(0x0200) +#define MAC_CSR19_DIR2 FIELD16(0x0400) +#define MAC_CSR19_DIR3 FIELD16(0x0800) +#define MAC_CSR19_DIR4 FIELD16(0x1000) +#define MAC_CSR19_DIR5 FIELD16(0x2000) +#define MAC_CSR19_DIR6 FIELD16(0x4000) +#define MAC_CSR19_DIR7 FIELD16(0x8000) /* * MAC_CSR20: LED control register. @@ -206,7 +227,7 @@ #define MAC_CSR21_OFF_PERIOD FIELD16(0xff00) /* - * Collision window control register. + * MAC_CSR22: Collision window control register. */ #define MAC_CSR22 0x042c @@ -293,7 +314,7 @@ #define TXRX_CSR7_BBP_ID1_VALID FIELD16(0x8000) /* - * TXRX_CSR5: OFDM TX BBP ID1. + * TXRX_CSR8: OFDM TX BBP ID1. */ #define TXRX_CSR8 0x0450 #define TXRX_CSR8_BBP_ID0 FIELD16(0x007f) @@ -367,7 +388,14 @@ */ /* - * SEC_CSR0-SEC_CSR7: Shared key 0, word 0-7 + * SEC_CSR0: Shared key 0, word 0 + * SEC_CSR1: Shared key 0, word 1 + * SEC_CSR2: Shared key 0, word 2 + * SEC_CSR3: Shared key 0, word 3 + * SEC_CSR4: Shared key 0, word 4 + * SEC_CSR5: Shared key 0, word 5 + * SEC_CSR6: Shared key 0, word 6 + * SEC_CSR7: Shared key 0, word 7 */ #define SEC_CSR0 0x0480 #define SEC_CSR1 0x0482 @@ -379,7 +407,14 @@ #define SEC_CSR7 0x048e /* - * SEC_CSR8-SEC_CSR15: Shared key 1, word 0-7 + * SEC_CSR8: Shared key 1, word 0 + * SEC_CSR9: Shared key 1, word 1 + * SEC_CSR10: Shared key 1, word 2 + * SEC_CSR11: Shared key 1, word 3 + * SEC_CSR12: Shared key 1, word 4 + * SEC_CSR13: Shared key 1, word 5 + * SEC_CSR14: Shared key 1, word 6 + * SEC_CSR15: Shared key 1, word 7 */ #define SEC_CSR8 0x0490 #define SEC_CSR9 0x0492 @@ -391,7 +426,14 @@ #define SEC_CSR15 0x049e /* - * SEC_CSR16-SEC_CSR23: Shared key 2, word 0-7 + * SEC_CSR16: Shared key 2, word 0 + * SEC_CSR17: Shared key 2, word 1 + * SEC_CSR18: Shared key 2, word 2 + * SEC_CSR19: Shared key 2, word 3 + * SEC_CSR20: Shared key 2, word 4 + * SEC_CSR21: Shared key 2, word 5 + * SEC_CSR22: Shared key 2, word 6 + * SEC_CSR23: Shared key 2, word 7 */ #define SEC_CSR16 0x04a0 #define SEC_CSR17 0x04a2 @@ -403,7 +445,14 @@ #define SEC_CSR23 0x04ae /* - * SEC_CSR24-SEC_CSR31: Shared key 3, word 0-7 + * SEC_CSR24: Shared key 3, word 0 + * SEC_CSR25: Shared key 3, word 1 + * SEC_CSR26: Shared key 3, word 2 + * SEC_CSR27: Shared key 3, word 3 + * SEC_CSR28: Shared key 3, word 4 + * SEC_CSR29: Shared key 3, word 5 + * SEC_CSR30: Shared key 3, word 6 + * SEC_CSR31: Shared key 3, word 7 */ #define SEC_CSR24 0x04b0 #define SEC_CSR25 0x04b2 @@ -414,6 +463,9 @@ #define SEC_CSR30 0x04bc #define SEC_CSR31 0x04be +#define KEY_ENTRY(__idx) \ + ( SEC_CSR0 + ((__idx) * 16) ) + /* * PHY control registers. */ @@ -686,6 +738,7 @@ */ #define EEPROM_BBPTUNE_VGC 0x0034 #define EEPROM_BBPTUNE_VGCUPPER FIELD16(0x00ff) +#define EEPROM_BBPTUNE_VGCLOWER FIELD16(0xff00) /* * EEPROM BBP R17 Tuning. @@ -786,24 +839,17 @@ #define RXD_W3_EIV FIELD32(0xffffffff) /* - * Macro's for converting txpower from EEPROM to dscape value - * and from dscape value to register value. + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? \ - DEFAULT_TXPOWER : (__txpower); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ - (__txpower)); \ -}) +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT2500USB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h new file mode 100644 index 00000000000..a394a9a9591 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -0,0 +1,2974 @@ +/* + Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> + Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2800 + Abstract: Data structures and registers for the rt2800 modules. + Supported chipsets: RT2800E, RT2800ED & RT2800U. + */ + +#ifndef RT2800_H +#define RT2800_H + +/* + * RF chip defines. + * + * RF2820 2.4G 2T3R + * RF2850 2.4G/5G 2T3R + * RF2720 2.4G 1T2R + * RF2750 2.4G/5G 1T2R + * RF3020 2.4G 1T1R + * RF2020 2.4G B/G + * RF3021 2.4G 1T2R + * RF3022 2.4G 2T2R + * RF3052 2.4G/5G 2T2R + * RF2853 2.4G/5G 3T3R + * RF3320 2.4G 1T1R(RT3350/RT3370/RT3390) + * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392) + * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662) + * RF5592 2.4G/5G 2T2R + * RF3070 2.4G 1T1R + * RF5360 2.4G 1T1R + * RF5370 2.4G 1T1R + * RF5390 2.4G 1T1R + */ +#define RF2820 0x0001 +#define RF2850 0x0002 +#define RF2720 0x0003 +#define RF2750 0x0004 +#define RF3020 0x0005 +#define RF2020 0x0006 +#define RF3021 0x0007 +#define RF3022 0x0008 +#define RF3052 0x0009 +#define RF2853 0x000a +#define RF3320 0x000b +#define RF3322 0x000c +#define RF3053 0x000d +#define RF5592 0x000f +#define RF3070 0x3070 +#define RF3290 0x3290 +#define RF5360 0x5360 +#define RF5370 0x5370 +#define RF5372 0x5372 +#define RF5390 0x5390 +#define RF5392 0x5392 + +/* + * Chipset revisions. + */ +#define REV_RT2860C 0x0100 +#define REV_RT2860D 0x0101 +#define REV_RT2872E 0x0200 +#define REV_RT3070E 0x0200 +#define REV_RT3070F 0x0201 +#define REV_RT3071E 0x0211 +#define REV_RT3090E 0x0211 +#define REV_RT3390E 0x0211 +#define REV_RT3593E 0x0211 +#define REV_RT5390F 0x0502 +#define REV_RT5390R 0x1502 +#define REV_RT5592C 0x0221 + +#define DEFAULT_RSSI_OFFSET 120 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x1000 +#define CSR_REG_SIZE 0x0800 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0200 +#define BBP_BASE 0x0000 +#define BBP_SIZE 0x00ff +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 +#define RFCSR_BASE 0x0000 +#define RFCSR_SIZE 0x0040 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 4 + +/* + * Registers. + */ + + +/* + * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number. + */ +#define MAC_CSR0_3290 0x0000 + +/* + * E2PROM_CSR: PCI EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE: 0: 93c46, 1:93c66. + * LOAD_STATUS: 1:loading, 0:done. + */ +#define E2PROM_CSR 0x0004 +#define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000001) +#define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000002) +#define E2PROM_CSR_DATA_IN FIELD32(0x00000004) +#define E2PROM_CSR_DATA_OUT FIELD32(0x00000008) +#define E2PROM_CSR_TYPE FIELD32(0x00000030) +#define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) +#define E2PROM_CSR_RELOAD FIELD32(0x00000080) + +/* + * CMB_CTRL_CFG + */ +#define CMB_CTRL 0x0020 +#define AUX_OPT_BIT0 FIELD32(0x00000001) +#define AUX_OPT_BIT1 FIELD32(0x00000002) +#define AUX_OPT_BIT2 FIELD32(0x00000004) +#define AUX_OPT_BIT3 FIELD32(0x00000008) +#define AUX_OPT_BIT4 FIELD32(0x00000010) +#define AUX_OPT_BIT5 FIELD32(0x00000020) +#define AUX_OPT_BIT6 FIELD32(0x00000040) +#define AUX_OPT_BIT7 FIELD32(0x00000080) +#define AUX_OPT_BIT8 FIELD32(0x00000100) +#define AUX_OPT_BIT9 FIELD32(0x00000200) +#define AUX_OPT_BIT10 FIELD32(0x00000400) +#define AUX_OPT_BIT11 FIELD32(0x00000800) +#define AUX_OPT_BIT12 FIELD32(0x00001000) +#define AUX_OPT_BIT13 FIELD32(0x00002000) +#define AUX_OPT_BIT14 FIELD32(0x00004000) +#define AUX_OPT_BIT15 FIELD32(0x00008000) +#define LDO25_LEVEL FIELD32(0x00030000) +#define LDO25_LARGEA FIELD32(0x00040000) +#define LDO25_FRC_ON FIELD32(0x00080000) +#define CMB_RSV FIELD32(0x00300000) +#define XTAL_RDY FIELD32(0x00400000) +#define PLL_LD FIELD32(0x00800000) +#define LDO_CORE_LEVEL FIELD32(0x0F000000) +#define LDO_BGSEL FIELD32(0x30000000) +#define LDO3_EN FIELD32(0x40000000) +#define LDO0_EN FIELD32(0x80000000) + +/* + * EFUSE_CSR_3290: RT3290 EEPROM + */ +#define EFUSE_CTRL_3290 0x0024 + +/* + * EFUSE_DATA3 of 3290 + */ +#define EFUSE_DATA3_3290 0x0028 + +/* + * EFUSE_DATA2 of 3290 + */ +#define EFUSE_DATA2_3290 0x002c + +/* + * EFUSE_DATA1 of 3290 + */ +#define EFUSE_DATA1_3290 0x0030 + +/* + * EFUSE_DATA0 of 3290 + */ +#define EFUSE_DATA0_3290 0x0034 + +/* + * OSC_CTRL_CFG + * Ring oscillator configuration + */ +#define OSC_CTRL 0x0038 +#define OSC_REF_CYCLE FIELD32(0x00001fff) +#define OSC_RSV FIELD32(0x0000e000) +#define OSC_CAL_CNT FIELD32(0x0fff0000) +#define OSC_CAL_ACK FIELD32(0x10000000) +#define OSC_CLK_32K_VLD FIELD32(0x20000000) +#define OSC_CAL_REQ FIELD32(0x40000000) +#define OSC_ROSC_EN FIELD32(0x80000000) + +/* + * COEX_CFG_0 + */ +#define COEX_CFG0 0x0040 +#define COEX_CFG_ANT FIELD32(0xff000000) +/* + * COEX_CFG_1 + */ +#define COEX_CFG1 0x0044 + +/* + * COEX_CFG_2 + */ +#define COEX_CFG2 0x0048 +#define BT_COEX_CFG1 FIELD32(0xff000000) +#define BT_COEX_CFG0 FIELD32(0x00ff0000) +#define WL_COEX_CFG1 FIELD32(0x0000ff00) +#define WL_COEX_CFG0 FIELD32(0x000000ff) +/* + * PLL_CTRL_CFG + * PLL configuration register + */ +#define PLL_CTRL 0x0050 +#define PLL_RESERVED_INPUT1 FIELD32(0x000000ff) +#define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00) +#define PLL_CONTROL FIELD32(0x00070000) +#define PLL_LPF_R1 FIELD32(0x00080000) +#define PLL_LPF_C1_CTRL FIELD32(0x00300000) +#define PLL_LPF_C2_CTRL FIELD32(0x00c00000) +#define PLL_CP_CURRENT_CTRL FIELD32(0x03000000) +#define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000) +#define PLL_LOCK_CTRL FIELD32(0x70000000) +#define PLL_VBGBK_EN FIELD32(0x80000000) + + +/* + * WLAN_CTRL_CFG + * RT3290 wlan configuration + */ +#define WLAN_FUN_CTRL 0x0080 +#define WLAN_EN FIELD32(0x00000001) +#define WLAN_CLK_EN FIELD32(0x00000002) +#define WLAN_RSV1 FIELD32(0x00000004) +#define WLAN_RESET FIELD32(0x00000008) +#define PCIE_APP0_CLK_REQ FIELD32(0x00000010) +#define FRC_WL_ANT_SET FIELD32(0x00000020) +#define INV_TR_SW0 FIELD32(0x00000040) +#define WLAN_GPIO_IN_BIT0 FIELD32(0x00000100) +#define WLAN_GPIO_IN_BIT1 FIELD32(0x00000200) +#define WLAN_GPIO_IN_BIT2 FIELD32(0x00000400) +#define WLAN_GPIO_IN_BIT3 FIELD32(0x00000800) +#define WLAN_GPIO_IN_BIT4 FIELD32(0x00001000) +#define WLAN_GPIO_IN_BIT5 FIELD32(0x00002000) +#define WLAN_GPIO_IN_BIT6 FIELD32(0x00004000) +#define WLAN_GPIO_IN_BIT7 FIELD32(0x00008000) +#define WLAN_GPIO_IN_BIT_ALL FIELD32(0x0000ff00) +#define WLAN_GPIO_OUT_BIT0 FIELD32(0x00010000) +#define WLAN_GPIO_OUT_BIT1 FIELD32(0x00020000) +#define WLAN_GPIO_OUT_BIT2 FIELD32(0x00040000) +#define WLAN_GPIO_OUT_BIT3 FIELD32(0x00050000) +#define WLAN_GPIO_OUT_BIT4 FIELD32(0x00100000) +#define WLAN_GPIO_OUT_BIT5 FIELD32(0x00200000) +#define WLAN_GPIO_OUT_BIT6 FIELD32(0x00400000) +#define WLAN_GPIO_OUT_BIT7 FIELD32(0x00800000) +#define WLAN_GPIO_OUT_BIT_ALL FIELD32(0x00ff0000) +#define WLAN_GPIO_OUT_OE_BIT0 FIELD32(0x01000000) +#define WLAN_GPIO_OUT_OE_BIT1 FIELD32(0x02000000) +#define WLAN_GPIO_OUT_OE_BIT2 FIELD32(0x04000000) +#define WLAN_GPIO_OUT_OE_BIT3 FIELD32(0x08000000) +#define WLAN_GPIO_OUT_OE_BIT4 FIELD32(0x10000000) +#define WLAN_GPIO_OUT_OE_BIT5 FIELD32(0x20000000) +#define WLAN_GPIO_OUT_OE_BIT6 FIELD32(0x40000000) +#define WLAN_GPIO_OUT_OE_BIT7 FIELD32(0x80000000) +#define WLAN_GPIO_OUT_OE_BIT_ALL FIELD32(0xff000000) + +/* + * AUX_CTRL: Aux/PCI-E related configuration + */ +#define AUX_CTRL 0x10c +#define AUX_CTRL_WAKE_PCIE_EN FIELD32(0x00000002) +#define AUX_CTRL_FORCE_PCIE_CLK FIELD32(0x00000400) + +/* + * OPT_14: Unknown register used by rt3xxx devices. + */ +#define OPT_14_CSR 0x0114 +#define OPT_14_CSR_BIT0 FIELD32(0x00000001) + +/* + * INT_SOURCE_CSR: Interrupt source register. + * Write one to clear corresponding bit. + * TX_FIFO_STATUS: FIFO Statistics is full, sw should read TX_STA_FIFO + */ +#define INT_SOURCE_CSR 0x0200 +#define INT_SOURCE_CSR_RXDELAYINT FIELD32(0x00000001) +#define INT_SOURCE_CSR_TXDELAYINT FIELD32(0x00000002) +#define INT_SOURCE_CSR_RX_DONE FIELD32(0x00000004) +#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00000008) +#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00000010) +#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00000020) +#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00000040) +#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00000080) +#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00000100) +#define INT_SOURCE_CSR_MCU_COMMAND FIELD32(0x00000200) +#define INT_SOURCE_CSR_RXTX_COHERENT FIELD32(0x00000400) +#define INT_SOURCE_CSR_TBTT FIELD32(0x00000800) +#define INT_SOURCE_CSR_PRE_TBTT FIELD32(0x00001000) +#define INT_SOURCE_CSR_TX_FIFO_STATUS FIELD32(0x00002000) +#define INT_SOURCE_CSR_AUTO_WAKEUP FIELD32(0x00004000) +#define INT_SOURCE_CSR_GPTIMER FIELD32(0x00008000) +#define INT_SOURCE_CSR_RX_COHERENT FIELD32(0x00010000) +#define INT_SOURCE_CSR_TX_COHERENT FIELD32(0x00020000) + +/* + * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. + */ +#define INT_MASK_CSR 0x0204 +#define INT_MASK_CSR_RXDELAYINT FIELD32(0x00000001) +#define INT_MASK_CSR_TXDELAYINT FIELD32(0x00000002) +#define INT_MASK_CSR_RX_DONE FIELD32(0x00000004) +#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00000008) +#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00000010) +#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00000020) +#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00000040) +#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00000080) +#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00000100) +#define INT_MASK_CSR_MCU_COMMAND FIELD32(0x00000200) +#define INT_MASK_CSR_RXTX_COHERENT FIELD32(0x00000400) +#define INT_MASK_CSR_TBTT FIELD32(0x00000800) +#define INT_MASK_CSR_PRE_TBTT FIELD32(0x00001000) +#define INT_MASK_CSR_TX_FIFO_STATUS FIELD32(0x00002000) +#define INT_MASK_CSR_AUTO_WAKEUP FIELD32(0x00004000) +#define INT_MASK_CSR_GPTIMER FIELD32(0x00008000) +#define INT_MASK_CSR_RX_COHERENT FIELD32(0x00010000) +#define INT_MASK_CSR_TX_COHERENT FIELD32(0x00020000) + +/* + * WPDMA_GLO_CFG + */ +#define WPDMA_GLO_CFG 0x0208 +#define WPDMA_GLO_CFG_ENABLE_TX_DMA FIELD32(0x00000001) +#define WPDMA_GLO_CFG_TX_DMA_BUSY FIELD32(0x00000002) +#define WPDMA_GLO_CFG_ENABLE_RX_DMA FIELD32(0x00000004) +#define WPDMA_GLO_CFG_RX_DMA_BUSY FIELD32(0x00000008) +#define WPDMA_GLO_CFG_WP_DMA_BURST_SIZE FIELD32(0x00000030) +#define WPDMA_GLO_CFG_TX_WRITEBACK_DONE FIELD32(0x00000040) +#define WPDMA_GLO_CFG_BIG_ENDIAN FIELD32(0x00000080) +#define WPDMA_GLO_CFG_RX_HDR_SCATTER FIELD32(0x0000ff00) +#define WPDMA_GLO_CFG_HDR_SEG_LEN FIELD32(0xffff0000) + +/* + * WPDMA_RST_IDX + */ +#define WPDMA_RST_IDX 0x020c +#define WPDMA_RST_IDX_DTX_IDX0 FIELD32(0x00000001) +#define WPDMA_RST_IDX_DTX_IDX1 FIELD32(0x00000002) +#define WPDMA_RST_IDX_DTX_IDX2 FIELD32(0x00000004) +#define WPDMA_RST_IDX_DTX_IDX3 FIELD32(0x00000008) +#define WPDMA_RST_IDX_DTX_IDX4 FIELD32(0x00000010) +#define WPDMA_RST_IDX_DTX_IDX5 FIELD32(0x00000020) +#define WPDMA_RST_IDX_DRX_IDX0 FIELD32(0x00010000) + +/* + * DELAY_INT_CFG + */ +#define DELAY_INT_CFG 0x0210 +#define DELAY_INT_CFG_RXMAX_PTIME FIELD32(0x000000ff) +#define DELAY_INT_CFG_RXMAX_PINT FIELD32(0x00007f00) +#define DELAY_INT_CFG_RXDLY_INT_EN FIELD32(0x00008000) +#define DELAY_INT_CFG_TXMAX_PTIME FIELD32(0x00ff0000) +#define DELAY_INT_CFG_TXMAX_PINT FIELD32(0x7f000000) +#define DELAY_INT_CFG_TXDLY_INT_EN FIELD32(0x80000000) + +/* + * WMM_AIFSN_CFG: Aifsn for each EDCA AC + * AIFSN0: AC_VO + * AIFSN1: AC_VI + * AIFSN2: AC_BE + * AIFSN3: AC_BK + */ +#define WMM_AIFSN_CFG 0x0214 +#define WMM_AIFSN_CFG_AIFSN0 FIELD32(0x0000000f) +#define WMM_AIFSN_CFG_AIFSN1 FIELD32(0x000000f0) +#define WMM_AIFSN_CFG_AIFSN2 FIELD32(0x00000f00) +#define WMM_AIFSN_CFG_AIFSN3 FIELD32(0x0000f000) + +/* + * WMM_CWMIN_CSR: CWmin for each EDCA AC + * CWMIN0: AC_VO + * CWMIN1: AC_VI + * CWMIN2: AC_BE + * CWMIN3: AC_BK + */ +#define WMM_CWMIN_CFG 0x0218 +#define WMM_CWMIN_CFG_CWMIN0 FIELD32(0x0000000f) +#define WMM_CWMIN_CFG_CWMIN1 FIELD32(0x000000f0) +#define WMM_CWMIN_CFG_CWMIN2 FIELD32(0x00000f00) +#define WMM_CWMIN_CFG_CWMIN3 FIELD32(0x0000f000) + +/* + * WMM_CWMAX_CSR: CWmax for each EDCA AC + * CWMAX0: AC_VO + * CWMAX1: AC_VI + * CWMAX2: AC_BE + * CWMAX3: AC_BK + */ +#define WMM_CWMAX_CFG 0x021c +#define WMM_CWMAX_CFG_CWMAX0 FIELD32(0x0000000f) +#define WMM_CWMAX_CFG_CWMAX1 FIELD32(0x000000f0) +#define WMM_CWMAX_CFG_CWMAX2 FIELD32(0x00000f00) +#define WMM_CWMAX_CFG_CWMAX3 FIELD32(0x0000f000) + +/* + * AC_TXOP0: AC_VO/AC_VI TXOP register + * AC0TXOP: AC_VO in unit of 32us + * AC1TXOP: AC_VI in unit of 32us + */ +#define WMM_TXOP0_CFG 0x0220 +#define WMM_TXOP0_CFG_AC0TXOP FIELD32(0x0000ffff) +#define WMM_TXOP0_CFG_AC1TXOP FIELD32(0xffff0000) + +/* + * AC_TXOP1: AC_BE/AC_BK TXOP register + * AC2TXOP: AC_BE in unit of 32us + * AC3TXOP: AC_BK in unit of 32us + */ +#define WMM_TXOP1_CFG 0x0224 +#define WMM_TXOP1_CFG_AC2TXOP FIELD32(0x0000ffff) +#define WMM_TXOP1_CFG_AC3TXOP FIELD32(0xffff0000) + +/* + * GPIO_CTRL: + * GPIO_CTRL_VALx: GPIO value + * GPIO_CTRL_DIRx: GPIO direction: 0 = output; 1 = input + */ +#define GPIO_CTRL 0x0228 +#define GPIO_CTRL_VAL0 FIELD32(0x00000001) +#define GPIO_CTRL_VAL1 FIELD32(0x00000002) +#define GPIO_CTRL_VAL2 FIELD32(0x00000004) +#define GPIO_CTRL_VAL3 FIELD32(0x00000008) +#define GPIO_CTRL_VAL4 FIELD32(0x00000010) +#define GPIO_CTRL_VAL5 FIELD32(0x00000020) +#define GPIO_CTRL_VAL6 FIELD32(0x00000040) +#define GPIO_CTRL_VAL7 FIELD32(0x00000080) +#define GPIO_CTRL_DIR0 FIELD32(0x00000100) +#define GPIO_CTRL_DIR1 FIELD32(0x00000200) +#define GPIO_CTRL_DIR2 FIELD32(0x00000400) +#define GPIO_CTRL_DIR3 FIELD32(0x00000800) +#define GPIO_CTRL_DIR4 FIELD32(0x00001000) +#define GPIO_CTRL_DIR5 FIELD32(0x00002000) +#define GPIO_CTRL_DIR6 FIELD32(0x00004000) +#define GPIO_CTRL_DIR7 FIELD32(0x00008000) +#define GPIO_CTRL_VAL8 FIELD32(0x00010000) +#define GPIO_CTRL_VAL9 FIELD32(0x00020000) +#define GPIO_CTRL_VAL10 FIELD32(0x00040000) +#define GPIO_CTRL_DIR8 FIELD32(0x01000000) +#define GPIO_CTRL_DIR9 FIELD32(0x02000000) +#define GPIO_CTRL_DIR10 FIELD32(0x04000000) + +/* + * MCU_CMD_CFG + */ +#define MCU_CMD_CFG 0x022c + +/* + * AC_VO register offsets + */ +#define TX_BASE_PTR0 0x0230 +#define TX_MAX_CNT0 0x0234 +#define TX_CTX_IDX0 0x0238 +#define TX_DTX_IDX0 0x023c + +/* + * AC_VI register offsets + */ +#define TX_BASE_PTR1 0x0240 +#define TX_MAX_CNT1 0x0244 +#define TX_CTX_IDX1 0x0248 +#define TX_DTX_IDX1 0x024c + +/* + * AC_BE register offsets + */ +#define TX_BASE_PTR2 0x0250 +#define TX_MAX_CNT2 0x0254 +#define TX_CTX_IDX2 0x0258 +#define TX_DTX_IDX2 0x025c + +/* + * AC_BK register offsets + */ +#define TX_BASE_PTR3 0x0260 +#define TX_MAX_CNT3 0x0264 +#define TX_CTX_IDX3 0x0268 +#define TX_DTX_IDX3 0x026c + +/* + * HCCA register offsets + */ +#define TX_BASE_PTR4 0x0270 +#define TX_MAX_CNT4 0x0274 +#define TX_CTX_IDX4 0x0278 +#define TX_DTX_IDX4 0x027c + +/* + * MGMT register offsets + */ +#define TX_BASE_PTR5 0x0280 +#define TX_MAX_CNT5 0x0284 +#define TX_CTX_IDX5 0x0288 +#define TX_DTX_IDX5 0x028c + +/* + * RX register offsets + */ +#define RX_BASE_PTR 0x0290 +#define RX_MAX_CNT 0x0294 +#define RX_CRX_IDX 0x0298 +#define RX_DRX_IDX 0x029c + +/* + * USB_DMA_CFG + * RX_BULK_AGG_TIMEOUT: Rx Bulk Aggregation TimeOut in unit of 33ns. + * RX_BULK_AGG_LIMIT: Rx Bulk Aggregation Limit in unit of 256 bytes. + * PHY_CLEAR: phy watch dog enable. + * TX_CLEAR: Clear USB DMA TX path. + * TXOP_HALT: Halt TXOP count down when TX buffer is full. + * RX_BULK_AGG_EN: Enable Rx Bulk Aggregation. + * RX_BULK_EN: Enable USB DMA Rx. + * TX_BULK_EN: Enable USB DMA Tx. + * EP_OUT_VALID: OUT endpoint data valid. + * RX_BUSY: USB DMA RX FSM busy. + * TX_BUSY: USB DMA TX FSM busy. + */ +#define USB_DMA_CFG 0x02a0 +#define USB_DMA_CFG_RX_BULK_AGG_TIMEOUT FIELD32(0x000000ff) +#define USB_DMA_CFG_RX_BULK_AGG_LIMIT FIELD32(0x0000ff00) +#define USB_DMA_CFG_PHY_CLEAR FIELD32(0x00010000) +#define USB_DMA_CFG_TX_CLEAR FIELD32(0x00080000) +#define USB_DMA_CFG_TXOP_HALT FIELD32(0x00100000) +#define USB_DMA_CFG_RX_BULK_AGG_EN FIELD32(0x00200000) +#define USB_DMA_CFG_RX_BULK_EN FIELD32(0x00400000) +#define USB_DMA_CFG_TX_BULK_EN FIELD32(0x00800000) +#define USB_DMA_CFG_EP_OUT_VALID FIELD32(0x3f000000) +#define USB_DMA_CFG_RX_BUSY FIELD32(0x40000000) +#define USB_DMA_CFG_TX_BUSY FIELD32(0x80000000) + +/* + * US_CYC_CNT + * BT_MODE_EN: Bluetooth mode enable + * CLOCK CYCLE: Clock cycle count in 1us. + * PCI:0x21, PCIE:0x7d, USB:0x1e + */ +#define US_CYC_CNT 0x02a4 +#define US_CYC_CNT_BT_MODE_EN FIELD32(0x00000100) +#define US_CYC_CNT_CLOCK_CYCLE FIELD32(0x000000ff) + +/* + * PBF_SYS_CTRL + * HOST_RAM_WRITE: enable Host program ram write selection + */ +#define PBF_SYS_CTRL 0x0400 +#define PBF_SYS_CTRL_READY FIELD32(0x00000080) +#define PBF_SYS_CTRL_HOST_RAM_WRITE FIELD32(0x00010000) + +/* + * HOST-MCU shared memory + */ +#define HOST_CMD_CSR 0x0404 +#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x000000ff) + +/* + * PBF registers + * Most are for debug. Driver doesn't touch PBF register. + */ +#define PBF_CFG 0x0408 +#define PBF_MAX_PCNT 0x040c +#define PBF_CTRL 0x0410 +#define PBF_INT_STA 0x0414 +#define PBF_INT_ENA 0x0418 + +/* + * BCN_OFFSET0: + */ +#define BCN_OFFSET0 0x042c +#define BCN_OFFSET0_BCN0 FIELD32(0x000000ff) +#define BCN_OFFSET0_BCN1 FIELD32(0x0000ff00) +#define BCN_OFFSET0_BCN2 FIELD32(0x00ff0000) +#define BCN_OFFSET0_BCN3 FIELD32(0xff000000) + +/* + * BCN_OFFSET1: + */ +#define BCN_OFFSET1 0x0430 +#define BCN_OFFSET1_BCN4 FIELD32(0x000000ff) +#define BCN_OFFSET1_BCN5 FIELD32(0x0000ff00) +#define BCN_OFFSET1_BCN6 FIELD32(0x00ff0000) +#define BCN_OFFSET1_BCN7 FIELD32(0xff000000) + +/* + * TXRXQ_PCNT: PBF register + * PCNT_TX0Q: Page count for TX hardware queue 0 + * PCNT_TX1Q: Page count for TX hardware queue 1 + * PCNT_TX2Q: Page count for TX hardware queue 2 + * PCNT_RX0Q: Page count for RX hardware queue + */ +#define TXRXQ_PCNT 0x0438 +#define TXRXQ_PCNT_TX0Q FIELD32(0x000000ff) +#define TXRXQ_PCNT_TX1Q FIELD32(0x0000ff00) +#define TXRXQ_PCNT_TX2Q FIELD32(0x00ff0000) +#define TXRXQ_PCNT_RX0Q FIELD32(0xff000000) + +/* + * PBF register + * Debug. Driver doesn't touch PBF register. + */ +#define PBF_DBG 0x043c + +/* + * RF registers + */ +#define RF_CSR_CFG 0x0500 +#define RF_CSR_CFG_DATA FIELD32(0x000000ff) +#define RF_CSR_CFG_REGNUM FIELD32(0x00003f00) +#define RF_CSR_CFG_WRITE FIELD32(0x00010000) +#define RF_CSR_CFG_BUSY FIELD32(0x00020000) + +/* + * EFUSE_CSR: RT30x0 EEPROM + */ +#define EFUSE_CTRL 0x0580 +#define EFUSE_CTRL_ADDRESS_IN FIELD32(0x03fe0000) +#define EFUSE_CTRL_MODE FIELD32(0x000000c0) +#define EFUSE_CTRL_KICK FIELD32(0x40000000) +#define EFUSE_CTRL_PRESENT FIELD32(0x80000000) + +/* + * EFUSE_DATA0 + */ +#define EFUSE_DATA0 0x0590 + +/* + * EFUSE_DATA1 + */ +#define EFUSE_DATA1 0x0594 + +/* + * EFUSE_DATA2 + */ +#define EFUSE_DATA2 0x0598 + +/* + * EFUSE_DATA3 + */ +#define EFUSE_DATA3 0x059c + +/* + * LDO_CFG0 + */ +#define LDO_CFG0 0x05d4 +#define LDO_CFG0_DELAY3 FIELD32(0x000000ff) +#define LDO_CFG0_DELAY2 FIELD32(0x0000ff00) +#define LDO_CFG0_DELAY1 FIELD32(0x00ff0000) +#define LDO_CFG0_BGSEL FIELD32(0x03000000) +#define LDO_CFG0_LDO_CORE_VLEVEL FIELD32(0x1c000000) +#define LD0_CFG0_LDO25_LEVEL FIELD32(0x60000000) +#define LDO_CFG0_LDO25_LARGEA FIELD32(0x80000000) + +/* + * GPIO_SWITCH + */ +#define GPIO_SWITCH 0x05dc +#define GPIO_SWITCH_0 FIELD32(0x00000001) +#define GPIO_SWITCH_1 FIELD32(0x00000002) +#define GPIO_SWITCH_2 FIELD32(0x00000004) +#define GPIO_SWITCH_3 FIELD32(0x00000008) +#define GPIO_SWITCH_4 FIELD32(0x00000010) +#define GPIO_SWITCH_5 FIELD32(0x00000020) +#define GPIO_SWITCH_6 FIELD32(0x00000040) +#define GPIO_SWITCH_7 FIELD32(0x00000080) + +/* + * FIXME: where the DEBUG_INDEX name come from? + */ +#define MAC_DEBUG_INDEX 0x05e8 +#define MAC_DEBUG_INDEX_XTAL FIELD32(0x80000000) + +/* + * MAC Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + * ASIC_REV: 0 + * ASIC_VER: 2860 or 2870 + */ +#define MAC_CSR0 0x1000 +#define MAC_CSR0_REVISION FIELD32(0x0000ffff) +#define MAC_CSR0_CHIPSET FIELD32(0xffff0000) + +/* + * MAC_SYS_CTRL: + */ +#define MAC_SYS_CTRL 0x1004 +#define MAC_SYS_CTRL_RESET_CSR FIELD32(0x00000001) +#define MAC_SYS_CTRL_RESET_BBP FIELD32(0x00000002) +#define MAC_SYS_CTRL_ENABLE_TX FIELD32(0x00000004) +#define MAC_SYS_CTRL_ENABLE_RX FIELD32(0x00000008) +#define MAC_SYS_CTRL_CONTINUOUS_TX FIELD32(0x00000010) +#define MAC_SYS_CTRL_LOOPBACK FIELD32(0x00000020) +#define MAC_SYS_CTRL_WLAN_HALT FIELD32(0x00000040) +#define MAC_SYS_CTRL_RX_TIMESTAMP FIELD32(0x00000080) + +/* + * MAC_ADDR_DW0: STA MAC register 0 + */ +#define MAC_ADDR_DW0 0x1008 +#define MAC_ADDR_DW0_BYTE0 FIELD32(0x000000ff) +#define MAC_ADDR_DW0_BYTE1 FIELD32(0x0000ff00) +#define MAC_ADDR_DW0_BYTE2 FIELD32(0x00ff0000) +#define MAC_ADDR_DW0_BYTE3 FIELD32(0xff000000) + +/* + * MAC_ADDR_DW1: STA MAC register 1 + * UNICAST_TO_ME_MASK: + * Used to mask off bits from byte 5 of the MAC address + * to determine the UNICAST_TO_ME bit for RX frames. + * The full mask is complemented by BSS_ID_MASK: + * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK + */ +#define MAC_ADDR_DW1 0x100c +#define MAC_ADDR_DW1_BYTE4 FIELD32(0x000000ff) +#define MAC_ADDR_DW1_BYTE5 FIELD32(0x0000ff00) +#define MAC_ADDR_DW1_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) + +/* + * MAC_BSSID_DW0: BSSID register 0 + */ +#define MAC_BSSID_DW0 0x1010 +#define MAC_BSSID_DW0_BYTE0 FIELD32(0x000000ff) +#define MAC_BSSID_DW0_BYTE1 FIELD32(0x0000ff00) +#define MAC_BSSID_DW0_BYTE2 FIELD32(0x00ff0000) +#define MAC_BSSID_DW0_BYTE3 FIELD32(0xff000000) + +/* + * MAC_BSSID_DW1: BSSID register 1 + * BSS_ID_MASK: + * 0: 1-BSSID mode (BSS index = 0) + * 1: 2-BSSID mode (BSS index: Byte5, bit 0) + * 2: 4-BSSID mode (BSS index: byte5, bit 0 - 1) + * 3: 8-BSSID mode (BSS index: byte5, bit 0 - 2) + * This mask is used to mask off bits 0, 1 and 2 of byte 5 of the + * BSSID. This will make sure that those bits will be ignored + * when determining the MY_BSS of RX frames. + */ +#define MAC_BSSID_DW1 0x1014 +#define MAC_BSSID_DW1_BYTE4 FIELD32(0x000000ff) +#define MAC_BSSID_DW1_BYTE5 FIELD32(0x0000ff00) +#define MAC_BSSID_DW1_BSS_ID_MASK FIELD32(0x00030000) +#define MAC_BSSID_DW1_BSS_BCN_NUM FIELD32(0x001c0000) + +/* + * MAX_LEN_CFG: Maximum frame length register. + * MAX_MPDU: rt2860b max 16k bytes + * MAX_PSDU: Maximum PSDU length + * (power factor) 0:2^13, 1:2^14, 2:2^15, 3:2^16 + */ +#define MAX_LEN_CFG 0x1018 +#define MAX_LEN_CFG_MAX_MPDU FIELD32(0x00000fff) +#define MAX_LEN_CFG_MAX_PSDU FIELD32(0x00003000) +#define MAX_LEN_CFG_MIN_PSDU FIELD32(0x0000c000) +#define MAX_LEN_CFG_MIN_MPDU FIELD32(0x000f0000) + +/* + * BBP_CSR_CFG: BBP serial control register + * VALUE: Register value to program into BBP + * REG_NUM: Selected BBP register + * READ_CONTROL: 0 write BBP, 1 read BBP + * BUSY: ASIC is busy executing BBP commands + * BBP_PAR_DUR: 0 4 MAC clocks, 1 8 MAC clocks + * BBP_RW_MODE: 0 serial, 1 parallel + */ +#define BBP_CSR_CFG 0x101c +#define BBP_CSR_CFG_VALUE FIELD32(0x000000ff) +#define BBP_CSR_CFG_REGNUM FIELD32(0x0000ff00) +#define BBP_CSR_CFG_READ_CONTROL FIELD32(0x00010000) +#define BBP_CSR_CFG_BUSY FIELD32(0x00020000) +#define BBP_CSR_CFG_BBP_PAR_DUR FIELD32(0x00040000) +#define BBP_CSR_CFG_BBP_RW_MODE FIELD32(0x00080000) + +/* + * RF_CSR_CFG0: RF control register + * REGID_AND_VALUE: Register value to program into RF + * BITWIDTH: Selected RF register + * STANDBYMODE: 0 high when standby, 1 low when standby + * SEL: 0 RF_LE0 activate, 1 RF_LE1 activate + * BUSY: ASIC is busy executing RF commands + */ +#define RF_CSR_CFG0 0x1020 +#define RF_CSR_CFG0_REGID_AND_VALUE FIELD32(0x00ffffff) +#define RF_CSR_CFG0_BITWIDTH FIELD32(0x1f000000) +#define RF_CSR_CFG0_REG_VALUE_BW FIELD32(0x1fffffff) +#define RF_CSR_CFG0_STANDBYMODE FIELD32(0x20000000) +#define RF_CSR_CFG0_SEL FIELD32(0x40000000) +#define RF_CSR_CFG0_BUSY FIELD32(0x80000000) + +/* + * RF_CSR_CFG1: RF control register + * REGID_AND_VALUE: Register value to program into RF + * RFGAP: Gap between BB_CONTROL_RF and RF_LE + * 0: 3 system clock cycle (37.5usec) + * 1: 5 system clock cycle (62.5usec) + */ +#define RF_CSR_CFG1 0x1024 +#define RF_CSR_CFG1_REGID_AND_VALUE FIELD32(0x00ffffff) +#define RF_CSR_CFG1_RFGAP FIELD32(0x1f000000) + +/* + * RF_CSR_CFG2: RF control register + * VALUE: Register value to program into RF + */ +#define RF_CSR_CFG2 0x1028 +#define RF_CSR_CFG2_VALUE FIELD32(0x00ffffff) + +/* + * LED_CFG: LED control + * ON_PERIOD: LED active time (ms) during TX (only used for LED mode 1) + * OFF_PERIOD: LED inactive time (ms) during TX (only used for LED mode 1) + * SLOW_BLINK_PERIOD: LED blink interval in seconds (only used for LED mode 2) + * color LED's: + * 0: off + * 1: blinking upon TX2 + * 2: periodic slow blinking + * 3: always on + * LED polarity: + * 0: active low + * 1: active high + */ +#define LED_CFG 0x102c +#define LED_CFG_ON_PERIOD FIELD32(0x000000ff) +#define LED_CFG_OFF_PERIOD FIELD32(0x0000ff00) +#define LED_CFG_SLOW_BLINK_PERIOD FIELD32(0x003f0000) +#define LED_CFG_R_LED_MODE FIELD32(0x03000000) +#define LED_CFG_G_LED_MODE FIELD32(0x0c000000) +#define LED_CFG_Y_LED_MODE FIELD32(0x30000000) +#define LED_CFG_LED_POLAR FIELD32(0x40000000) + +/* + * AMPDU_BA_WINSIZE: Force BlockAck window size + * FORCE_WINSIZE_ENABLE: + * 0: Disable forcing of BlockAck window size + * 1: Enable forcing of BlockAck window size, overwrites values BlockAck + * window size values in the TXWI + * FORCE_WINSIZE: BlockAck window size + */ +#define AMPDU_BA_WINSIZE 0x1040 +#define AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE FIELD32(0x00000020) +#define AMPDU_BA_WINSIZE_FORCE_WINSIZE FIELD32(0x0000001f) + +/* + * XIFS_TIME_CFG: MAC timing + * CCKM_SIFS_TIME: unit 1us. Applied after CCK RX/TX + * OFDM_SIFS_TIME: unit 1us. Applied after OFDM RX/TX + * OFDM_XIFS_TIME: unit 1us. Applied after OFDM RX + * when MAC doesn't reference BBP signal BBRXEND + * EIFS: unit 1us + * BB_RXEND_ENABLE: reference RXEND signal to begin XIFS defer + * + */ +#define XIFS_TIME_CFG 0x1100 +#define XIFS_TIME_CFG_CCKM_SIFS_TIME FIELD32(0x000000ff) +#define XIFS_TIME_CFG_OFDM_SIFS_TIME FIELD32(0x0000ff00) +#define XIFS_TIME_CFG_OFDM_XIFS_TIME FIELD32(0x000f0000) +#define XIFS_TIME_CFG_EIFS FIELD32(0x1ff00000) +#define XIFS_TIME_CFG_BB_RXEND_ENABLE FIELD32(0x20000000) + +/* + * BKOFF_SLOT_CFG: + */ +#define BKOFF_SLOT_CFG 0x1104 +#define BKOFF_SLOT_CFG_SLOT_TIME FIELD32(0x000000ff) +#define BKOFF_SLOT_CFG_CC_DELAY_TIME FIELD32(0x0000ff00) + +/* + * NAV_TIME_CFG: + */ +#define NAV_TIME_CFG 0x1108 +#define NAV_TIME_CFG_SIFS FIELD32(0x000000ff) +#define NAV_TIME_CFG_SLOT_TIME FIELD32(0x0000ff00) +#define NAV_TIME_CFG_EIFS FIELD32(0x01ff0000) +#define NAV_TIME_ZERO_SIFS FIELD32(0x02000000) + +/* + * CH_TIME_CFG: count as channel busy + * EIFS_BUSY: Count EIFS as channel busy + * NAV_BUSY: Count NAS as channel busy + * RX_BUSY: Count RX as channel busy + * TX_BUSY: Count TX as channel busy + * TMR_EN: Enable channel statistics timer + */ +#define CH_TIME_CFG 0x110c +#define CH_TIME_CFG_EIFS_BUSY FIELD32(0x00000010) +#define CH_TIME_CFG_NAV_BUSY FIELD32(0x00000008) +#define CH_TIME_CFG_RX_BUSY FIELD32(0x00000004) +#define CH_TIME_CFG_TX_BUSY FIELD32(0x00000002) +#define CH_TIME_CFG_TMR_EN FIELD32(0x00000001) + +/* + * PBF_LIFE_TIMER: TX/RX MPDU timestamp timer (free run) Unit: 1us + */ +#define PBF_LIFE_TIMER 0x1110 + +/* + * BCN_TIME_CFG: + * BEACON_INTERVAL: in unit of 1/16 TU + * TSF_TICKING: Enable TSF auto counting + * TSF_SYNC: Enable TSF sync, 00: disable, 01: infra mode, 10: ad-hoc mode + * BEACON_GEN: Enable beacon generator + */ +#define BCN_TIME_CFG 0x1114 +#define BCN_TIME_CFG_BEACON_INTERVAL FIELD32(0x0000ffff) +#define BCN_TIME_CFG_TSF_TICKING FIELD32(0x00010000) +#define BCN_TIME_CFG_TSF_SYNC FIELD32(0x00060000) +#define BCN_TIME_CFG_TBTT_ENABLE FIELD32(0x00080000) +#define BCN_TIME_CFG_BEACON_GEN FIELD32(0x00100000) +#define BCN_TIME_CFG_TX_TIME_COMPENSATE FIELD32(0xf0000000) + +/* + * TBTT_SYNC_CFG: + * BCN_AIFSN: Beacon AIFSN after TBTT interrupt in slots + * BCN_CWMIN: Beacon CWMin after TBTT interrupt in slots + */ +#define TBTT_SYNC_CFG 0x1118 +#define TBTT_SYNC_CFG_TBTT_ADJUST FIELD32(0x000000ff) +#define TBTT_SYNC_CFG_BCN_EXP_WIN FIELD32(0x0000ff00) +#define TBTT_SYNC_CFG_BCN_AIFSN FIELD32(0x000f0000) +#define TBTT_SYNC_CFG_BCN_CWMIN FIELD32(0x00f00000) + +/* + * TSF_TIMER_DW0: Local lsb TSF timer, read-only + */ +#define TSF_TIMER_DW0 0x111c +#define TSF_TIMER_DW0_LOW_WORD FIELD32(0xffffffff) + +/* + * TSF_TIMER_DW1: Local msb TSF timer, read-only + */ +#define TSF_TIMER_DW1 0x1120 +#define TSF_TIMER_DW1_HIGH_WORD FIELD32(0xffffffff) + +/* + * TBTT_TIMER: TImer remains till next TBTT, read-only + */ +#define TBTT_TIMER 0x1124 + +/* + * INT_TIMER_CFG: timer configuration + * PRE_TBTT_TIMER: leadtime to tbtt for pretbtt interrupt in units of 1/16 TU + * GP_TIMER: period of general purpose timer in units of 1/16 TU + */ +#define INT_TIMER_CFG 0x1128 +#define INT_TIMER_CFG_PRE_TBTT_TIMER FIELD32(0x0000ffff) +#define INT_TIMER_CFG_GP_TIMER FIELD32(0xffff0000) + +/* + * INT_TIMER_EN: GP-timer and pre-tbtt Int enable + */ +#define INT_TIMER_EN 0x112c +#define INT_TIMER_EN_PRE_TBTT_TIMER FIELD32(0x00000001) +#define INT_TIMER_EN_GP_TIMER FIELD32(0x00000002) + +/* + * CH_IDLE_STA: channel idle time (in us) + */ +#define CH_IDLE_STA 0x1130 + +/* + * CH_BUSY_STA: channel busy time on primary channel (in us) + */ +#define CH_BUSY_STA 0x1134 + +/* + * CH_BUSY_STA_SEC: channel busy time on secondary channel in HT40 mode (in us) + */ +#define CH_BUSY_STA_SEC 0x1138 + +/* + * MAC_STATUS_CFG: + * BBP_RF_BUSY: When set to 0, BBP and RF are stable. + * if 1 or higher one of the 2 registers is busy. + */ +#define MAC_STATUS_CFG 0x1200 +#define MAC_STATUS_CFG_BBP_RF_BUSY FIELD32(0x00000003) + +/* + * PWR_PIN_CFG: + */ +#define PWR_PIN_CFG 0x1204 + +/* + * AUTOWAKEUP_CFG: Manual power control / status register + * TBCN_BEFORE_WAKE: ForceWake has high privilege than PutToSleep when both set + * AUTOWAKE: 0:sleep, 1:awake + */ +#define AUTOWAKEUP_CFG 0x1208 +#define AUTOWAKEUP_CFG_AUTO_LEAD_TIME FIELD32(0x000000ff) +#define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00) +#define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000) + +/* + * EDCA_AC0_CFG: + */ +#define EDCA_AC0_CFG 0x1300 +#define EDCA_AC0_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC0_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC0_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC0_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC1_CFG: + */ +#define EDCA_AC1_CFG 0x1304 +#define EDCA_AC1_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC1_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC1_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC1_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC2_CFG: + */ +#define EDCA_AC2_CFG 0x1308 +#define EDCA_AC2_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC2_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC2_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC2_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_AC3_CFG: + */ +#define EDCA_AC3_CFG 0x130c +#define EDCA_AC3_CFG_TX_OP FIELD32(0x000000ff) +#define EDCA_AC3_CFG_AIFSN FIELD32(0x00000f00) +#define EDCA_AC3_CFG_CWMIN FIELD32(0x0000f000) +#define EDCA_AC3_CFG_CWMAX FIELD32(0x000f0000) + +/* + * EDCA_TID_AC_MAP: + */ +#define EDCA_TID_AC_MAP 0x1310 + +/* + * TX_PWR_CFG: + */ +#define TX_PWR_CFG_RATE0 FIELD32(0x0000000f) +#define TX_PWR_CFG_RATE1 FIELD32(0x000000f0) +#define TX_PWR_CFG_RATE2 FIELD32(0x00000f00) +#define TX_PWR_CFG_RATE3 FIELD32(0x0000f000) +#define TX_PWR_CFG_RATE4 FIELD32(0x000f0000) +#define TX_PWR_CFG_RATE5 FIELD32(0x00f00000) +#define TX_PWR_CFG_RATE6 FIELD32(0x0f000000) +#define TX_PWR_CFG_RATE7 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_0: + */ +#define TX_PWR_CFG_0 0x1314 +#define TX_PWR_CFG_0_1MBS FIELD32(0x0000000f) +#define TX_PWR_CFG_0_2MBS FIELD32(0x000000f0) +#define TX_PWR_CFG_0_55MBS FIELD32(0x00000f00) +#define TX_PWR_CFG_0_11MBS FIELD32(0x0000f000) +#define TX_PWR_CFG_0_6MBS FIELD32(0x000f0000) +#define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000) +#define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000) +#define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_0_CCK1_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_0_CCK1_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_0_CCK5_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_0_CCK5_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_0_OFDM6_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_0_OFDM6_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_0_OFDM12_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_0_OFDM12_CH1 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_1: + */ +#define TX_PWR_CFG_1 0x1318 +#define TX_PWR_CFG_1_24MBS FIELD32(0x0000000f) +#define TX_PWR_CFG_1_36MBS FIELD32(0x000000f0) +#define TX_PWR_CFG_1_48MBS FIELD32(0x00000f00) +#define TX_PWR_CFG_1_54MBS FIELD32(0x0000f000) +#define TX_PWR_CFG_1_MCS0 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000) +#define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000) +#define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_1_OFDM24_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_1_OFDM24_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_1_OFDM48_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_1_OFDM48_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_1_MCS0_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_MCS0_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_1_MCS2_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_1_MCS2_CH1 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_2: + */ +#define TX_PWR_CFG_2 0x131c +#define TX_PWR_CFG_2_MCS4 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_MCS5 FIELD32(0x000000f0) +#define TX_PWR_CFG_2_MCS6 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_MCS7 FIELD32(0x0000f000) +#define TX_PWR_CFG_2_MCS8 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000) +#define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000) +#define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_2_MCS4_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_MCS4_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_2_MCS6_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_MCS6_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_2_MCS8_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_MCS8_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_2_MCS10_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_2_MCS10_CH1 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_3: + */ +#define TX_PWR_CFG_3 0x1320 +#define TX_PWR_CFG_3_MCS12 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_MCS13 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_MCS14 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_MCS15 FIELD32(0x0000f000) +#define TX_PWR_CFG_3_UKNOWN1 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000) +#define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000) +#define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000) +/* bits for 3T devices */ +#define TX_PWR_CFG_3_MCS12_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_MCS12_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_MCS14_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_MCS14_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_3_STBC0_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_STBC0_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_3_STBC2_CH0 FIELD32(0x0f000000) +#define TX_PWR_CFG_3_STBC2_CH1 FIELD32(0xf0000000) + +/* + * TX_PWR_CFG_4: + */ +#define TX_PWR_CFG_4 0x1324 +#define TX_PWR_CFG_4_UKNOWN5 FIELD32(0x0000000f) +#define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0) +#define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00) +#define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000) +/* bits for 3T devices */ +#define TX_PWR_CFG_3_STBC4_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_STBC4_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_3_STBC6_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_STBC6_CH1 FIELD32(0x0000f000) + +/* + * TX_PIN_CFG: + */ +#define TX_PIN_CFG 0x1328 +#define TX_PIN_CFG_PA_PE_DISABLE 0xfcfffff0 +#define TX_PIN_CFG_PA_PE_A0_EN FIELD32(0x00000001) +#define TX_PIN_CFG_PA_PE_G0_EN FIELD32(0x00000002) +#define TX_PIN_CFG_PA_PE_A1_EN FIELD32(0x00000004) +#define TX_PIN_CFG_PA_PE_G1_EN FIELD32(0x00000008) +#define TX_PIN_CFG_PA_PE_A0_POL FIELD32(0x00000010) +#define TX_PIN_CFG_PA_PE_G0_POL FIELD32(0x00000020) +#define TX_PIN_CFG_PA_PE_A1_POL FIELD32(0x00000040) +#define TX_PIN_CFG_PA_PE_G1_POL FIELD32(0x00000080) +#define TX_PIN_CFG_LNA_PE_A0_EN FIELD32(0x00000100) +#define TX_PIN_CFG_LNA_PE_G0_EN FIELD32(0x00000200) +#define TX_PIN_CFG_LNA_PE_A1_EN FIELD32(0x00000400) +#define TX_PIN_CFG_LNA_PE_G1_EN FIELD32(0x00000800) +#define TX_PIN_CFG_LNA_PE_A0_POL FIELD32(0x00001000) +#define TX_PIN_CFG_LNA_PE_G0_POL FIELD32(0x00002000) +#define TX_PIN_CFG_LNA_PE_A1_POL FIELD32(0x00004000) +#define TX_PIN_CFG_LNA_PE_G1_POL FIELD32(0x00008000) +#define TX_PIN_CFG_RFTR_EN FIELD32(0x00010000) +#define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000) +#define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000) +#define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000) +#define TX_PIN_CFG_PA_PE_A2_EN FIELD32(0x01000000) +#define TX_PIN_CFG_PA_PE_G2_EN FIELD32(0x02000000) +#define TX_PIN_CFG_PA_PE_A2_POL FIELD32(0x04000000) +#define TX_PIN_CFG_PA_PE_G2_POL FIELD32(0x08000000) +#define TX_PIN_CFG_LNA_PE_A2_EN FIELD32(0x10000000) +#define TX_PIN_CFG_LNA_PE_G2_EN FIELD32(0x20000000) +#define TX_PIN_CFG_LNA_PE_A2_POL FIELD32(0x40000000) +#define TX_PIN_CFG_LNA_PE_G2_POL FIELD32(0x80000000) + +/* + * TX_BAND_CFG: 0x1 use upper 20MHz, 0x0 use lower 20MHz + */ +#define TX_BAND_CFG 0x132c +#define TX_BAND_CFG_HT40_MINUS FIELD32(0x00000001) +#define TX_BAND_CFG_A FIELD32(0x00000002) +#define TX_BAND_CFG_BG FIELD32(0x00000004) + +/* + * TX_SW_CFG0: + */ +#define TX_SW_CFG0 0x1330 + +/* + * TX_SW_CFG1: + */ +#define TX_SW_CFG1 0x1334 + +/* + * TX_SW_CFG2: + */ +#define TX_SW_CFG2 0x1338 + +/* + * TXOP_THRES_CFG: + */ +#define TXOP_THRES_CFG 0x133c + +/* + * TXOP_CTRL_CFG: + * TIMEOUT_TRUN_EN: Enable/Disable TXOP timeout truncation + * AC_TRUN_EN: Enable/Disable truncation for AC change + * TXRATEGRP_TRUN_EN: Enable/Disable truncation for TX rate group change + * USER_MODE_TRUN_EN: Enable/Disable truncation for user TXOP mode + * MIMO_PS_TRUN_EN: Enable/Disable truncation for MIMO PS RTS/CTS + * RESERVED_TRUN_EN: Reserved + * LSIG_TXOP_EN: Enable/Disable L-SIG TXOP protection + * EXT_CCA_EN: Enable/Disable extension channel CCA reference (Defer 40Mhz + * transmissions if extension CCA is clear). + * EXT_CCA_DLY: Extension CCA signal delay time (unit: us) + * EXT_CWMIN: CwMin for extension channel backoff + * 0: Disabled + * + */ +#define TXOP_CTRL_CFG 0x1340 +#define TXOP_CTRL_CFG_TIMEOUT_TRUN_EN FIELD32(0x00000001) +#define TXOP_CTRL_CFG_AC_TRUN_EN FIELD32(0x00000002) +#define TXOP_CTRL_CFG_TXRATEGRP_TRUN_EN FIELD32(0x00000004) +#define TXOP_CTRL_CFG_USER_MODE_TRUN_EN FIELD32(0x00000008) +#define TXOP_CTRL_CFG_MIMO_PS_TRUN_EN FIELD32(0x00000010) +#define TXOP_CTRL_CFG_RESERVED_TRUN_EN FIELD32(0x00000020) +#define TXOP_CTRL_CFG_LSIG_TXOP_EN FIELD32(0x00000040) +#define TXOP_CTRL_CFG_EXT_CCA_EN FIELD32(0x00000080) +#define TXOP_CTRL_CFG_EXT_CCA_DLY FIELD32(0x0000ff00) +#define TXOP_CTRL_CFG_EXT_CWMIN FIELD32(0x000f0000) + +/* + * TX_RTS_CFG: + * RTS_THRES: unit:byte + * RTS_FBK_EN: enable rts rate fallback + */ +#define TX_RTS_CFG 0x1344 +#define TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT FIELD32(0x000000ff) +#define TX_RTS_CFG_RTS_THRES FIELD32(0x00ffff00) +#define TX_RTS_CFG_RTS_FBK_EN FIELD32(0x01000000) + +/* + * TX_TIMEOUT_CFG: + * MPDU_LIFETIME: expiration time = 2^(9+MPDU LIFE TIME) us + * RX_ACK_TIMEOUT: unit:slot. Used for TX procedure + * TX_OP_TIMEOUT: TXOP timeout value for TXOP truncation. + * it is recommended that: + * (SLOT_TIME) > (TX_OP_TIMEOUT) > (RX_ACK_TIMEOUT) + */ +#define TX_TIMEOUT_CFG 0x1348 +#define TX_TIMEOUT_CFG_MPDU_LIFETIME FIELD32(0x000000f0) +#define TX_TIMEOUT_CFG_RX_ACK_TIMEOUT FIELD32(0x0000ff00) +#define TX_TIMEOUT_CFG_TX_OP_TIMEOUT FIELD32(0x00ff0000) + +/* + * TX_RTY_CFG: + * SHORT_RTY_LIMIT: short retry limit + * LONG_RTY_LIMIT: long retry limit + * LONG_RTY_THRE: Long retry threshoold + * NON_AGG_RTY_MODE: Non-Aggregate MPDU retry mode + * 0:expired by retry limit, 1: expired by mpdu life timer + * AGG_RTY_MODE: Aggregate MPDU retry mode + * 0:expired by retry limit, 1: expired by mpdu life timer + * TX_AUTO_FB_ENABLE: Tx retry PHY rate auto fallback enable + */ +#define TX_RTY_CFG 0x134c +#define TX_RTY_CFG_SHORT_RTY_LIMIT FIELD32(0x000000ff) +#define TX_RTY_CFG_LONG_RTY_LIMIT FIELD32(0x0000ff00) +#define TX_RTY_CFG_LONG_RTY_THRE FIELD32(0x0fff0000) +#define TX_RTY_CFG_NON_AGG_RTY_MODE FIELD32(0x10000000) +#define TX_RTY_CFG_AGG_RTY_MODE FIELD32(0x20000000) +#define TX_RTY_CFG_TX_AUTO_FB_ENABLE FIELD32(0x40000000) + +/* + * TX_LINK_CFG: + * REMOTE_MFB_LIFETIME: remote MFB life time. unit: 32us + * MFB_ENABLE: TX apply remote MFB 1:enable + * REMOTE_UMFS_ENABLE: remote unsolicit MFB enable + * 0: not apply remote remote unsolicit (MFS=7) + * TX_MRQ_EN: MCS request TX enable + * TX_RDG_EN: RDG TX enable + * TX_CF_ACK_EN: Piggyback CF-ACK enable + * REMOTE_MFB: remote MCS feedback + * REMOTE_MFS: remote MCS feedback sequence number + */ +#define TX_LINK_CFG 0x1350 +#define TX_LINK_CFG_REMOTE_MFB_LIFETIME FIELD32(0x000000ff) +#define TX_LINK_CFG_MFB_ENABLE FIELD32(0x00000100) +#define TX_LINK_CFG_REMOTE_UMFS_ENABLE FIELD32(0x00000200) +#define TX_LINK_CFG_TX_MRQ_EN FIELD32(0x00000400) +#define TX_LINK_CFG_TX_RDG_EN FIELD32(0x00000800) +#define TX_LINK_CFG_TX_CF_ACK_EN FIELD32(0x00001000) +#define TX_LINK_CFG_REMOTE_MFB FIELD32(0x00ff0000) +#define TX_LINK_CFG_REMOTE_MFS FIELD32(0xff000000) + +/* + * HT_FBK_CFG0: + */ +#define HT_FBK_CFG0 0x1354 +#define HT_FBK_CFG0_HTMCS0FBK FIELD32(0x0000000f) +#define HT_FBK_CFG0_HTMCS1FBK FIELD32(0x000000f0) +#define HT_FBK_CFG0_HTMCS2FBK FIELD32(0x00000f00) +#define HT_FBK_CFG0_HTMCS3FBK FIELD32(0x0000f000) +#define HT_FBK_CFG0_HTMCS4FBK FIELD32(0x000f0000) +#define HT_FBK_CFG0_HTMCS5FBK FIELD32(0x00f00000) +#define HT_FBK_CFG0_HTMCS6FBK FIELD32(0x0f000000) +#define HT_FBK_CFG0_HTMCS7FBK FIELD32(0xf0000000) + +/* + * HT_FBK_CFG1: + */ +#define HT_FBK_CFG1 0x1358 +#define HT_FBK_CFG1_HTMCS8FBK FIELD32(0x0000000f) +#define HT_FBK_CFG1_HTMCS9FBK FIELD32(0x000000f0) +#define HT_FBK_CFG1_HTMCS10FBK FIELD32(0x00000f00) +#define HT_FBK_CFG1_HTMCS11FBK FIELD32(0x0000f000) +#define HT_FBK_CFG1_HTMCS12FBK FIELD32(0x000f0000) +#define HT_FBK_CFG1_HTMCS13FBK FIELD32(0x00f00000) +#define HT_FBK_CFG1_HTMCS14FBK FIELD32(0x0f000000) +#define HT_FBK_CFG1_HTMCS15FBK FIELD32(0xf0000000) + +/* + * LG_FBK_CFG0: + */ +#define LG_FBK_CFG0 0x135c +#define LG_FBK_CFG0_OFDMMCS0FBK FIELD32(0x0000000f) +#define LG_FBK_CFG0_OFDMMCS1FBK FIELD32(0x000000f0) +#define LG_FBK_CFG0_OFDMMCS2FBK FIELD32(0x00000f00) +#define LG_FBK_CFG0_OFDMMCS3FBK FIELD32(0x0000f000) +#define LG_FBK_CFG0_OFDMMCS4FBK FIELD32(0x000f0000) +#define LG_FBK_CFG0_OFDMMCS5FBK FIELD32(0x00f00000) +#define LG_FBK_CFG0_OFDMMCS6FBK FIELD32(0x0f000000) +#define LG_FBK_CFG0_OFDMMCS7FBK FIELD32(0xf0000000) + +/* + * LG_FBK_CFG1: + */ +#define LG_FBK_CFG1 0x1360 +#define LG_FBK_CFG0_CCKMCS0FBK FIELD32(0x0000000f) +#define LG_FBK_CFG0_CCKMCS1FBK FIELD32(0x000000f0) +#define LG_FBK_CFG0_CCKMCS2FBK FIELD32(0x00000f00) +#define LG_FBK_CFG0_CCKMCS3FBK FIELD32(0x0000f000) + +/* + * CCK_PROT_CFG: CCK Protection + * PROTECT_RATE: Protection control frame rate for CCK TX(RTS/CTS/CFEnd) + * PROTECT_CTRL: Protection control frame type for CCK TX + * 0:none, 1:RTS/CTS, 2:CTS-to-self + * PROTECT_NAV_SHORT: TXOP protection type for CCK TX with short NAV + * PROTECT_NAV_LONG: TXOP protection type for CCK TX with long NAV + * TX_OP_ALLOW_CCK: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_OFDM: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_MM20: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_MM40: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_GF20: CCK TXOP allowance, 0:disallow + * TX_OP_ALLOW_GF40: CCK TXOP allowance, 0:disallow + * RTS_TH_EN: RTS threshold enable on CCK TX + */ +#define CCK_PROT_CFG 0x1364 +#define CCK_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define CCK_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define CCK_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define CCK_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define CCK_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define CCK_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define CCK_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define CCK_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define CCK_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define CCK_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define CCK_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * OFDM_PROT_CFG: OFDM Protection + */ +#define OFDM_PROT_CFG 0x1368 +#define OFDM_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define OFDM_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define OFDM_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define OFDM_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define OFDM_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define OFDM_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * MM20_PROT_CFG: MM20 Protection + */ +#define MM20_PROT_CFG 0x136c +#define MM20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define MM20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define MM20_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define MM20_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define MM20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define MM20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define MM20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define MM20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define MM20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define MM20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define MM20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * MM40_PROT_CFG: MM40 Protection + */ +#define MM40_PROT_CFG 0x1370 +#define MM40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define MM40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define MM40_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define MM40_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define MM40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define MM40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define MM40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define MM40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define MM40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define MM40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define MM40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * GF20_PROT_CFG: GF20 Protection + */ +#define GF20_PROT_CFG 0x1374 +#define GF20_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define GF20_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define GF20_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define GF20_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define GF20_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define GF20_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define GF20_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define GF20_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define GF20_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define GF20_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define GF20_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * GF40_PROT_CFG: GF40 Protection + */ +#define GF40_PROT_CFG 0x1378 +#define GF40_PROT_CFG_PROTECT_RATE FIELD32(0x0000ffff) +#define GF40_PROT_CFG_PROTECT_CTRL FIELD32(0x00030000) +#define GF40_PROT_CFG_PROTECT_NAV_SHORT FIELD32(0x00040000) +#define GF40_PROT_CFG_PROTECT_NAV_LONG FIELD32(0x00080000) +#define GF40_PROT_CFG_TX_OP_ALLOW_CCK FIELD32(0x00100000) +#define GF40_PROT_CFG_TX_OP_ALLOW_OFDM FIELD32(0x00200000) +#define GF40_PROT_CFG_TX_OP_ALLOW_MM20 FIELD32(0x00400000) +#define GF40_PROT_CFG_TX_OP_ALLOW_MM40 FIELD32(0x00800000) +#define GF40_PROT_CFG_TX_OP_ALLOW_GF20 FIELD32(0x01000000) +#define GF40_PROT_CFG_TX_OP_ALLOW_GF40 FIELD32(0x02000000) +#define GF40_PROT_CFG_RTS_TH_EN FIELD32(0x04000000) + +/* + * EXP_CTS_TIME: + */ +#define EXP_CTS_TIME 0x137c + +/* + * EXP_ACK_TIME: + */ +#define EXP_ACK_TIME 0x1380 + +/* TX_PWR_CFG_5 */ +#define TX_PWR_CFG_5 0x1384 +#define TX_PWR_CFG_5_MCS16_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_5_MCS16_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_5_MCS16_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_5_MCS18_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_5_MCS18_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_5_MCS18_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_6 */ +#define TX_PWR_CFG_6 0x1388 +#define TX_PWR_CFG_6_MCS20_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_6_MCS20_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_6_MCS20_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_6_MCS22_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_6_MCS22_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_6_MCS22_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_0_EXT */ +#define TX_PWR_CFG_0_EXT 0x1390 +#define TX_PWR_CFG_0_EXT_CCK1_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_0_EXT_CCK5_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_0_EXT_OFDM6_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_0_EXT_OFDM12_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_1_EXT */ +#define TX_PWR_CFG_1_EXT 0x1394 +#define TX_PWR_CFG_1_EXT_OFDM24_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_1_EXT_OFDM48_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_1_EXT_MCS0_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_1_EXT_MCS2_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_2_EXT */ +#define TX_PWR_CFG_2_EXT 0x1398 +#define TX_PWR_CFG_2_EXT_MCS4_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_2_EXT_MCS6_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_2_EXT_MCS8_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_2_EXT_MCS10_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_3_EXT */ +#define TX_PWR_CFG_3_EXT 0x139c +#define TX_PWR_CFG_3_EXT_MCS12_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_3_EXT_MCS14_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_3_EXT_STBC0_CH2 FIELD32(0x000f0000) +#define TX_PWR_CFG_3_EXT_STBC2_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_4_EXT */ +#define TX_PWR_CFG_4_EXT 0x13a0 +#define TX_PWR_CFG_4_EXT_STBC4_CH2 FIELD32(0x0000000f) +#define TX_PWR_CFG_4_EXT_STBC6_CH2 FIELD32(0x00000f00) + +/* TX_PWR_CFG_7 */ +#define TX_PWR_CFG_7 0x13d4 +#define TX_PWR_CFG_7_OFDM54_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_7_OFDM54_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_7_OFDM54_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_7_MCS7_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_7_MCS7_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_7_MCS7_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_8 */ +#define TX_PWR_CFG_8 0x13d8 +#define TX_PWR_CFG_8_MCS15_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_8_MCS15_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_8_MCS15_CH2 FIELD32(0x00000f00) +#define TX_PWR_CFG_8_MCS23_CH0 FIELD32(0x000f0000) +#define TX_PWR_CFG_8_MCS23_CH1 FIELD32(0x00f00000) +#define TX_PWR_CFG_8_MCS23_CH2 FIELD32(0x0f000000) + +/* TX_PWR_CFG_9 */ +#define TX_PWR_CFG_9 0x13dc +#define TX_PWR_CFG_9_STBC7_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_9_STBC7_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_9_STBC7_CH2 FIELD32(0x00000f00) + +/* + * RX_FILTER_CFG: RX configuration register. + */ +#define RX_FILTER_CFG 0x1400 +#define RX_FILTER_CFG_DROP_CRC_ERROR FIELD32(0x00000001) +#define RX_FILTER_CFG_DROP_PHY_ERROR FIELD32(0x00000002) +#define RX_FILTER_CFG_DROP_NOT_TO_ME FIELD32(0x00000004) +#define RX_FILTER_CFG_DROP_NOT_MY_BSSD FIELD32(0x00000008) +#define RX_FILTER_CFG_DROP_VER_ERROR FIELD32(0x00000010) +#define RX_FILTER_CFG_DROP_MULTICAST FIELD32(0x00000020) +#define RX_FILTER_CFG_DROP_BROADCAST FIELD32(0x00000040) +#define RX_FILTER_CFG_DROP_DUPLICATE FIELD32(0x00000080) +#define RX_FILTER_CFG_DROP_CF_END_ACK FIELD32(0x00000100) +#define RX_FILTER_CFG_DROP_CF_END FIELD32(0x00000200) +#define RX_FILTER_CFG_DROP_ACK FIELD32(0x00000400) +#define RX_FILTER_CFG_DROP_CTS FIELD32(0x00000800) +#define RX_FILTER_CFG_DROP_RTS FIELD32(0x00001000) +#define RX_FILTER_CFG_DROP_PSPOLL FIELD32(0x00002000) +#define RX_FILTER_CFG_DROP_BA FIELD32(0x00004000) +#define RX_FILTER_CFG_DROP_BAR FIELD32(0x00008000) +#define RX_FILTER_CFG_DROP_CNTL FIELD32(0x00010000) + +/* + * AUTO_RSP_CFG: + * AUTORESPONDER: 0: disable, 1: enable + * BAC_ACK_POLICY: 0:long, 1:short preamble + * CTS_40_MMODE: Response CTS 40MHz duplicate mode + * CTS_40_MREF: Response CTS 40MHz duplicate mode + * AR_PREAMBLE: Auto responder preamble 0:long, 1:short preamble + * DUAL_CTS_EN: Power bit value in control frame + * ACK_CTS_PSM_BIT:Power bit value in control frame + */ +#define AUTO_RSP_CFG 0x1404 +#define AUTO_RSP_CFG_AUTORESPONDER FIELD32(0x00000001) +#define AUTO_RSP_CFG_BAC_ACK_POLICY FIELD32(0x00000002) +#define AUTO_RSP_CFG_CTS_40_MMODE FIELD32(0x00000004) +#define AUTO_RSP_CFG_CTS_40_MREF FIELD32(0x00000008) +#define AUTO_RSP_CFG_AR_PREAMBLE FIELD32(0x00000010) +#define AUTO_RSP_CFG_DUAL_CTS_EN FIELD32(0x00000040) +#define AUTO_RSP_CFG_ACK_CTS_PSM_BIT FIELD32(0x00000080) + +/* + * LEGACY_BASIC_RATE: + */ +#define LEGACY_BASIC_RATE 0x1408 + +/* + * HT_BASIC_RATE: + */ +#define HT_BASIC_RATE 0x140c + +/* + * HT_CTRL_CFG: + */ +#define HT_CTRL_CFG 0x1410 + +/* + * SIFS_COST_CFG: + */ +#define SIFS_COST_CFG 0x1414 + +/* + * RX_PARSER_CFG: + * Set NAV for all received frames + */ +#define RX_PARSER_CFG 0x1418 + +/* + * TX_SEC_CNT0: + */ +#define TX_SEC_CNT0 0x1500 + +/* + * RX_SEC_CNT0: + */ +#define RX_SEC_CNT0 0x1504 + +/* + * CCMP_FC_MUTE: + */ +#define CCMP_FC_MUTE 0x1508 + +/* + * TXOP_HLDR_ADDR0: + */ +#define TXOP_HLDR_ADDR0 0x1600 + +/* + * TXOP_HLDR_ADDR1: + */ +#define TXOP_HLDR_ADDR1 0x1604 + +/* + * TXOP_HLDR_ET: + */ +#define TXOP_HLDR_ET 0x1608 + +/* + * QOS_CFPOLL_RA_DW0: + */ +#define QOS_CFPOLL_RA_DW0 0x160c + +/* + * QOS_CFPOLL_RA_DW1: + */ +#define QOS_CFPOLL_RA_DW1 0x1610 + +/* + * QOS_CFPOLL_QC: + */ +#define QOS_CFPOLL_QC 0x1614 + +/* + * RX_STA_CNT0: RX PLCP error count & RX CRC error count + */ +#define RX_STA_CNT0 0x1700 +#define RX_STA_CNT0_CRC_ERR FIELD32(0x0000ffff) +#define RX_STA_CNT0_PHY_ERR FIELD32(0xffff0000) + +/* + * RX_STA_CNT1: RX False CCA count & RX LONG frame count + */ +#define RX_STA_CNT1 0x1704 +#define RX_STA_CNT1_FALSE_CCA FIELD32(0x0000ffff) +#define RX_STA_CNT1_PLCP_ERR FIELD32(0xffff0000) + +/* + * RX_STA_CNT2: + */ +#define RX_STA_CNT2 0x1708 +#define RX_STA_CNT2_RX_DUPLI_COUNT FIELD32(0x0000ffff) +#define RX_STA_CNT2_RX_FIFO_OVERFLOW FIELD32(0xffff0000) + +/* + * TX_STA_CNT0: TX Beacon count + */ +#define TX_STA_CNT0 0x170c +#define TX_STA_CNT0_TX_FAIL_COUNT FIELD32(0x0000ffff) +#define TX_STA_CNT0_TX_BEACON_COUNT FIELD32(0xffff0000) + +/* + * TX_STA_CNT1: TX tx count + */ +#define TX_STA_CNT1 0x1710 +#define TX_STA_CNT1_TX_SUCCESS FIELD32(0x0000ffff) +#define TX_STA_CNT1_TX_RETRANSMIT FIELD32(0xffff0000) + +/* + * TX_STA_CNT2: TX tx count + */ +#define TX_STA_CNT2 0x1714 +#define TX_STA_CNT2_TX_ZERO_LEN_COUNT FIELD32(0x0000ffff) +#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000) + +/* + * TX_STA_FIFO: TX Result for specific PID status fifo register. + * + * This register is implemented as FIFO with 16 entries in the HW. Each + * register read fetches the next tx result. If the FIFO is full because + * it wasn't read fast enough after the according interrupt (TX_FIFO_STATUS) + * triggered, the hw seems to simply drop further tx results. + * + * VALID: 1: this tx result is valid + * 0: no valid tx result -> driver should stop reading + * PID_TYPE: The PID latched from the PID field in the TXWI, can be used + * to match a frame with its tx result (even though the PID is + * only 4 bits wide). + * PID_QUEUE: Part of PID_TYPE, this is the queue index number (0-3) + * PID_ENTRY: Part of PID_TYPE, this is the queue entry index number (1-3) + * This identification number is calculated by ((idx % 3) + 1). + * TX_SUCCESS: Indicates tx success (1) or failure (0) + * TX_AGGRE: Indicates if the frame was part of an aggregate (1) or not (0) + * TX_ACK_REQUIRED: Indicates if the frame needed to get ack'ed (1) or not (0) + * WCID: The wireless client ID. + * MCS: The tx rate used during the last transmission of this frame, be it + * successful or not. + * PHYMODE: The phymode used for the transmission. + */ +#define TX_STA_FIFO 0x1718 +#define TX_STA_FIFO_VALID FIELD32(0x00000001) +#define TX_STA_FIFO_PID_TYPE FIELD32(0x0000001e) +#define TX_STA_FIFO_PID_QUEUE FIELD32(0x00000006) +#define TX_STA_FIFO_PID_ENTRY FIELD32(0x00000018) +#define TX_STA_FIFO_TX_SUCCESS FIELD32(0x00000020) +#define TX_STA_FIFO_TX_AGGRE FIELD32(0x00000040) +#define TX_STA_FIFO_TX_ACK_REQUIRED FIELD32(0x00000080) +#define TX_STA_FIFO_WCID FIELD32(0x0000ff00) +#define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000) +#define TX_STA_FIFO_MCS FIELD32(0x007f0000) +#define TX_STA_FIFO_PHYMODE FIELD32(0xc0000000) + +/* + * TX_AGG_CNT: Debug counter + */ +#define TX_AGG_CNT 0x171c +#define TX_AGG_CNT_NON_AGG_TX_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT_AGG_TX_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT0: + */ +#define TX_AGG_CNT0 0x1720 +#define TX_AGG_CNT0_AGG_SIZE_1_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT0_AGG_SIZE_2_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT1: + */ +#define TX_AGG_CNT1 0x1724 +#define TX_AGG_CNT1_AGG_SIZE_3_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT1_AGG_SIZE_4_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT2: + */ +#define TX_AGG_CNT2 0x1728 +#define TX_AGG_CNT2_AGG_SIZE_5_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT2_AGG_SIZE_6_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT3: + */ +#define TX_AGG_CNT3 0x172c +#define TX_AGG_CNT3_AGG_SIZE_7_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT3_AGG_SIZE_8_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT4: + */ +#define TX_AGG_CNT4 0x1730 +#define TX_AGG_CNT4_AGG_SIZE_9_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT4_AGG_SIZE_10_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT5: + */ +#define TX_AGG_CNT5 0x1734 +#define TX_AGG_CNT5_AGG_SIZE_11_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT5_AGG_SIZE_12_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT6: + */ +#define TX_AGG_CNT6 0x1738 +#define TX_AGG_CNT6_AGG_SIZE_13_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT6_AGG_SIZE_14_COUNT FIELD32(0xffff0000) + +/* + * TX_AGG_CNT7: + */ +#define TX_AGG_CNT7 0x173c +#define TX_AGG_CNT7_AGG_SIZE_15_COUNT FIELD32(0x0000ffff) +#define TX_AGG_CNT7_AGG_SIZE_16_COUNT FIELD32(0xffff0000) + +/* + * MPDU_DENSITY_CNT: + * TX_ZERO_DEL: TX zero length delimiter count + * RX_ZERO_DEL: RX zero length delimiter count + */ +#define MPDU_DENSITY_CNT 0x1740 +#define MPDU_DENSITY_CNT_TX_ZERO_DEL FIELD32(0x0000ffff) +#define MPDU_DENSITY_CNT_RX_ZERO_DEL FIELD32(0xffff0000) + +/* + * Security key table memory. + * + * The pairwise key table shares some memory with the beacon frame + * buffers 6 and 7. That basically means that when beacon 6 & 7 + * are used we should only use the reduced pairwise key table which + * has a maximum of 222 entries. + * + * --------------------------------------------- + * |0x4000 | Pairwise Key | Reduced Pairwise | + * | | Table | Key Table | + * | | Size: 256 * 32 | Size: 222 * 32 | + * |0x5BC0 | |------------------- + * | | | Beacon 6 | + * |0x5DC0 | |------------------- + * | | | Beacon 7 | + * |0x5FC0 | |------------------- + * |0x5FFF | | + * -------------------------- + * + * MAC_WCID_BASE: 8-bytes (use only 6 bytes) * 256 entry + * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry + * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry + * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry + * SHARED_KEY_TABLE_BASE: 32-byte * 16-entry + * SHARED_KEY_MODE_BASE: 4-byte * 16-entry + */ +#define MAC_WCID_BASE 0x1800 +#define PAIRWISE_KEY_TABLE_BASE 0x4000 +#define MAC_IVEIV_TABLE_BASE 0x6000 +#define MAC_WCID_ATTRIBUTE_BASE 0x6800 +#define SHARED_KEY_TABLE_BASE 0x6c00 +#define SHARED_KEY_MODE_BASE 0x7000 + +#define MAC_WCID_ENTRY(__idx) \ + (MAC_WCID_BASE + ((__idx) * sizeof(struct mac_wcid_entry))) +#define PAIRWISE_KEY_ENTRY(__idx) \ + (PAIRWISE_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry))) +#define MAC_IVEIV_ENTRY(__idx) \ + (MAC_IVEIV_TABLE_BASE + ((__idx) * sizeof(struct mac_iveiv_entry))) +#define MAC_WCID_ATTR_ENTRY(__idx) \ + (MAC_WCID_ATTRIBUTE_BASE + ((__idx) * sizeof(u32))) +#define SHARED_KEY_ENTRY(__idx) \ + (SHARED_KEY_TABLE_BASE + ((__idx) * sizeof(struct hw_key_entry))) +#define SHARED_KEY_MODE_ENTRY(__idx) \ + (SHARED_KEY_MODE_BASE + ((__idx) * sizeof(u32))) + +struct mac_wcid_entry { + u8 mac[6]; + u8 reserved[2]; +} __packed; + +struct hw_key_entry { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __packed; + +struct mac_iveiv_entry { + u8 iv[8]; +} __packed; + +/* + * MAC_WCID_ATTRIBUTE: + */ +#define MAC_WCID_ATTRIBUTE_KEYTAB FIELD32(0x00000001) +#define MAC_WCID_ATTRIBUTE_CIPHER FIELD32(0x0000000e) +#define MAC_WCID_ATTRIBUTE_BSS_IDX FIELD32(0x00000070) +#define MAC_WCID_ATTRIBUTE_RX_WIUDF FIELD32(0x00000380) +#define MAC_WCID_ATTRIBUTE_CIPHER_EXT FIELD32(0x00000400) +#define MAC_WCID_ATTRIBUTE_BSS_IDX_EXT FIELD32(0x00000800) +#define MAC_WCID_ATTRIBUTE_WAPI_MCBC FIELD32(0x00008000) +#define MAC_WCID_ATTRIBUTE_WAPI_KEY_IDX FIELD32(0xff000000) + +/* + * SHARED_KEY_MODE: + */ +#define SHARED_KEY_MODE_BSS0_KEY0 FIELD32(0x00000007) +#define SHARED_KEY_MODE_BSS0_KEY1 FIELD32(0x00000070) +#define SHARED_KEY_MODE_BSS0_KEY2 FIELD32(0x00000700) +#define SHARED_KEY_MODE_BSS0_KEY3 FIELD32(0x00007000) +#define SHARED_KEY_MODE_BSS1_KEY0 FIELD32(0x00070000) +#define SHARED_KEY_MODE_BSS1_KEY1 FIELD32(0x00700000) +#define SHARED_KEY_MODE_BSS1_KEY2 FIELD32(0x07000000) +#define SHARED_KEY_MODE_BSS1_KEY3 FIELD32(0x70000000) + +/* + * HOST-MCU communication + */ + +/* + * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. + * CMD_TOKEN: Command id, 0xff disable status reporting. + */ +#define H2M_MAILBOX_CSR 0x7010 +#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) +#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) +#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) +#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) + +/* + * H2M_MAILBOX_CID: + * Free slots contain 0xff. MCU will store command's token to lowest free slot. + * If all slots are occupied status will be dropped. + */ +#define H2M_MAILBOX_CID 0x7014 +#define H2M_MAILBOX_CID_CMD0 FIELD32(0x000000ff) +#define H2M_MAILBOX_CID_CMD1 FIELD32(0x0000ff00) +#define H2M_MAILBOX_CID_CMD2 FIELD32(0x00ff0000) +#define H2M_MAILBOX_CID_CMD3 FIELD32(0xff000000) + +/* + * H2M_MAILBOX_STATUS: + * Command status will be saved to same slot as command id. + */ +#define H2M_MAILBOX_STATUS 0x701c + +/* + * H2M_INT_SRC: + */ +#define H2M_INT_SRC 0x7024 + +/* + * H2M_BBP_AGENT: + */ +#define H2M_BBP_AGENT 0x7028 + +/* + * MCU_LEDCS: LED control for MCU Mailbox. + */ +#define MCU_LEDCS_LED_MODE FIELD8(0x1f) +#define MCU_LEDCS_POLARITY FIELD8(0x01) + +/* + * HW_CS_CTS_BASE: + * Carrier-sense CTS frame base address. + * It's where mac stores carrier-sense frame for carrier-sense function. + */ +#define HW_CS_CTS_BASE 0x7700 + +/* + * HW_DFS_CTS_BASE: + * DFS CTS frame base address. It's where mac stores CTS frame for DFS. + */ +#define HW_DFS_CTS_BASE 0x7780 + +/* + * TXRX control registers - base address 0x3000 + */ + +/* + * TXRX_CSR1: + * rt2860b UNKNOWN reg use R/O Reg Addr 0x77d0 first.. + */ +#define TXRX_CSR1 0x77d0 + +/* + * HW_DEBUG_SETTING_BASE: + * since NULL frame won't be that long (256 byte) + * We steal 16 tail bytes to save debugging settings + */ +#define HW_DEBUG_SETTING_BASE 0x77f0 +#define HW_DEBUG_SETTING_BASE2 0x7770 + +/* + * HW_BEACON_BASE + * In order to support maximum 8 MBSS and its maximum length + * is 512 bytes for each beacon + * Three section discontinue memory segments will be used. + * 1. The original region for BCN 0~3 + * 2. Extract memory from FCE table for BCN 4~5 + * 3. Extract memory from Pair-wise key table for BCN 6~7 + * It occupied those memory of wcid 238~253 for BCN 6 + * and wcid 222~237 for BCN 7 (see Security key table memory + * for more info). + * + * IMPORTANT NOTE: Not sure why legacy driver does this, + * but HW_BEACON_BASE7 is 0x0200 bytes below HW_BEACON_BASE6. + */ +#define HW_BEACON_BASE0 0x7800 +#define HW_BEACON_BASE1 0x7a00 +#define HW_BEACON_BASE2 0x7c00 +#define HW_BEACON_BASE3 0x7e00 +#define HW_BEACON_BASE4 0x7200 +#define HW_BEACON_BASE5 0x7400 +#define HW_BEACON_BASE6 0x5dc0 +#define HW_BEACON_BASE7 0x5bc0 + +#define HW_BEACON_BASE(__index) \ + (((__index) < 4) ? (HW_BEACON_BASE0 + (__index * 0x0200)) : \ + (((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \ + (HW_BEACON_BASE6 - ((__index - 6) * 0x0200)))) + +#define BEACON_BASE_TO_OFFSET(_base) (((_base) - 0x4000) / 64) + +/* + * BBP registers. + * The wordsize of the BBP is 8 bits. + */ + +/* + * BBP 1: TX Antenna & Power Control + * POWER_CTRL: + * 0 - normal, + * 1 - drop tx power by 6dBm, + * 2 - drop tx power by 12dBm, + * 3 - increase tx power by 6dBm + */ +#define BBP1_TX_POWER_CTRL FIELD8(0x07) +#define BBP1_TX_ANTENNA FIELD8(0x18) + +/* + * BBP 3: RX Antenna + */ +#define BBP3_RX_ADC FIELD8(0x03) +#define BBP3_RX_ANTENNA FIELD8(0x18) +#define BBP3_HT40_MINUS FIELD8(0x20) +#define BBP3_ADC_MODE_SWITCH FIELD8(0x40) +#define BBP3_ADC_INIT_MODE FIELD8(0x80) + +/* + * BBP 4: Bandwidth + */ +#define BBP4_TX_BF FIELD8(0x01) +#define BBP4_BANDWIDTH FIELD8(0x18) +#define BBP4_MAC_IF_CTRL FIELD8(0x40) + +/* BBP27 */ +#define BBP27_RX_CHAIN_SEL FIELD8(0x60) + +/* + * BBP 47: Bandwidth + */ +#define BBP47_TSSI_REPORT_SEL FIELD8(0x03) +#define BBP47_TSSI_UPDATE_REQ FIELD8(0x04) +#define BBP47_TSSI_TSSI_MODE FIELD8(0x18) +#define BBP47_TSSI_ADC6 FIELD8(0x80) + +/* + * BBP 49 + */ +#define BBP49_UPDATE_FLAG FIELD8(0x01) + +/* + * BBP 105: + * - bit0: detect SIG on primary channel only (on 40MHz bandwidth) + * - bit1: FEQ (Feed Forward Compensation) for independend streams + * - bit2: MLD (Maximum Likehood Detection) for 2 streams (reserved on single + * stream) + * - bit4: channel estimation updates based on remodulation of + * L-SIG and HT-SIG symbols + */ +#define BBP105_DETECT_SIG_ON_PRIMARY FIELD8(0x01) +#define BBP105_FEQ FIELD8(0x02) +#define BBP105_MLD FIELD8(0x04) +#define BBP105_SIG_REMODULATION FIELD8(0x08) + +/* + * BBP 109 + */ +#define BBP109_TX0_POWER FIELD8(0x0f) +#define BBP109_TX1_POWER FIELD8(0xf0) + +/* BBP 110 */ +#define BBP110_TX2_POWER FIELD8(0x0f) + + +/* + * BBP 138: Unknown + */ +#define BBP138_RX_ADC1 FIELD8(0x02) +#define BBP138_RX_ADC2 FIELD8(0x04) +#define BBP138_TX_DAC1 FIELD8(0x20) +#define BBP138_TX_DAC2 FIELD8(0x40) + +/* + * BBP 152: Rx Ant + */ +#define BBP152_RX_DEFAULT_ANT FIELD8(0x80) + +/* + * BBP 254: unknown + */ +#define BBP254_BIT7 FIELD8(0x80) + +/* + * RFCSR registers + * The wordsize of the RFCSR is 8 bits. + */ + +/* + * RFCSR 1: + */ +#define RFCSR1_RF_BLOCK_EN FIELD8(0x01) +#define RFCSR1_PLL_PD FIELD8(0x02) +#define RFCSR1_RX0_PD FIELD8(0x04) +#define RFCSR1_TX0_PD FIELD8(0x08) +#define RFCSR1_RX1_PD FIELD8(0x10) +#define RFCSR1_TX1_PD FIELD8(0x20) +#define RFCSR1_RX2_PD FIELD8(0x40) +#define RFCSR1_TX2_PD FIELD8(0x80) + +/* + * RFCSR 2: + */ +#define RFCSR2_RESCAL_EN FIELD8(0x80) + +/* + * RFCSR 3: + */ +#define RFCSR3_K FIELD8(0x0f) +/* Bits [7-4] for RF3320 (RT3370/RT3390), on other chipsets reserved */ +#define RFCSR3_PA1_BIAS_CCK FIELD8(0x70) +#define RFCSR3_PA2_CASCODE_BIAS_CCKK FIELD8(0x80) +/* Bits for RF3290/RF5360/RF5370/RF5372/RF5390/RF5392 */ +#define RFCSR3_VCOCAL_EN FIELD8(0x80) +/* Bits for RF3050 */ +#define RFCSR3_BIT1 FIELD8(0x02) +#define RFCSR3_BIT2 FIELD8(0x04) +#define RFCSR3_BIT3 FIELD8(0x08) +#define RFCSR3_BIT4 FIELD8(0x10) +#define RFCSR3_BIT5 FIELD8(0x20) + +/* + * FRCSR 5: + */ +#define RFCSR5_R1 FIELD8(0x0c) + +/* + * RFCSR 6: + */ +#define RFCSR6_R1 FIELD8(0x03) +#define RFCSR6_R2 FIELD8(0x40) +#define RFCSR6_TXDIV FIELD8(0x0c) +/* bits for RF3053 */ +#define RFCSR6_VCO_IC FIELD8(0xc0) + +/* + * RFCSR 7: + */ +#define RFCSR7_RF_TUNING FIELD8(0x01) +#define RFCSR7_BIT1 FIELD8(0x02) +#define RFCSR7_BIT2 FIELD8(0x04) +#define RFCSR7_BIT3 FIELD8(0x08) +#define RFCSR7_BIT4 FIELD8(0x10) +#define RFCSR7_BIT5 FIELD8(0x20) +#define RFCSR7_BITS67 FIELD8(0xc0) + +/* + * RFCSR 9: + */ +#define RFCSR9_K FIELD8(0x0f) +#define RFCSR9_N FIELD8(0x10) +#define RFCSR9_UNKNOWN FIELD8(0x60) +#define RFCSR9_MOD FIELD8(0x80) + +/* + * RFCSR 11: + */ +#define RFCSR11_R FIELD8(0x03) +#define RFCSR11_PLL_MOD FIELD8(0x0c) +#define RFCSR11_MOD FIELD8(0xc0) +/* bits for RF3053 */ +/* TODO: verify RFCSR11_MOD usage on other chips */ +#define RFCSR11_PLL_IDOH FIELD8(0x40) + + +/* + * RFCSR 12: + */ +#define RFCSR12_TX_POWER FIELD8(0x1f) +#define RFCSR12_DR0 FIELD8(0xe0) + +/* + * RFCSR 13: + */ +#define RFCSR13_TX_POWER FIELD8(0x1f) +#define RFCSR13_DR0 FIELD8(0xe0) + +/* + * RFCSR 15: + */ +#define RFCSR15_TX_LO2_EN FIELD8(0x08) + +/* + * RFCSR 16: + */ +#define RFCSR16_TXMIXER_GAIN FIELD8(0x07) + +/* + * RFCSR 17: + */ +#define RFCSR17_TXMIXER_GAIN FIELD8(0x07) +#define RFCSR17_TX_LO1_EN FIELD8(0x08) +#define RFCSR17_R FIELD8(0x20) +#define RFCSR17_CODE FIELD8(0x7f) + +/* RFCSR 18 */ +#define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40) + + +/* + * RFCSR 20: + */ +#define RFCSR20_RX_LO1_EN FIELD8(0x08) + +/* + * RFCSR 21: + */ +#define RFCSR21_RX_LO2_EN FIELD8(0x08) + +/* + * RFCSR 22: + */ +#define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01) + +/* + * RFCSR 23: + */ +#define RFCSR23_FREQ_OFFSET FIELD8(0x7f) + +/* + * RFCSR 24: + */ +#define RFCSR24_TX_AGC_FC FIELD8(0x1f) +#define RFCSR24_TX_H20M FIELD8(0x20) +#define RFCSR24_TX_CALIB FIELD8(0x7f) + +/* + * RFCSR 27: + */ +#define RFCSR27_R1 FIELD8(0x03) +#define RFCSR27_R2 FIELD8(0x04) +#define RFCSR27_R3 FIELD8(0x30) +#define RFCSR27_R4 FIELD8(0x40) + +/* + * RFCSR 29: + */ +#define RFCSR29_ADC6_TEST FIELD8(0x01) +#define RFCSR29_ADC6_INT_TEST FIELD8(0x02) +#define RFCSR29_RSSI_RESET FIELD8(0x04) +#define RFCSR29_RSSI_ON FIELD8(0x08) +#define RFCSR29_RSSI_RIP_CTRL FIELD8(0x30) +#define RFCSR29_RSSI_GAIN FIELD8(0xc0) + +/* + * RFCSR 30: + */ +#define RFCSR30_TX_H20M FIELD8(0x02) +#define RFCSR30_RX_H20M FIELD8(0x04) +#define RFCSR30_RX_VCM FIELD8(0x18) +#define RFCSR30_RF_CALIBRATION FIELD8(0x80) + +/* + * RFCSR 31: + */ +#define RFCSR31_RX_AGC_FC FIELD8(0x1f) +#define RFCSR31_RX_H20M FIELD8(0x20) +#define RFCSR31_RX_CALIB FIELD8(0x7f) + +/* RFCSR 32 bits for RF3053 */ +#define RFCSR32_TX_AGC_FC FIELD8(0xf8) + +/* RFCSR 36 bits for RF3053 */ +#define RFCSR36_RF_BS FIELD8(0x80) + +/* + * RFCSR 38: + */ +#define RFCSR38_RX_LO1_EN FIELD8(0x20) + +/* + * RFCSR 39: + */ +#define RFCSR39_RX_DIV FIELD8(0x40) +#define RFCSR39_RX_LO2_EN FIELD8(0x80) + +/* + * RFCSR 49: + */ +#define RFCSR49_TX FIELD8(0x3f) +#define RFCSR49_EP FIELD8(0xc0) +/* bits for RT3593 */ +#define RFCSR49_TX_LO1_IC FIELD8(0x1c) +#define RFCSR49_TX_DIV FIELD8(0x20) + +/* + * RFCSR 50: + */ +#define RFCSR50_TX FIELD8(0x3f) +#define RFCSR50_EP FIELD8(0xc0) +/* bits for RT3593 */ +#define RFCSR50_TX_LO1_EN FIELD8(0x20) +#define RFCSR50_TX_LO2_EN FIELD8(0x10) + +/* RFCSR 51 */ +/* bits for RT3593 */ +#define RFCSR51_BITS01 FIELD8(0x03) +#define RFCSR51_BITS24 FIELD8(0x1c) +#define RFCSR51_BITS57 FIELD8(0xe0) + +#define RFCSR53_TX_POWER FIELD8(0x3f) +#define RFCSR53_UNKNOWN FIELD8(0xc0) + +#define RFCSR54_TX_POWER FIELD8(0x3f) +#define RFCSR54_UNKNOWN FIELD8(0xc0) + +#define RFCSR55_TX_POWER FIELD8(0x3f) +#define RFCSR55_UNKNOWN FIELD8(0xc0) + +#define RFCSR57_DRV_CC FIELD8(0xfc) + + +/* + * RF registers + */ + +/* + * RF 2 + */ +#define RF2_ANTENNA_RX2 FIELD32(0x00000040) +#define RF2_ANTENNA_TX1 FIELD32(0x00004000) +#define RF2_ANTENNA_RX1 FIELD32(0x00020000) + +/* + * RF 3 + */ +#define RF3_TXPOWER_G FIELD32(0x00003e00) +#define RF3_TXPOWER_A_7DBM_BOOST FIELD32(0x00000200) +#define RF3_TXPOWER_A FIELD32(0x00003c00) + +/* + * RF 4 + */ +#define RF4_TXPOWER_G FIELD32(0x000007c0) +#define RF4_TXPOWER_A_7DBM_BOOST FIELD32(0x00000040) +#define RF4_TXPOWER_A FIELD32(0x00000780) +#define RF4_FREQ_OFFSET FIELD32(0x001f8000) +#define RF4_HT40 FIELD32(0x00200000) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +enum rt2800_eeprom_word { + EEPROM_CHIP_ID = 0, + EEPROM_VERSION, + EEPROM_MAC_ADDR_0, + EEPROM_MAC_ADDR_1, + EEPROM_MAC_ADDR_2, + EEPROM_NIC_CONF0, + EEPROM_NIC_CONF1, + EEPROM_FREQ, + EEPROM_LED_AG_CONF, + EEPROM_LED_ACT_CONF, + EEPROM_LED_POLARITY, + EEPROM_NIC_CONF2, + EEPROM_LNA, + EEPROM_RSSI_BG, + EEPROM_RSSI_BG2, + EEPROM_TXMIXER_GAIN_BG, + EEPROM_RSSI_A, + EEPROM_RSSI_A2, + EEPROM_TXMIXER_GAIN_A, + EEPROM_EIRP_MAX_TX_POWER, + EEPROM_TXPOWER_DELTA, + EEPROM_TXPOWER_BG1, + EEPROM_TXPOWER_BG2, + EEPROM_TSSI_BOUND_BG1, + EEPROM_TSSI_BOUND_BG2, + EEPROM_TSSI_BOUND_BG3, + EEPROM_TSSI_BOUND_BG4, + EEPROM_TSSI_BOUND_BG5, + EEPROM_TXPOWER_A1, + EEPROM_TXPOWER_A2, + EEPROM_TSSI_BOUND_A1, + EEPROM_TSSI_BOUND_A2, + EEPROM_TSSI_BOUND_A3, + EEPROM_TSSI_BOUND_A4, + EEPROM_TSSI_BOUND_A5, + EEPROM_TXPOWER_BYRATE, + EEPROM_BBP_START, + + /* IDs for extended EEPROM format used by three-chain devices */ + EEPROM_EXT_LNA2, + EEPROM_EXT_TXPOWER_BG3, + EEPROM_EXT_TXPOWER_A3, + + /* New values must be added before this */ + EEPROM_WORD_COUNT +}; + +/* + * EEPROM Version + */ +#define EEPROM_VERSION_FAE FIELD16(0x00ff) +#define EEPROM_VERSION_VERSION FIELD16(0xff00) + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM NIC Configuration 0 + * RXPATH: 1: 1R, 2: 2R, 3: 3R + * TXPATH: 1: 1T, 2: 2T, 3: 3T + * RF_TYPE: RFIC type + */ +#define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f) +#define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0) +#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) + +/* + * EEPROM NIC Configuration 1 + * HW_RADIO: 0: disable, 1: enable + * EXTERNAL_TX_ALC: 0: disable, 1: enable + * EXTERNAL_LNA_2G: 0: disable, 1: enable + * EXTERNAL_LNA_5G: 0: disable, 1: enable + * CARDBUS_ACCEL: 0: enable, 1: disable + * BW40M_SB_2G: 0: disable, 1: enable + * BW40M_SB_5G: 0: disable, 1: enable + * WPS_PBC: 0: disable, 1: enable + * BW40M_2G: 0: enable, 1: disable + * BW40M_5G: 0: enable, 1: disable + * BROADBAND_EXT_LNA: 0: disable, 1: enable + * ANT_DIVERSITY: 00: Disable, 01: Diversity, + * 10: Main antenna, 11: Aux antenna + * INTERNAL_TX_ALC: 0: disable, 1: enable + * BT_COEXIST: 0: disable, 1: enable + * DAC_TEST: 0: disable, 1: enable + */ +#define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001) +#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) +#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) +#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008) +#define EEPROM_NIC_CONF1_CARDBUS_ACCEL FIELD16(0x0010) +#define EEPROM_NIC_CONF1_BW40M_SB_2G FIELD16(0x0020) +#define EEPROM_NIC_CONF1_BW40M_SB_5G FIELD16(0x0040) +#define EEPROM_NIC_CONF1_WPS_PBC FIELD16(0x0080) +#define EEPROM_NIC_CONF1_BW40M_2G FIELD16(0x0100) +#define EEPROM_NIC_CONF1_BW40M_5G FIELD16(0x0200) +#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400) +#define EEPROM_NIC_CONF1_ANT_DIVERSITY FIELD16(0x1800) +#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000) +#define EEPROM_NIC_CONF1_BT_COEXIST FIELD16(0x4000) +#define EEPROM_NIC_CONF1_DAC_TEST FIELD16(0x8000) + +/* + * EEPROM frequency + */ +#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) +#define EEPROM_FREQ_LED_MODE FIELD16(0x7f00) +#define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000) + +/* + * EEPROM LED + * POLARITY_RDY_G: Polarity RDY_G setting. + * POLARITY_RDY_A: Polarity RDY_A setting. + * POLARITY_ACT: Polarity ACT setting. + * POLARITY_GPIO_0: Polarity GPIO0 setting. + * POLARITY_GPIO_1: Polarity GPIO1 setting. + * POLARITY_GPIO_2: Polarity GPIO2 setting. + * POLARITY_GPIO_3: Polarity GPIO3 setting. + * POLARITY_GPIO_4: Polarity GPIO4 setting. + * LED_MODE: Led mode. + */ +#define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001) +#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) +#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) +#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) +#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) +#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) +#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) +#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) +#define EEPROM_LED_LED_MODE FIELD16(0x1f00) + +/* + * EEPROM NIC Configuration 2 + * RX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream + * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream + * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved + */ +#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) +#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) +#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) + +/* + * EEPROM LNA + */ +#define EEPROM_LNA_BG FIELD16(0x00ff) +#define EEPROM_LNA_A0 FIELD16(0xff00) + +/* + * EEPROM RSSI BG offset + */ +#define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff) +#define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00) + +/* + * EEPROM RSSI BG2 offset + */ +#define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff) +#define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00) + +/* + * EEPROM TXMIXER GAIN BG offset (note overlaps with EEPROM RSSI BG2). + */ +#define EEPROM_TXMIXER_GAIN_BG_VAL FIELD16(0x0007) + +/* + * EEPROM RSSI A offset + */ +#define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff) +#define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00) + +/* + * EEPROM RSSI A2 offset + */ +#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff) +#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00) + +/* + * EEPROM TXMIXER GAIN A offset (note overlaps with EEPROM RSSI A2). + */ +#define EEPROM_TXMIXER_GAIN_A_VAL FIELD16(0x0007) + +/* + * EEPROM EIRP Maximum TX power values(unit: dbm) + */ +#define EEPROM_EIRP_MAX_TX_POWER_2GHZ FIELD16(0x00ff) +#define EEPROM_EIRP_MAX_TX_POWER_5GHZ FIELD16(0xff00) + +/* + * EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power. + * This is delta in 40MHZ. + * VALUE: Tx Power dalta value, MAX=4(unit: dbm) + * TYPE: 1: Plus the delta value, 0: minus the delta value + * ENABLE: enable tx power compensation for 40BW + */ +#define EEPROM_TXPOWER_DELTA_VALUE_2G FIELD16(0x003f) +#define EEPROM_TXPOWER_DELTA_TYPE_2G FIELD16(0x0040) +#define EEPROM_TXPOWER_DELTA_ENABLE_2G FIELD16(0x0080) +#define EEPROM_TXPOWER_DELTA_VALUE_5G FIELD16(0x3f00) +#define EEPROM_TXPOWER_DELTA_TYPE_5G FIELD16(0x4000) +#define EEPROM_TXPOWER_DELTA_ENABLE_5G FIELD16(0x8000) + +/* + * EEPROM TXPOWER 802.11BG + */ +#define EEPROM_TXPOWER_BG_SIZE 7 +#define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * MINUS4: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -4) + * MINUS3: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -3) + */ +#define EEPROM_TSSI_BOUND_BG1_MINUS4 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG1_MINUS3 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * MINUS2: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -2) + * MINUS1: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -1) + */ +#define EEPROM_TSSI_BOUND_BG2_MINUS2 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG2_MINUS1 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * REF: Reference TSSI value, no tx power changes needed + * PLUS1: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 1) + */ +#define EEPROM_TSSI_BOUND_BG3_REF FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG3_PLUS1 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * PLUS2: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 2) + * PLUS3: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 3) + */ +#define EEPROM_TSSI_BOUND_BG4_PLUS2 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG4_PLUS3 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11BG + * PLUS4: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 4) + * AGC_STEP: Temperature compensation step. + */ +#define EEPROM_TSSI_BOUND_BG5_PLUS4 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00) + +/* + * EEPROM TXPOWER 802.11A + */ +#define EEPROM_TXPOWER_A_SIZE 6 +#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) + +/* EEPROM_TXPOWER_{A,G} fields for RT3593 */ +#define EEPROM_TXPOWER_ALC FIELD8(0x1f) +#define EEPROM_TXPOWER_FINE_CTRL FIELD8(0xe0) + +/* + * EEPROM temperature compensation boundaries 802.11A + * MINUS4: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -4) + * MINUS3: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -3) + */ +#define EEPROM_TSSI_BOUND_A1_MINUS4 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A1_MINUS3 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11A + * MINUS2: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -2) + * MINUS1: If the actual TSSI is below this boundary, tx power needs to be + * reduced by (agc_step * -1) + */ +#define EEPROM_TSSI_BOUND_A2_MINUS2 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A2_MINUS1 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11A + * REF: Reference TSSI value, no tx power changes needed + * PLUS1: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 1) + */ +#define EEPROM_TSSI_BOUND_A3_REF FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A3_PLUS1 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11A + * PLUS2: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 2) + * PLUS3: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 3) + */ +#define EEPROM_TSSI_BOUND_A4_PLUS2 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A4_PLUS3 FIELD16(0xff00) + +/* + * EEPROM temperature compensation boundaries 802.11A + * PLUS4: If the actual TSSI is above this boundary, tx power needs to be + * increased by (agc_step * 4) + * AGC_STEP: Temperature compensation step. + */ +#define EEPROM_TSSI_BOUND_A5_PLUS4 FIELD16(0x00ff) +#define EEPROM_TSSI_BOUND_A5_AGC_STEP FIELD16(0xff00) + +/* + * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode + */ +#define EEPROM_TXPOWER_BYRATE_SIZE 9 + +#define EEPROM_TXPOWER_BYRATE_RATE0 FIELD16(0x000f) +#define EEPROM_TXPOWER_BYRATE_RATE1 FIELD16(0x00f0) +#define EEPROM_TXPOWER_BYRATE_RATE2 FIELD16(0x0f00) +#define EEPROM_TXPOWER_BYRATE_RATE3 FIELD16(0xf000) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* EEPROM_EXT_LNA2 */ +#define EEPROM_EXT_LNA2_A1 FIELD16(0x00ff) +#define EEPROM_EXT_LNA2_A2 FIELD16(0xff00) + +/* + * EEPROM IQ Calibration, unlike other entries those are byte addresses. + */ + +#define EEPROM_IQ_GAIN_CAL_TX0_2G 0x130 +#define EEPROM_IQ_PHASE_CAL_TX0_2G 0x131 +#define EEPROM_IQ_GROUPDELAY_CAL_TX0_2G 0x132 +#define EEPROM_IQ_GAIN_CAL_TX1_2G 0x133 +#define EEPROM_IQ_PHASE_CAL_TX1_2G 0x134 +#define EEPROM_IQ_GROUPDELAY_CAL_TX1_2G 0x135 +#define EEPROM_IQ_GAIN_CAL_RX0_2G 0x136 +#define EEPROM_IQ_PHASE_CAL_RX0_2G 0x137 +#define EEPROM_IQ_GROUPDELAY_CAL_RX0_2G 0x138 +#define EEPROM_IQ_GAIN_CAL_RX1_2G 0x139 +#define EEPROM_IQ_PHASE_CAL_RX1_2G 0x13A +#define EEPROM_IQ_GROUPDELAY_CAL_RX1_2G 0x13B +#define EEPROM_RF_IQ_COMPENSATION_CONTROL 0x13C +#define EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CONTROL 0x13D +#define EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5G 0x144 +#define EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5G 0x145 +#define EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5G 0X146 +#define EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5G 0x147 +#define EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5G 0x148 +#define EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5G 0x149 +#define EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5G 0x14A +#define EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5G 0x14B +#define EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5G 0X14C +#define EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5G 0x14D +#define EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5G 0x14E +#define EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5G 0x14F +#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH36_TO_CH64_5G 0x150 +#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH36_TO_CH64_5G 0x151 +#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH100_TO_CH138_5G 0x152 +#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH100_TO_CH138_5G 0x153 +#define EEPROM_IQ_GROUPDELAY_CAL_TX0_CH140_TO_CH165_5G 0x154 +#define EEPROM_IQ_GROUPDELAY_CAL_TX1_CH140_TO_CH165_5G 0x155 +#define EEPROM_IQ_GAIN_CAL_RX0_CH36_TO_CH64_5G 0x156 +#define EEPROM_IQ_PHASE_CAL_RX0_CH36_TO_CH64_5G 0x157 +#define EEPROM_IQ_GAIN_CAL_RX0_CH100_TO_CH138_5G 0X158 +#define EEPROM_IQ_PHASE_CAL_RX0_CH100_TO_CH138_5G 0x159 +#define EEPROM_IQ_GAIN_CAL_RX0_CH140_TO_CH165_5G 0x15A +#define EEPROM_IQ_PHASE_CAL_RX0_CH140_TO_CH165_5G 0x15B +#define EEPROM_IQ_GAIN_CAL_RX1_CH36_TO_CH64_5G 0x15C +#define EEPROM_IQ_PHASE_CAL_RX1_CH36_TO_CH64_5G 0x15D +#define EEPROM_IQ_GAIN_CAL_RX1_CH100_TO_CH138_5G 0X15E +#define EEPROM_IQ_PHASE_CAL_RX1_CH100_TO_CH138_5G 0x15F +#define EEPROM_IQ_GAIN_CAL_RX1_CH140_TO_CH165_5G 0x160 +#define EEPROM_IQ_PHASE_CAL_RX1_CH140_TO_CH165_5G 0x161 +#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH36_TO_CH64_5G 0x162 +#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH36_TO_CH64_5G 0x163 +#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH100_TO_CH138_5G 0x164 +#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH100_TO_CH138_5G 0x165 +#define EEPROM_IQ_GROUPDELAY_CAL_RX0_CH140_TO_CH165_5G 0x166 +#define EEPROM_IQ_GROUPDELAY_CAL_RX1_CH140_TO_CH165_5G 0x167 + +/* + * MCU mailbox commands. + * MCU_SLEEP - go to power-save mode. + * arg1: 1: save as much power as possible, 0: save less power. + * status: 1: success, 2: already asleep, + * 3: maybe MAC is busy so can't finish this task. + * MCU_RADIO_OFF + * arg0: 0: do power-saving, NOT turn off radio. + */ +#define MCU_SLEEP 0x30 +#define MCU_WAKEUP 0x31 +#define MCU_RADIO_OFF 0x35 +#define MCU_CURRENT 0x36 +#define MCU_LED 0x50 +#define MCU_LED_STRENGTH 0x51 +#define MCU_LED_AG_CONF 0x52 +#define MCU_LED_ACT_CONF 0x53 +#define MCU_LED_LED_POLARITY 0x54 +#define MCU_RADAR 0x60 +#define MCU_BOOT_SIGNAL 0x72 +#define MCU_ANT_SELECT 0X73 +#define MCU_FREQ_OFFSET 0x74 +#define MCU_BBP_SIGNAL 0x80 +#define MCU_POWER_SAVE 0x83 +#define MCU_BAND_SELECT 0x91 + +/* + * MCU mailbox tokens + */ +#define TOKEN_SLEEP 1 +#define TOKEN_RADIO_OFF 2 +#define TOKEN_WAKEUP 3 + + +/* + * DMA descriptor defines. + */ + +#define TXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) +#define TXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) + +#define RXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32)) +#define RXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32)) +#define RXWI_DESC_SIZE_6WORDS (6 * sizeof(__le32)) + +/* + * TX WI structure + */ + +/* + * Word0 + * FRAG: 1 To inform TKIP engine this is a fragment. + * MIMO_PS: The remote peer is in dynamic MIMO-PS mode + * TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs + * BW: Channel bandwidth 0:20MHz, 1:40 MHz (for legacy rates this will + * duplicate the frame to both channels). + * STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED + * AMPDU: 1: this frame is eligible for AMPDU aggregation, the hw will + * aggregate consecutive frames with the same RA and QoS TID. If + * a frame A with the same RA and QoS TID but AMPDU=0 is queued + * directly after a frame B with AMPDU=1, frame A might still + * get aggregated into the AMPDU started by frame B. So, setting + * AMPDU to 0 does _not_ necessarily mean the frame is sent as + * MPDU, it can still end up in an AMPDU if the previous frame + * was tagged as AMPDU. + */ +#define TXWI_W0_FRAG FIELD32(0x00000001) +#define TXWI_W0_MIMO_PS FIELD32(0x00000002) +#define TXWI_W0_CF_ACK FIELD32(0x00000004) +#define TXWI_W0_TS FIELD32(0x00000008) +#define TXWI_W0_AMPDU FIELD32(0x00000010) +#define TXWI_W0_MPDU_DENSITY FIELD32(0x000000e0) +#define TXWI_W0_TX_OP FIELD32(0x00000300) +#define TXWI_W0_MCS FIELD32(0x007f0000) +#define TXWI_W0_BW FIELD32(0x00800000) +#define TXWI_W0_SHORT_GI FIELD32(0x01000000) +#define TXWI_W0_STBC FIELD32(0x06000000) +#define TXWI_W0_IFS FIELD32(0x08000000) +#define TXWI_W0_PHYMODE FIELD32(0xc0000000) + +/* + * Word1 + * ACK: 0: No Ack needed, 1: Ack needed + * NSEQ: 0: Don't assign hw sequence number, 1: Assign hw sequence number + * BW_WIN_SIZE: BA windows size of the recipient + * WIRELESS_CLI_ID: Client ID for WCID table access + * MPDU_TOTAL_BYTE_COUNT: Length of 802.11 frame + * PACKETID: Will be latched into the TX_STA_FIFO register once the according + * frame was processed. If multiple frames are aggregated together + * (AMPDU==1) the reported tx status will always contain the packet + * id of the first frame. 0: Don't report tx status for this frame. + * PACKETID_QUEUE: Part of PACKETID, This is the queue index (0-3) + * PACKETID_ENTRY: Part of PACKETID, THis is the queue entry index (1-3) + * This identification number is calculated by ((idx % 3) + 1). + * The (+1) is required to prevent PACKETID to become 0. + */ +#define TXWI_W1_ACK FIELD32(0x00000001) +#define TXWI_W1_NSEQ FIELD32(0x00000002) +#define TXWI_W1_BW_WIN_SIZE FIELD32(0x000000fc) +#define TXWI_W1_WIRELESS_CLI_ID FIELD32(0x0000ff00) +#define TXWI_W1_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) +#define TXWI_W1_PACKETID FIELD32(0xf0000000) +#define TXWI_W1_PACKETID_QUEUE FIELD32(0x30000000) +#define TXWI_W1_PACKETID_ENTRY FIELD32(0xc0000000) + +/* + * Word2 + */ +#define TXWI_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + */ +#define TXWI_W3_EIV FIELD32(0xffffffff) + +/* + * RX WI structure + */ + +/* + * Word0 + */ +#define RXWI_W0_WIRELESS_CLI_ID FIELD32(0x000000ff) +#define RXWI_W0_KEY_INDEX FIELD32(0x00000300) +#define RXWI_W0_BSSID FIELD32(0x00001c00) +#define RXWI_W0_UDF FIELD32(0x0000e000) +#define RXWI_W0_MPDU_TOTAL_BYTE_COUNT FIELD32(0x0fff0000) +#define RXWI_W0_TID FIELD32(0xf0000000) + +/* + * Word1 + */ +#define RXWI_W1_FRAG FIELD32(0x0000000f) +#define RXWI_W1_SEQUENCE FIELD32(0x0000fff0) +#define RXWI_W1_MCS FIELD32(0x007f0000) +#define RXWI_W1_BW FIELD32(0x00800000) +#define RXWI_W1_SHORT_GI FIELD32(0x01000000) +#define RXWI_W1_STBC FIELD32(0x06000000) +#define RXWI_W1_PHYMODE FIELD32(0xc0000000) + +/* + * Word2 + */ +#define RXWI_W2_RSSI0 FIELD32(0x000000ff) +#define RXWI_W2_RSSI1 FIELD32(0x0000ff00) +#define RXWI_W2_RSSI2 FIELD32(0x00ff0000) + +/* + * Word3 + */ +#define RXWI_W3_SNR0 FIELD32(0x000000ff) +#define RXWI_W3_SNR1 FIELD32(0x0000ff00) + +/* + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. + */ +#define MIN_G_TXPOWER 0 +#define MIN_A_TXPOWER -7 +#define MAX_G_TXPOWER 31 +#define MAX_A_TXPOWER 15 +#define DEFAULT_TXPOWER 5 + +#define MIN_A_TXPOWER_3593 0 +#define MAX_A_TXPOWER_3593 31 + +#define TXPOWER_G_FROM_DEV(__txpower) \ + ((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_A_FROM_DEV(__txpower) \ + ((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +/* + * Board's maximun TX power limitation + */ +#define EIRP_MAX_TX_POWER_LIMIT 0x50 + +/* + * Number of TBTT intervals after which we have to adjust + * the hw beacon timer. + */ +#define BCN_TBTT_OFFSET 64 + +/* + * RT2800 driver data structure + */ +struct rt2800_drv_data { + u8 calibration_bw20; + u8 calibration_bw40; + u8 bbp25; + u8 bbp26; + u8 txmixer_gain_24g; + u8 txmixer_gain_5g; + unsigned int tbtt_tick; +}; + +#endif /* RT2800_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c new file mode 100644 index 00000000000..c17fcf27272 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -0,0 +1,8007 @@ +/* + Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> + Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> + Copyright (C) 2009 Gertjan van Wingerde <gwingerde@gmail.com> + + Based on the original rt2800pci.c and rt2800usb.c. + Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2800lib + Abstract: rt2800 generic device routines. + */ + +#include <linux/crc-ccitt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "rt2x00.h" +#include "rt2800lib.h" +#include "rt2800.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2800_register_read and rt2800_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + * The _lock versions must be used if you already hold the csr_mutex + */ +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2800_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg)) +#define WAIT_FOR_RFCSR(__dev, __reg) \ + rt2800_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2800_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg)) +#define WAIT_FOR_MCU(__dev, __reg) \ + rt2800_regbusy_read((__dev), H2M_MAILBOX_CSR, \ + H2M_MAILBOX_CSR_OWNER, (__reg)) + +static inline bool rt2800_is_305x_soc(struct rt2x00_dev *rt2x00dev) +{ + /* check for rt2872 on SoC */ + if (!rt2x00_is_soc(rt2x00dev) || + !rt2x00_rt(rt2x00dev, RT2872)) + return false; + + /* we know for sure that these rf chipsets are used on rt305x boards */ + if (rt2x00_rf(rt2x00dev, RF3020) || + rt2x00_rf(rt2x00dev, RF3021) || + rt2x00_rf(rt2x00dev, RF3022)) + return true; + + rt2x00_warn(rt2x00dev, "Unknown RF chipset on rt305x\n"); + return false; +} + +static void rt2800_bbp_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBP_CSR_CFG_VALUE, value); + rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); + rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 0); + rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); + + rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_bbp_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, BBP_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, BBP_CSR_CFG_BUSY, 1); + rt2x00_set_field32(®, BBP_CSR_CFG_READ_CONTROL, 1); + rt2x00_set_field32(®, BBP_CSR_CFG_BBP_RW_MODE, 1); + + rt2800_register_write_lock(rt2x00dev, BBP_CSR_CFG, reg); + + WAIT_FOR_BBP(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, BBP_CSR_CFG_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u8 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RFCSR becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_DATA, value); + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev, + const unsigned int word, u8 *value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RFCSR becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. + */ + if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 0); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + + WAIT_FOR_RFCSR(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev, + const unsigned int word, const u32 value) +{ + u32 reg; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG0_REG_VALUE_BW, value); + rt2x00_set_field32(®, RF_CSR_CFG0_STANDBYMODE, 0); + rt2x00_set_field32(®, RF_CSR_CFG0_SEL, 0); + rt2x00_set_field32(®, RF_CSR_CFG0_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG0, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = { + [EEPROM_CHIP_ID] = 0x0000, + [EEPROM_VERSION] = 0x0001, + [EEPROM_MAC_ADDR_0] = 0x0002, + [EEPROM_MAC_ADDR_1] = 0x0003, + [EEPROM_MAC_ADDR_2] = 0x0004, + [EEPROM_NIC_CONF0] = 0x001a, + [EEPROM_NIC_CONF1] = 0x001b, + [EEPROM_FREQ] = 0x001d, + [EEPROM_LED_AG_CONF] = 0x001e, + [EEPROM_LED_ACT_CONF] = 0x001f, + [EEPROM_LED_POLARITY] = 0x0020, + [EEPROM_NIC_CONF2] = 0x0021, + [EEPROM_LNA] = 0x0022, + [EEPROM_RSSI_BG] = 0x0023, + [EEPROM_RSSI_BG2] = 0x0024, + [EEPROM_TXMIXER_GAIN_BG] = 0x0024, /* overlaps with RSSI_BG2 */ + [EEPROM_RSSI_A] = 0x0025, + [EEPROM_RSSI_A2] = 0x0026, + [EEPROM_TXMIXER_GAIN_A] = 0x0026, /* overlaps with RSSI_A2 */ + [EEPROM_EIRP_MAX_TX_POWER] = 0x0027, + [EEPROM_TXPOWER_DELTA] = 0x0028, + [EEPROM_TXPOWER_BG1] = 0x0029, + [EEPROM_TXPOWER_BG2] = 0x0030, + [EEPROM_TSSI_BOUND_BG1] = 0x0037, + [EEPROM_TSSI_BOUND_BG2] = 0x0038, + [EEPROM_TSSI_BOUND_BG3] = 0x0039, + [EEPROM_TSSI_BOUND_BG4] = 0x003a, + [EEPROM_TSSI_BOUND_BG5] = 0x003b, + [EEPROM_TXPOWER_A1] = 0x003c, + [EEPROM_TXPOWER_A2] = 0x0053, + [EEPROM_TSSI_BOUND_A1] = 0x006a, + [EEPROM_TSSI_BOUND_A2] = 0x006b, + [EEPROM_TSSI_BOUND_A3] = 0x006c, + [EEPROM_TSSI_BOUND_A4] = 0x006d, + [EEPROM_TSSI_BOUND_A5] = 0x006e, + [EEPROM_TXPOWER_BYRATE] = 0x006f, + [EEPROM_BBP_START] = 0x0078, +}; + +static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = { + [EEPROM_CHIP_ID] = 0x0000, + [EEPROM_VERSION] = 0x0001, + [EEPROM_MAC_ADDR_0] = 0x0002, + [EEPROM_MAC_ADDR_1] = 0x0003, + [EEPROM_MAC_ADDR_2] = 0x0004, + [EEPROM_NIC_CONF0] = 0x001a, + [EEPROM_NIC_CONF1] = 0x001b, + [EEPROM_NIC_CONF2] = 0x001c, + [EEPROM_EIRP_MAX_TX_POWER] = 0x0020, + [EEPROM_FREQ] = 0x0022, + [EEPROM_LED_AG_CONF] = 0x0023, + [EEPROM_LED_ACT_CONF] = 0x0024, + [EEPROM_LED_POLARITY] = 0x0025, + [EEPROM_LNA] = 0x0026, + [EEPROM_EXT_LNA2] = 0x0027, + [EEPROM_RSSI_BG] = 0x0028, + [EEPROM_RSSI_BG2] = 0x0029, + [EEPROM_RSSI_A] = 0x002a, + [EEPROM_RSSI_A2] = 0x002b, + [EEPROM_TXPOWER_BG1] = 0x0030, + [EEPROM_TXPOWER_BG2] = 0x0037, + [EEPROM_EXT_TXPOWER_BG3] = 0x003e, + [EEPROM_TSSI_BOUND_BG1] = 0x0045, + [EEPROM_TSSI_BOUND_BG2] = 0x0046, + [EEPROM_TSSI_BOUND_BG3] = 0x0047, + [EEPROM_TSSI_BOUND_BG4] = 0x0048, + [EEPROM_TSSI_BOUND_BG5] = 0x0049, + [EEPROM_TXPOWER_A1] = 0x004b, + [EEPROM_TXPOWER_A2] = 0x0065, + [EEPROM_EXT_TXPOWER_A3] = 0x007f, + [EEPROM_TSSI_BOUND_A1] = 0x009a, + [EEPROM_TSSI_BOUND_A2] = 0x009b, + [EEPROM_TSSI_BOUND_A3] = 0x009c, + [EEPROM_TSSI_BOUND_A4] = 0x009d, + [EEPROM_TSSI_BOUND_A5] = 0x009e, + [EEPROM_TXPOWER_BYRATE] = 0x00a0, +}; + +static unsigned int rt2800_eeprom_word_index(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word) +{ + const unsigned int *map; + unsigned int index; + + if (WARN_ONCE(word >= EEPROM_WORD_COUNT, + "%s: invalid EEPROM word %d\n", + wiphy_name(rt2x00dev->hw->wiphy), word)) + return 0; + + if (rt2x00_rt(rt2x00dev, RT3593)) + map = rt2800_eeprom_map_ext; + else + map = rt2800_eeprom_map; + + index = map[word]; + + /* Index 0 is valid only for EEPROM_CHIP_ID. + * Otherwise it means that the offset of the + * given word is not initialized in the map, + * or that the field is not usable on the + * actual chipset. + */ + WARN_ONCE(word != EEPROM_CHIP_ID && index == 0, + "%s: invalid access of EEPROM word %d\n", + wiphy_name(rt2x00dev->hw->wiphy), word); + + return index; +} + +static void *rt2800_eeprom_addr(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + return rt2x00_eeprom_addr(rt2x00dev, index); +} + +static void rt2800_eeprom_read(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word, u16 *data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + rt2x00_eeprom_read(rt2x00dev, index, data); +} + +static void rt2800_eeprom_write(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word word, u16 data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, word); + rt2x00_eeprom_write(rt2x00dev, index, data); +} + +static void rt2800_eeprom_read_from_array(struct rt2x00_dev *rt2x00dev, + const enum rt2800_eeprom_word array, + unsigned int offset, + u16 *data) +{ + unsigned int index; + + index = rt2800_eeprom_word_index(rt2x00dev, array); + rt2x00_eeprom_read(rt2x00dev, index + offset, data); +} + +static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + int i, count; + + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + if (rt2x00_get_field32(reg, WLAN_EN)) + return 0; + + rt2x00_set_field32(®, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff); + rt2x00_set_field32(®, FRC_WL_ANT_SET, 1); + rt2x00_set_field32(®, WLAN_CLK_EN, 0); + rt2x00_set_field32(®, WLAN_EN, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + + udelay(REGISTER_BUSY_DELAY); + + count = 0; + do { + /* + * Check PLL_LD & XTAL_RDY. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, CMB_CTRL, ®); + if (rt2x00_get_field32(reg, PLL_LD) && + rt2x00_get_field32(reg, XTAL_RDY)) + break; + udelay(REGISTER_BUSY_DELAY); + } + + if (i >= REGISTER_BUSY_COUNT) { + + if (count >= 10) + return -EIO; + + rt2800_register_write(rt2x00dev, 0x58, 0x018); + udelay(REGISTER_BUSY_DELAY); + rt2800_register_write(rt2x00dev, 0x58, 0x418); + udelay(REGISTER_BUSY_DELAY); + rt2800_register_write(rt2x00dev, 0x58, 0x618); + udelay(REGISTER_BUSY_DELAY); + count++; + } else { + count = 0; + } + + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 0); + rt2x00_set_field32(®, WLAN_CLK_EN, 1); + rt2x00_set_field32(®, WLAN_RESET, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + udelay(10); + rt2x00_set_field32(®, WLAN_RESET, 0); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + udelay(10); + rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff); + } while (count != 0); + + return 0; +} + +void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, + const u8 command, const u8 token, + const u8 arg0, const u8 arg1) +{ + u32 reg; + + /* + * SOC devices don't support MCU requests. + */ + if (rt2x00_is_soc(rt2x00dev)) + return; + + mutex_lock(&rt2x00dev->csr_mutex); + + /* + * Wait until the MCU becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_MCU(rt2x00dev, ®)) { + rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); + rt2800_register_write_lock(rt2x00dev, H2M_MAILBOX_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); + rt2800_register_write_lock(rt2x00dev, HOST_CMD_CSR, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); +} +EXPORT_SYMBOL_GPL(rt2800_mcu_request); + +int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i = 0; + u32 reg; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg && reg != ~0) + return 0; + msleep(1); + } + + rt2x00_err(rt2x00dev, "Unstable hardware\n"); + return -EBUSY; +} +EXPORT_SYMBOL_GPL(rt2800_wait_csr_ready); + +int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + /* + * Some devices are really slow to respond here. Wait a whole second + * before timing out. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && + !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) + return 0; + + msleep(10); + } + + rt2x00_err(rt2x00dev, "WPDMA TX/RX busy [0x%08x]\n", reg); + return -EACCES; +} +EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready); + +void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); +} +EXPORT_SYMBOL_GPL(rt2800_disable_wpdma); + +void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, + unsigned short *txwi_size, + unsigned short *rxwi_size) +{ + switch (rt2x00dev->chip.rt) { + case RT3593: + *txwi_size = TXWI_DESC_SIZE_4WORDS; + *rxwi_size = RXWI_DESC_SIZE_5WORDS; + break; + + case RT5592: + *txwi_size = TXWI_DESC_SIZE_5WORDS; + *rxwi_size = RXWI_DESC_SIZE_6WORDS; + break; + + default: + *txwi_size = TXWI_DESC_SIZE_4WORDS; + *rxwi_size = RXWI_DESC_SIZE_4WORDS; + break; + } +} +EXPORT_SYMBOL_GPL(rt2800_get_txwi_rxwi_size); + +static bool rt2800_check_firmware_crc(const u8 *data, const size_t len) +{ + u16 fw_crc; + u16 crc; + + /* + * The last 2 bytes in the firmware array are the crc checksum itself, + * this means that we should never pass those 2 bytes to the crc + * algorithm. + */ + fw_crc = (data[len - 2] << 8 | data[len - 1]); + + /* + * Use the crc ccitt algorithm. + * This will return the same value as the legacy driver which + * used bit ordering reversion on the both the firmware bytes + * before input input as well as on the final output. + * Obviously using crc ccitt directly is much more efficient. + */ + crc = crc_ccitt(~0, data, len - 2); + + /* + * There is a small difference between the crc-itu-t + bitrev and + * the crc-ccitt crc calculation. In the latter method the 2 bytes + * will be swapped, use swab16 to convert the crc to the correct + * value. + */ + crc = swab16(crc); + + return fw_crc == crc; +} + +int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + size_t offset = 0; + size_t fw_len; + bool multiple; + + /* + * PCI(e) & SOC devices require firmware with a length + * of 8kb. USB devices require firmware files with a length + * of 4kb. Certain USB chipsets however require different firmware, + * which Ralink only provides attached to the original firmware + * file. Thus for USB devices, firmware files have a length + * which is a multiple of 4kb. The firmware for rt3290 chip also + * have a length which is a multiple of 4kb. + */ + if (rt2x00_is_usb(rt2x00dev) || rt2x00_rt(rt2x00dev, RT3290)) + fw_len = 4096; + else + fw_len = 8192; + + multiple = true; + /* + * Validate the firmware length + */ + if (len != fw_len && (!multiple || (len % fw_len) != 0)) + return FW_BAD_LENGTH; + + /* + * Check if the chipset requires one of the upper parts + * of the firmware. + */ + if (rt2x00_is_usb(rt2x00dev) && + !rt2x00_rt(rt2x00dev, RT2860) && + !rt2x00_rt(rt2x00dev, RT2872) && + !rt2x00_rt(rt2x00dev, RT3070) && + ((len / fw_len) == 1)) + return FW_BAD_VERSION; + + /* + * 8kb firmware files must be checked as if it were + * 2 separate firmware files. + */ + while (offset < len) { + if (!rt2800_check_firmware_crc(data + offset, fw_len)) + return FW_BAD_CRC; + + offset += fw_len; + } + + return FW_OK; +} +EXPORT_SYMBOL_GPL(rt2800_check_firmware); + +int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + unsigned int i; + u32 reg; + int retval; + + if (rt2x00_rt(rt2x00dev, RT3290)) { + retval = rt2800_enable_wlan_rt3290(rt2x00dev); + if (retval) + return -EBUSY; + } + + /* + * If driver doesn't wake up firmware here, + * rt2800_load_firmware will hang forever when interface is up again. + */ + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000); + + /* + * Wait for stable hardware. + */ + if (rt2800_wait_csr_ready(rt2x00dev)) + return -EBUSY; + + if (rt2x00_is_pci(rt2x00dev)) { + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_register_read(rt2x00dev, AUX_CTRL, ®); + rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); + rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); + rt2800_register_write(rt2x00dev, AUX_CTRL, reg); + } + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002); + } + + rt2800_disable_wpdma(rt2x00dev); + + /* + * Write firmware to the device. + */ + rt2800_drv_write_firmware(rt2x00dev, data, len); + + /* + * Wait for device to stabilize. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, ®); + if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY)) + break; + msleep(1); + } + + if (i == REGISTER_BUSY_COUNT) { + rt2x00_err(rt2x00dev, "PBF system register not ready\n"); + return -EBUSY; + } + + /* + * Disable DMA, will be reenabled later when enabling + * the radio. + */ + rt2800_disable_wpdma(rt2x00dev); + + /* + * Initialize firmware. + */ + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0); + rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); + } + msleep(1); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_load_firmware); + +void rt2800_write_tx_data(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + __le32 *txwi = rt2800_drv_get_txwi(entry); + u32 word; + int i; + + /* + * Initialize TX Info descriptor + */ + rt2x00_desc_read(txwi, 0, &word); + rt2x00_set_field32(&word, TXWI_W0_FRAG, + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, + test_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0); + rt2x00_set_field32(&word, TXWI_W0_TS, + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_AMPDU, + test_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_MPDU_DENSITY, + txdesc->u.ht.mpdu_density); + rt2x00_set_field32(&word, TXWI_W0_TX_OP, txdesc->u.ht.txop); + rt2x00_set_field32(&word, TXWI_W0_MCS, txdesc->u.ht.mcs); + rt2x00_set_field32(&word, TXWI_W0_BW, + test_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_SHORT_GI, + test_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W0_STBC, txdesc->u.ht.stbc); + rt2x00_set_field32(&word, TXWI_W0_PHYMODE, txdesc->rate_mode); + rt2x00_desc_write(txwi, 0, word); + + rt2x00_desc_read(txwi, 1, &word); + rt2x00_set_field32(&word, TXWI_W1_ACK, + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W1_NSEQ, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); + rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->u.ht.ba_size); + rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID, + test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ? + txdesc->key_idx : txdesc->u.ht.wcid); + rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT, + txdesc->length); + rt2x00_set_field32(&word, TXWI_W1_PACKETID_QUEUE, entry->queue->qid); + rt2x00_set_field32(&word, TXWI_W1_PACKETID_ENTRY, (entry->entry_idx % 3) + 1); + rt2x00_desc_write(txwi, 1, word); + + /* + * Always write 0 to IV/EIV fields (word 2 and 3), hardware will insert + * the IV from the IVEIV register when TXD_W3_WIV is set to 0. + * When TXD_W3_WIV is set to 1 it will use the IV data + * from the descriptor. The TXWI_W1_WIRELESS_CLI_ID indicates which + * crypto entry in the registers should be used to encrypt the frame. + * + * Nulify all remaining words as well, we don't know how to program them. + */ + for (i = 2; i < entry->queue->winfo_size / sizeof(__le32); i++) + _rt2x00_desc_write(txwi, i, 0); +} +EXPORT_SYMBOL_GPL(rt2800_write_tx_data); + +static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2) +{ + s8 rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0); + s8 rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1); + s8 rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2); + u16 eeprom; + u8 offset0; + u8 offset1; + u8 offset2; + + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom); + offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0); + offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); + offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom); + offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0); + offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1); + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); + offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2); + } + + /* + * Convert the value from the descriptor into the RSSI value + * If the value in the descriptor is 0, it is considered invalid + * and the default (extremely low) rssi value is assumed + */ + rssi0 = (rssi0) ? (-12 - offset0 - rt2x00dev->lna_gain - rssi0) : -128; + rssi1 = (rssi1) ? (-12 - offset1 - rt2x00dev->lna_gain - rssi1) : -128; + rssi2 = (rssi2) ? (-12 - offset2 - rt2x00dev->lna_gain - rssi2) : -128; + + /* + * mac80211 only accepts a single RSSI value. Calculating the + * average doesn't deliver a fair answer either since -60:-60 would + * be considered equally good as -50:-70 while the second is the one + * which gives less energy... + */ + rssi0 = max(rssi0, rssi1); + return (int)max(rssi0, rssi2); +} + +void rt2800_process_rxwi(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + __le32 *rxwi = (__le32 *) entry->skb->data; + u32 word; + + rt2x00_desc_read(rxwi, 0, &word); + + rxdesc->cipher = rt2x00_get_field32(word, RXWI_W0_UDF); + rxdesc->size = rt2x00_get_field32(word, RXWI_W0_MPDU_TOTAL_BYTE_COUNT); + + rt2x00_desc_read(rxwi, 1, &word); + + if (rt2x00_get_field32(word, RXWI_W1_SHORT_GI)) + rxdesc->flags |= RX_FLAG_SHORT_GI; + + if (rt2x00_get_field32(word, RXWI_W1_BW)) + rxdesc->flags |= RX_FLAG_40MHZ; + + /* + * Detect RX rate, always use MCS as signal type. + */ + rxdesc->dev_flags |= RXDONE_SIGNAL_MCS; + rxdesc->signal = rt2x00_get_field32(word, RXWI_W1_MCS); + rxdesc->rate_mode = rt2x00_get_field32(word, RXWI_W1_PHYMODE); + + /* + * Mask of 0x8 bit to remove the short preamble flag. + */ + if (rxdesc->rate_mode == RATE_MODE_CCK) + rxdesc->signal &= ~0x8; + + rt2x00_desc_read(rxwi, 2, &word); + + /* + * Convert descriptor AGC value to RSSI value. + */ + rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word); + /* + * Remove RXWI descriptor from start of the buffer. + */ + skb_pull(entry->skb, entry->queue->winfo_size); +} +EXPORT_SYMBOL_GPL(rt2800_process_rxwi); + +void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct txdone_entry_desc txdesc; + u32 word; + u16 mcs, real_mcs; + int aggr, ampdu; + + /* + * Obtain the status about this packet. + */ + txdesc.flags = 0; + rt2x00_desc_read(txwi, 0, &word); + + mcs = rt2x00_get_field32(word, TXWI_W0_MCS); + ampdu = rt2x00_get_field32(word, TXWI_W0_AMPDU); + + real_mcs = rt2x00_get_field32(status, TX_STA_FIFO_MCS); + aggr = rt2x00_get_field32(status, TX_STA_FIFO_TX_AGGRE); + + /* + * If a frame was meant to be sent as a single non-aggregated MPDU + * but ended up in an aggregate the used tx rate doesn't correlate + * with the one specified in the TXWI as the whole aggregate is sent + * with the same rate. + * + * For example: two frames are sent to rt2x00, the first one sets + * AMPDU=1 and requests MCS7 whereas the second frame sets AMDPU=0 + * and requests MCS15. If the hw aggregates both frames into one + * AMDPU the tx status for both frames will contain MCS7 although + * the frame was sent successfully. + * + * Hence, replace the requested rate with the real tx rate to not + * confuse the rate control algortihm by providing clearly wrong + * data. + */ + if (unlikely(aggr == 1 && ampdu == 0 && real_mcs != mcs)) { + skbdesc->tx_rate_idx = real_mcs; + mcs = real_mcs; + } + + if (aggr == 1 || ampdu == 1) + __set_bit(TXDONE_AMPDU, &txdesc.flags); + + /* + * Ralink has a retry mechanism using a global fallback + * table. We setup this fallback table to try the immediate + * lower rate for all rates. In the TX_STA_FIFO, the MCS field + * always contains the MCS used for the last transmission, be + * it successful or not. + */ + if (rt2x00_get_field32(status, TX_STA_FIFO_TX_SUCCESS)) { + /* + * Transmission succeeded. The number of retries is + * mcs - real_mcs + */ + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0); + } else { + /* + * Transmission failed. The number of retries is + * always 7 in this case (for a total number of 8 + * frames sent). + */ + __set_bit(TXDONE_FAILURE, &txdesc.flags); + txdesc.retry = rt2x00dev->long_retry; + } + + /* + * the frame was retried at least once + * -> hw used fallback rates + */ + if (txdesc.retry) + __set_bit(TXDONE_FALLBACK, &txdesc.flags); + + rt2x00lib_txdone(entry, &txdesc); +} +EXPORT_SYMBOL_GPL(rt2800_txdone_entry); + +static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev, + unsigned int index) +{ + return HW_BEACON_BASE(index); +} + +static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev, + unsigned int index) +{ + return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index)); +} + +void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + unsigned int beacon_base; + unsigned int padding_len; + u32 orig_reg, reg; + const int txwi_desc_size = entry->queue->winfo_size; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + orig_reg = reg; + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + /* + * Add space for the TXWI in front of the skb. + */ + memset(skb_push(entry->skb, txwi_desc_size), 0, txwi_desc_size); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->flags |= SKBDESC_DESC_IN_SKB; + skbdesc->desc = entry->skb->data; + skbdesc->desc_len = txwi_desc_size; + + /* + * Add the TXWI for the beacon to the skb. + */ + rt2800_write_tx_data(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); + + /* + * Write entire beacon with TXWI and padding to register. + */ + padding_len = roundup(entry->skb->len, 4) - entry->skb->len; + if (padding_len && skb_pad(entry->skb, padding_len)) { + rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); + /* skb freed by skb_pad() on failure */ + entry->skb = NULL; + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); + return; + } + + beacon_base = rt2800_hw_beacon_base(rt2x00dev, entry->entry_idx); + + rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, + entry->skb->len + padding_len); + + /* + * Restore beaconing state. + */ + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); + + /* + * Clean up beacon skb. + */ + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; +} +EXPORT_SYMBOL_GPL(rt2800_write_beacon); + +static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev, + unsigned int index) +{ + int i; + const int txwi_desc_size = rt2x00dev->bcn->winfo_size; + unsigned int beacon_base; + + beacon_base = rt2800_hw_beacon_base(rt2x00dev, index); + + /* + * For the Beacon base registers we only need to clear + * the whole TXWI which (when set to 0) will invalidate + * the entire beacon. + */ + for (i = 0; i < txwi_desc_size; i += sizeof(__le32)) + rt2800_register_write(rt2x00dev, beacon_base + i, 0); +} + +void rt2800_clear_beacon(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &orig_reg); + reg = orig_reg; + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + /* + * Clear beacon. + */ + rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx); + + /* + * Restore beaconing state. + */ + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); +} +EXPORT_SYMBOL_GPL(rt2800_clear_beacon); + +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +const struct rt2x00debug rt2800_rt2x00debug = { + .owner = THIS_MODULE, + .csr = { + .read = rt2800_register_read, + .write = rt2800_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .eeprom = { + /* NOTE: The local EEPROM access functions can't + * be used here, use the generic versions instead. + */ + .read = rt2x00_eeprom_read, + .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .bbp = { + .read = rt2800_bbp_read, + .write = rt2800_bbp_write, + .word_base = BBP_BASE, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, + .rf = { + .read = rt2x00_rf_read, + .write = rt2800_rf_write, + .word_base = RF_BASE, + .word_size = sizeof(u32), + .word_count = RF_SIZE / sizeof(u32), + }, + .rfcsr = { + .read = rt2800_rfcsr_read, + .write = rt2800_rfcsr_write, + .word_base = RFCSR_BASE, + .word_size = sizeof(u8), + .word_count = RFCSR_SIZE / sizeof(u8), + }, +}; +EXPORT_SYMBOL_GPL(rt2800_rt2x00debug); +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ + +int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + return rt2x00_get_field32(reg, WLAN_GPIO_IN_BIT0); + } else { + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + return rt2x00_get_field32(reg, GPIO_CTRL_VAL2); + } +} +EXPORT_SYMBOL_GPL(rt2800_rfkill_poll); + +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt2800_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + unsigned int bg_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); + unsigned int polarity = + rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, + EEPROM_FREQ_LED_POLARITY); + unsigned int ledmode = + rt2x00_get_field16(led->rt2x00dev->led_mcu_reg, + EEPROM_FREQ_LED_MODE); + u32 reg; + + /* Check for SoC (SOC devices don't support MCU requests) */ + if (rt2x00_is_soc(led->rt2x00dev)) { + rt2800_register_read(led->rt2x00dev, LED_CFG, ®); + + /* Set LED Polarity */ + rt2x00_set_field32(®, LED_CFG_LED_POLAR, polarity); + + /* Set LED Mode */ + if (led->type == LED_TYPE_RADIO) { + rt2x00_set_field32(®, LED_CFG_G_LED_MODE, + enabled ? 3 : 0); + } else if (led->type == LED_TYPE_ASSOC) { + rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, + enabled ? 3 : 0); + } else if (led->type == LED_TYPE_QUALITY) { + rt2x00_set_field32(®, LED_CFG_R_LED_MODE, + enabled ? 3 : 0); + } + + rt2800_register_write(led->rt2x00dev, LED_CFG, reg); + + } else { + if (led->type == LED_TYPE_RADIO) { + rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, + enabled ? 0x20 : 0); + } else if (led->type == LED_TYPE_ASSOC) { + rt2800_mcu_request(led->rt2x00dev, MCU_LED, 0xff, ledmode, + enabled ? (bg_mode ? 0x60 : 0xa0) : 0x20); + } else if (led->type == LED_TYPE_QUALITY) { + /* + * The brightness is divided into 6 levels (0 - 5), + * The specs tell us the following levels: + * 0, 1 ,3, 7, 15, 31 + * to determine the level in a simple way we can simply + * work with bitshifting: + * (1 << level) - 1 + */ + rt2800_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, + (1 << brightness / (LED_FULL / 6)) - 1, + polarity); + } + } +} + +static void rt2800_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt2800_brightness_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ +static void rt2800_config_wcid(struct rt2x00_dev *rt2x00dev, + const u8 *address, + int wcid) +{ + struct mac_wcid_entry wcid_entry; + u32 offset; + + offset = MAC_WCID_ENTRY(wcid); + + memset(&wcid_entry, 0xff, sizeof(wcid_entry)); + if (address) + memcpy(wcid_entry.mac, address, ETH_ALEN); + + rt2800_register_multiwrite(rt2x00dev, offset, + &wcid_entry, sizeof(wcid_entry)); +} + +static void rt2800_delete_wcid_attr(struct rt2x00_dev *rt2x00dev, int wcid) +{ + u32 offset; + offset = MAC_WCID_ATTR_ENTRY(wcid); + rt2800_register_write(rt2x00dev, offset, 0); +} + +static void rt2800_config_wcid_attr_bssidx(struct rt2x00_dev *rt2x00dev, + int wcid, u32 bssidx) +{ + u32 offset = MAC_WCID_ATTR_ENTRY(wcid); + u32 reg; + + /* + * The BSS Idx numbers is split in a main value of 3 bits, + * and a extended field for adding one additional bit to the value. + */ + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX, (bssidx & 0x7)); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_BSS_IDX_EXT, + (bssidx & 0x8) >> 3); + rt2800_register_write(rt2x00dev, offset, reg); +} + +static void rt2800_config_wcid_attr_cipher(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct mac_iveiv_entry iveiv_entry; + u32 offset; + u32 reg; + + offset = MAC_WCID_ATTR_ENTRY(key->hw_key_idx); + + if (crypto->cmd == SET_KEY) { + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, + !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); + /* + * Both the cipher as the BSS Idx numbers are split in a main + * value of 3 bits, and a extended field for adding one additional + * bit to the value. + */ + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, + (crypto->cipher & 0x7)); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER_EXT, + (crypto->cipher & 0x8) >> 3); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher); + rt2800_register_write(rt2x00dev, offset, reg); + } else { + /* Delete the cipher without touching the bssidx */ + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_KEYTAB, 0); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER, 0); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_CIPHER_EXT, 0); + rt2x00_set_field32(®, MAC_WCID_ATTRIBUTE_RX_WIUDF, 0); + rt2800_register_write(rt2x00dev, offset, reg); + } + + offset = MAC_IVEIV_ENTRY(key->hw_key_idx); + + memset(&iveiv_entry, 0, sizeof(iveiv_entry)); + if ((crypto->cipher == CIPHER_TKIP) || + (crypto->cipher == CIPHER_TKIP_NO_MIC) || + (crypto->cipher == CIPHER_AES)) + iveiv_entry.iv[3] |= 0x20; + iveiv_entry.iv[3] |= key->keyidx << 6; + rt2800_register_multiwrite(rt2x00dev, offset, + &iveiv_entry, sizeof(iveiv_entry)); +} + +int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + u32 offset; + u32 reg; + + if (crypto->cmd == SET_KEY) { + key->hw_key_idx = (4 * crypto->bssidx) + key->keyidx; + + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + offset = SHARED_KEY_ENTRY(key->hw_key_idx); + rt2800_register_multiwrite(rt2x00dev, offset, + &key_entry, sizeof(key_entry)); + } + + /* + * The cipher types are stored over multiple registers + * starting with SHARED_KEY_MODE_BASE each word will have + * 32 bits and contains the cipher types for 2 bssidx each. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + field.bit_offset = 4 * (key->hw_key_idx % 8); + field.bit_mask = 0x7 << field.bit_offset; + + offset = SHARED_KEY_MODE_ENTRY(key->hw_key_idx / 8); + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, + (crypto->cmd == SET_KEY) * crypto->cipher); + rt2800_register_write(rt2x00dev, offset, reg); + + /* + * Update WCID information + */ + rt2800_config_wcid(rt2x00dev, crypto->address, key->hw_key_idx); + rt2800_config_wcid_attr_bssidx(rt2x00dev, key->hw_key_idx, + crypto->bssidx); + rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_config_shared_key); + +static inline int rt2800_find_wcid(struct rt2x00_dev *rt2x00dev) +{ + struct mac_wcid_entry wcid_entry; + int idx; + u32 offset; + + /* + * Search for the first free WCID entry and return the corresponding + * index. + * + * Make sure the WCID starts _after_ the last possible shared key + * entry (>32). + * + * Since parts of the pairwise key table might be shared with + * the beacon frame buffers 6 & 7 we should only write into the + * first 222 entries. + */ + for (idx = 33; idx <= 222; idx++) { + offset = MAC_WCID_ENTRY(idx); + rt2800_register_multiread(rt2x00dev, offset, &wcid_entry, + sizeof(wcid_entry)); + if (is_broadcast_ether_addr(wcid_entry.mac)) + return idx; + } + + /* + * Use -1 to indicate that we don't have any more space in the WCID + * table. + */ + return -1; +} + +int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + u32 offset; + + if (crypto->cmd == SET_KEY) { + /* + * Allow key configuration only for STAs that are + * known by the hw. + */ + if (crypto->wcid < 0) + return -ENOSPC; + key->hw_key_idx = crypto->wcid; + + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + offset = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + rt2800_register_multiwrite(rt2x00dev, offset, + &key_entry, sizeof(key_entry)); + } + + /* + * Update WCID information + */ + rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_config_pairwise_key); + +int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + int wcid; + struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); + + /* + * Find next free WCID. + */ + wcid = rt2800_find_wcid(rt2x00dev); + + /* + * Store selected wcid even if it is invalid so that we can + * later decide if the STA is uploaded into the hw. + */ + sta_priv->wcid = wcid; + + /* + * No space left in the device, however, we can still communicate + * with the STA -> No error. + */ + if (wcid < 0) + return 0; + + /* + * Clean up WCID attributes and write STA address to the device. + */ + rt2800_delete_wcid_attr(rt2x00dev, wcid); + rt2800_config_wcid(rt2x00dev, sta->addr, wcid); + rt2800_config_wcid_attr_bssidx(rt2x00dev, wcid, + rt2x00lib_get_bssidx(rt2x00dev, vif)); + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_sta_add); + +int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid) +{ + /* + * Remove WCID entry, no need to clean the attributes as they will + * get renewed when the WCID is reused. + */ + rt2800_config_wcid(rt2x00dev, NULL, wcid); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_sta_remove); + +void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2800_register_read(rt2x00dev, RX_FILTER_CFG, ®); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CRC_ERROR, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PHY_ERROR, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_TO_ME, + !(filter_flags & FIF_PROMISC_IN_BSS)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_VER_ERROR, 1); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BROADCAST, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_DUPLICATE, 1); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END_ACK, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CF_END, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_ACK, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CTS, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_RTS, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, + !(filter_flags & FIF_PSPOLL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, 0); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, + !(filter_flags & FIF_CONTROL)); + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, + !(filter_flags & FIF_CONTROL)); + rt2800_register_write(rt2x00dev, RX_FILTER_CFG, reg); +} +EXPORT_SYMBOL_GPL(rt2800_config_filter); + +void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, const unsigned int flags) +{ + u32 reg; + bool update_bssid = false; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable synchronisation. + */ + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, conf->sync); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + if (conf->sync == TSF_SYNC_AP_NONE) { + /* + * Tune beacon queue transmit parameters for AP mode + */ + rt2800_register_read(rt2x00dev, TBTT_SYNC_CFG, ®); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_CWMIN, 0); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_AIFSN, 1); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_EXP_WIN, 32); + rt2x00_set_field32(®, TBTT_SYNC_CFG_TBTT_ADJUST, 0); + rt2800_register_write(rt2x00dev, TBTT_SYNC_CFG, reg); + } else { + rt2800_register_read(rt2x00dev, TBTT_SYNC_CFG, ®); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_CWMIN, 4); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_AIFSN, 2); + rt2x00_set_field32(®, TBTT_SYNC_CFG_BCN_EXP_WIN, 32); + rt2x00_set_field32(®, TBTT_SYNC_CFG_TBTT_ADJUST, 16); + rt2800_register_write(rt2x00dev, TBTT_SYNC_CFG, reg); + } + } + + if (flags & CONFIG_UPDATE_MAC) { + if (flags & CONFIG_UPDATE_TYPE && + conf->sync == TSF_SYNC_AP_NONE) { + /* + * The BSSID register has to be set to our own mac + * address in AP mode. + */ + memcpy(conf->bssid, conf->mac, sizeof(conf->mac)); + update_bssid = true; + } + + if (!is_zero_ether_addr((const u8 *)conf->mac)) { + reg = le32_to_cpu(conf->mac[1]); + rt2x00_set_field32(®, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff); + conf->mac[1] = cpu_to_le32(reg); + } + + rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0, + conf->mac, sizeof(conf->mac)); + } + + if ((flags & CONFIG_UPDATE_BSSID) || update_bssid) { + if (!is_zero_ether_addr((const u8 *)conf->bssid)) { + reg = le32_to_cpu(conf->bssid[1]); + rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_ID_MASK, 3); + rt2x00_set_field32(®, MAC_BSSID_DW1_BSS_BCN_NUM, 7); + conf->bssid[1] = cpu_to_le32(reg); + } + + rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0, + conf->bssid, sizeof(conf->bssid)); + } +} +EXPORT_SYMBOL_GPL(rt2800_config_intf); + +static void rt2800_config_ht_opmode(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp) +{ + bool any_sta_nongf = !!(erp->ht_opmode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + u8 protection = erp->ht_opmode & IEEE80211_HT_OP_MODE_PROTECTION; + u8 mm20_mode, mm40_mode, gf20_mode, gf40_mode; + u16 mm20_rate, mm40_rate, gf20_rate, gf40_rate; + u32 reg; + + /* default protection rate for HT20: OFDM 24M */ + mm20_rate = gf20_rate = 0x4004; + + /* default protection rate for HT40: duplicate OFDM 24M */ + mm40_rate = gf40_rate = 0x4084; + + switch (protection) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + /* + * All STAs in this BSS are HT20/40 but there might be + * STAs not supporting greenfield mode. + * => Disable protection for HT transmissions. + */ + mm20_mode = mm40_mode = gf20_mode = gf40_mode = 0; + + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + /* + * All STAs in this BSS are HT20 or HT20/40 but there + * might be STAs not supporting greenfield mode. + * => Protect all HT40 transmissions. + */ + mm20_mode = gf20_mode = 0; + mm40_mode = gf40_mode = 2; + + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + /* + * Nonmember protection: + * According to 802.11n we _should_ protect all + * HT transmissions (but we don't have to). + * + * But if cts_protection is enabled we _shall_ protect + * all HT transmissions using a CCK rate. + * + * And if any station is non GF we _shall_ protect + * GF transmissions. + * + * We decide to protect everything + * -> fall through to mixed mode. + */ + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + /* + * Legacy STAs are present + * => Protect all HT transmissions. + */ + mm20_mode = mm40_mode = gf20_mode = gf40_mode = 2; + + /* + * If erp protection is needed we have to protect HT + * transmissions with CCK 11M long preamble. + */ + if (erp->cts_protection) { + /* don't duplicate RTS/CTS in CCK mode */ + mm20_rate = mm40_rate = 0x0003; + gf20_rate = gf40_rate = 0x0003; + } + break; + } + + /* check for STAs not supporting greenfield mode */ + if (any_sta_nongf) + gf20_mode = gf40_mode = 2; + + /* Update HT protection config */ + rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, mm20_rate); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, mm20_mode); + rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, mm40_rate); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, mm40_mode); + rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, gf20_rate); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, gf20_mode); + rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, gf40_rate); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, gf40_mode); + rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); +} + +void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, + u32 changed) +{ + u32 reg; + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); + rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, + !!erp->short_preamble); + rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, + !!erp->short_preamble); + rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, + erp->cts_protection ? 2 : 0); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, + erp->basic_rates); + rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); + rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, + erp->slot_time); + rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); + + rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); + rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, erp->eifs); + rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); + } + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } + + if (changed & BSS_CHANGED_HT) + rt2800_config_ht_opmode(rt2x00dev, erp); +} +EXPORT_SYMBOL_GPL(rt2800_config_erp); + +static void rt2800_config_3572bt_ant(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 eeprom; + u8 led_ctrl, led_g_mode, led_r_mode; + + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + rt2x00_set_field32(®, GPIO_SWITCH_0, 1); + rt2x00_set_field32(®, GPIO_SWITCH_1, 1); + } else { + rt2x00_set_field32(®, GPIO_SWITCH_0, 0); + rt2x00_set_field32(®, GPIO_SWITCH_1, 0); + } + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + + rt2800_register_read(rt2x00dev, LED_CFG, ®); + led_g_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 3 : 0; + led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3; + if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) || + led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE); + if (led_ctrl == 0 || led_ctrl > 0x40) { + rt2x00_set_field32(®, LED_CFG_G_LED_MODE, led_g_mode); + rt2x00_set_field32(®, LED_CFG_R_LED_MODE, led_r_mode); + rt2800_register_write(rt2x00dev, LED_CFG, reg); + } else { + rt2800_mcu_request(rt2x00dev, MCU_BAND_SELECT, 0xff, + (led_g_mode << 2) | led_r_mode, 1); + } + } +} + +static void rt2800_set_ant_diversity(struct rt2x00_dev *rt2x00dev, + enum antenna ant) +{ + u32 reg; + u8 eesk_pin = (ant == ANTENNA_A) ? 1 : 0; + u8 gpio_bit3 = (ant == ANTENNA_A) ? 0 : 1; + + if (rt2x00_is_pci(rt2x00dev)) { + rt2800_register_read(rt2x00dev, E2PROM_CSR, ®); + rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, eesk_pin); + rt2800_register_write(rt2x00dev, E2PROM_CSR, reg); + } else if (rt2x00_is_usb(rt2x00dev)) + rt2800_mcu_request(rt2x00dev, MCU_ANT_SELECT, 0xff, + eesk_pin, 0); + + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL3, gpio_bit3); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); +} + +void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) +{ + u8 r1; + u8 r3; + u16 eeprom; + + rt2800_bbp_read(rt2x00dev, 1, &r1); + rt2800_bbp_read(rt2x00dev, 3, &r3); + + if (rt2x00_rt(rt2x00dev, RT3572) && + rt2x00_has_cap_bt_coexist(rt2x00dev)) + rt2800_config_3572bt_ant(rt2x00dev); + + /* + * Configure the TX antenna. + */ + switch (ant->tx_chain_num) { + case 1: + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0); + break; + case 2: + if (rt2x00_rt(rt2x00dev, RT3572) && + rt2x00_has_cap_bt_coexist(rt2x00dev)) + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1); + else + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); + break; + case 3: + rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); + break; + } + + /* + * Configure the RX antenna. + */ + switch (ant->rx_chain_num) { + case 1: + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3352) || + rt2x00_rt(rt2x00dev, RT3390)) { + rt2800_eeprom_read(rt2x00dev, + EEPROM_NIC_CONF1, &eeprom); + if (rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_ANT_DIVERSITY)) + rt2800_set_ant_diversity(rt2x00dev, + rt2x00dev->default_ant.rx); + } + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 0); + break; + case 2: + if (rt2x00_rt(rt2x00dev, RT3572) && + rt2x00_has_cap_bt_coexist(rt2x00dev)) { + rt2x00_set_field8(&r3, BBP3_RX_ADC, 1); + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, + rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); + rt2800_set_ant_diversity(rt2x00dev, ANTENNA_B); + } else { + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 1); + } + break; + case 3: + rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, 2); + break; + } + + rt2800_bbp_write(rt2x00dev, 3, r3); + rt2800_bbp_write(rt2x00dev, 1, r1); + + if (rt2x00_rt(rt2x00dev, RT3593)) { + if (ant->rx_chain_num == 1) + rt2800_bbp_write(rt2x00dev, 86, 0x00); + else + rt2800_bbp_write(rt2x00dev, 86, 0x46); + } +} +EXPORT_SYMBOL_GPL(rt2800_config_ant); + +static void rt2800_config_lna_gain(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u16 eeprom; + short lna_gain; + + if (libconf->rf.channel <= 14) { + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG); + } else if (libconf->rf.channel <= 64) { + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0); + } else if (libconf->rf.channel <= 128) { + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_EXT_LNA2_A1); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_RSSI_BG2_LNA_A1); + } + } else { + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_EXT_LNA2_A2); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom); + lna_gain = rt2x00_get_field16(eeprom, + EEPROM_RSSI_A2_LNA_A2); + } + } + + rt2x00dev->lna_gain = lna_gain; +} + +#define FREQ_OFFSET_BOUND 0x5f + +static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev) +{ + u8 freq_offset, prev_freq_offset; + u8 rfcsr, prev_rfcsr; + + freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE); + freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND); + + rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); + prev_rfcsr = rfcsr; + + rt2x00_set_field8(&rfcsr, RFCSR17_CODE, freq_offset); + if (rfcsr == prev_rfcsr) + return; + + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_mcu_request(rt2x00dev, MCU_FREQ_OFFSET, 0xff, + freq_offset, prev_rfcsr); + return; + } + + prev_freq_offset = rt2x00_get_field8(prev_rfcsr, RFCSR17_CODE); + while (prev_freq_offset != freq_offset) { + if (prev_freq_offset < freq_offset) + prev_freq_offset++; + else + prev_freq_offset--; + + rt2x00_set_field8(&rfcsr, RFCSR17_CODE, prev_freq_offset); + rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); + + usleep_range(1000, 1500); + } +} + +static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + if (rt2x00dev->default_ant.tx_chain_num == 1) + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_TX1, 1); + + if (rt2x00dev->default_ant.rx_chain_num == 1) { + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX1, 1); + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); + } else if (rt2x00dev->default_ant.rx_chain_num == 2) + rt2x00_set_field32(&rf->rf2, RF2_ANTENNA_RX2, 1); + + if (rf->channel > 14) { + /* + * When TX power is below 0, we should increase it by 7 to + * make it a positive value (Minimum value is -7). + * However this means that values between 0 and 7 have + * double meaning, and we should set a 7DBm boost flag. + */ + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST, + (info->default_power1 >= 0)); + + if (info->default_power1 < 0) + info->default_power1 += 7; + + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, info->default_power1); + + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST, + (info->default_power2 >= 0)); + + if (info->default_power2 < 0) + info->default_power2 += 7; + + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, info->default_power2); + } else { + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, info->default_power1); + rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, info->default_power2); + } + + rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf)); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt2800_rf_write(rt2x00dev, 1, rf->rf1); + rt2800_rf_write(rt2x00dev, 2, rf->rf2); + rt2800_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt2800_rf_write(rt2x00dev, 4, rf->rf4); +} + +static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 rfcsr, calib_tx, calib_rx; + + rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); + + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR3_K, rf->rf3); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->default_power1); + rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->default_power2); + rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, + rt2x00dev->default_ant.rx_chain_num <= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, + rt2x00dev->default_ant.rx_chain_num <= 2); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, + rt2x00dev->default_ant.tx_chain_num <= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, + rt2x00dev->default_ant.tx_chain_num <= 2); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); + rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); + + if (rt2x00_rt(rt2x00dev, RT3390)) { + calib_tx = conf_is_ht40(conf) ? 0x68 : 0x4f; + calib_rx = conf_is_ht40(conf) ? 0x6f : 0x4f; + } else { + if (conf_is_ht40(conf)) { + calib_tx = drv_data->calibration_bw40; + calib_rx = drv_data->calibration_bw40; + } else { + calib_tx = drv_data->calibration_bw20; + calib_rx = drv_data->calibration_bw20; + } + } + + rt2800_rfcsr_read(rt2x00dev, 24, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR24_TX_CALIB, calib_tx); + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 31, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR31_RX_CALIB, calib_rx); + rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); + rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + msleep(1); + rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); +} + +static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 rfcsr; + u32 reg; + + if (rf->channel <= 14) { + rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25); + rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26); + } else { + rt2800_bbp_write(rt2x00dev, 25, 0x09); + rt2800_bbp_write(rt2x00dev, 26, 0xff); + } + + rt2800_rfcsr_write(rt2x00dev, 2, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 3, rf->rf3); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R1, rf->rf2); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 2); + else + rt2x00_set_field8(&rfcsr, RFCSR6_TXDIV, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 5, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR5_R1, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR5_R1, 2); + rt2800_rfcsr_write(rt2x00dev, 5, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 3); + rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, + info->default_power1); + } else { + rt2x00_set_field8(&rfcsr, RFCSR12_DR0, 7); + rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, + (info->default_power1 & 0x3) | + ((info->default_power1 & 0xC) << 1)); + } + rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 3); + rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, + info->default_power2); + } else { + rt2x00_set_field8(&rfcsr, RFCSR13_DR0, 7); + rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, + (info->default_power2 & 0x3) | + ((info->default_power2 & 0xC) << 1)); + } + rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + } + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); + } else { + switch (rt2x00dev->default_ant.tx_chain_num) { + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); + break; + } + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); + break; + } + } + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); + rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); + + if (conf_is_ht40(conf)) { + rt2800_rfcsr_write(rt2x00dev, 24, drv_data->calibration_bw40); + rt2800_rfcsr_write(rt2x00dev, 31, drv_data->calibration_bw40); + } else { + rt2800_rfcsr_write(rt2x00dev, 24, drv_data->calibration_bw20); + rt2800_rfcsr_write(rt2x00dev, 31, drv_data->calibration_bw20); + } + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); + rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); + rt2800_rfcsr_write(rt2x00dev, 15, 0x53); + rfcsr = 0x4c; + rt2x00_set_field8(&rfcsr, RFCSR16_TXMIXER_GAIN, + drv_data->txmixer_gain_24g); + rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 17, 0x23); + rt2800_rfcsr_write(rt2x00dev, 19, 0x93); + rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 25, 0x15); + rt2800_rfcsr_write(rt2x00dev, 26, 0x85); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); + } else { + rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_BIT2, 1); + rt2x00_set_field8(&rfcsr, RFCSR7_BIT3, 0); + rt2x00_set_field8(&rfcsr, RFCSR7_BIT4, 1); + rt2x00_set_field8(&rfcsr, RFCSR7_BITS67, 0); + rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); + rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 11, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x43); + rfcsr = 0x7a; + rt2x00_set_field8(&rfcsr, RFCSR16_TXMIXER_GAIN, + drv_data->txmixer_gain_5g); + rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 17, 0x23); + if (rf->channel <= 64) { + rt2800_rfcsr_write(rt2x00dev, 19, 0xb7); + rt2800_rfcsr_write(rt2x00dev, 20, 0xf6); + rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); + } else if (rf->channel <= 128) { + rt2800_rfcsr_write(rt2x00dev, 19, 0x74); + rt2800_rfcsr_write(rt2x00dev, 20, 0xf4); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + } else { + rt2800_rfcsr_write(rt2x00dev, 19, 0x72); + rt2800_rfcsr_write(rt2x00dev, 20, 0xf3); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + } + rt2800_rfcsr_write(rt2x00dev, 26, 0x87); + rt2800_rfcsr_write(rt2x00dev, 27, 0x01); + rt2800_rfcsr_write(rt2x00dev, 29, 0x9f); + } + + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); + if (rf->channel <= 14) + rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); + else + rt2x00_set_field32(®, GPIO_CTRL_VAL7, 0); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + + rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); + rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); +} + +static void rt2800_config_channel_rf3053(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 txrx_agc_fc; + u8 txrx_h20m; + u8 rfcsr; + u8 bbp; + const bool txbf_enabled = false; /* TODO */ + + /* TODO: use TX{0,1,2}FinePowerControl values from EEPROM */ + rt2800_bbp_read(rt2x00dev, 109, &bbp); + rt2x00_set_field8(&bbp, BBP109_TX0_POWER, 0); + rt2x00_set_field8(&bbp, BBP109_TX1_POWER, 0); + rt2800_bbp_write(rt2x00dev, 109, bbp); + + rt2800_bbp_read(rt2x00dev, 110, &bbp); + rt2x00_set_field8(&bbp, BBP110_TX2_POWER, 0); + rt2800_bbp_write(rt2x00dev, 110, bbp); + + if (rf->channel <= 14) { + /* Restore BBP 25 & 26 for 2.4 GHz */ + rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25); + rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26); + } else { + /* Hard code BBP 25 & 26 for 5GHz */ + + /* Enable IQ Phase correction */ + rt2800_bbp_write(rt2x00dev, 25, 0x09); + /* Setup IQ Phase correction value */ + rt2800_bbp_write(rt2x00dev, 26, 0xff); + } + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3 & 0xf); + + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, (rf->rf2 & 0x3)); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_IDOH, 1); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 2); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 53, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER, + info->default_power1 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER, + ((info->default_power1 & 0x18) << 1) | + (info->default_power1 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 53, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 55, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER, + info->default_power2 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER, + ((info->default_power2 & 0x18) << 1) | + (info->default_power2 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 55, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 54, &rfcsr); + if (rf->channel <= 14) { + rfcsr = 0; + rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER, + info->default_power3 & 0x1f); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rfcsr = 0x40; + + rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER, + ((info->default_power3 & 0x18) << 1) | + (info->default_power3 & 7)); + } + rt2800_rfcsr_write(rt2x00dev, 54, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); + /* fallthrough */ + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + /* fallthrough */ + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + break; + } + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 3: + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); + /* fallthrough */ + case 2: + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + /* fallthrough */ + case 1: + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + break; + } + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + if (conf_is_ht40(conf)) { + txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw40, + RFCSR24_TX_AGC_FC); + txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw40, + RFCSR24_TX_H20M); + } else { + txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw20, + RFCSR24_TX_AGC_FC); + txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw20, + RFCSR24_TX_H20M); + } + + /* NOTE: the reference driver does not writes the new value + * back to RFCSR 32 + */ + rt2800_rfcsr_read(rt2x00dev, 32, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR32_TX_AGC_FC, txrx_agc_fc); + + if (rf->channel <= 14) + rfcsr = 0xa0; + else + rfcsr = 0x80; + rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, txrx_h20m); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, txrx_h20m); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + /* Band selection */ + rt2800_rfcsr_read(rt2x00dev, 36, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 0); + rt2800_rfcsr_write(rt2x00dev, 36, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 34, &rfcsr); + if (rf->channel <= 14) + rfcsr = 0x3c; + else + rfcsr = 0x20; + rt2800_rfcsr_write(rt2x00dev, 34, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr); + if (rf->channel <= 14) + rfcsr = 0x1a; + else + rfcsr = 0x12; + rt2800_rfcsr_write(rt2x00dev, 12, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + if (rf->channel >= 1 && rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1); + else if (rf->channel >= 36 && rf->channel <= 64) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2); + else if (rf->channel >= 100 && rf->channel <= 128) + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2); + else + rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 46, 0x60); + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 10, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 13, 0x12); + } else { + rt2800_rfcsr_write(rt2x00dev, 10, 0xd8); + rt2800_rfcsr_write(rt2x00dev, 13, 0x23); + } + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS01, 1); + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 5); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 3); + } else { + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 4); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 2); + } + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 3); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 2); + + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR49_TX_DIV, 1); + + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 57, &rfcsr); + if (rf->channel <= 14) + rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x1b); + else + rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 57, rfcsr); + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 44, 0x93); + rt2800_rfcsr_write(rt2x00dev, 52, 0x45); + } else { + rt2800_rfcsr_write(rt2x00dev, 44, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 52, 0x05); + } + + /* Initiate VCO calibration */ + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + if (rf->channel <= 14) { + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + } else { + rt2x00_set_field8(&rfcsr, RFCSR3_BIT1, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT2, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT3, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT4, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_BIT5, 1); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + } + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + + if (rf->channel >= 1 && rf->channel <= 14) { + rfcsr = 0x23; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xbb); + } else if (rf->channel >= 36 && rf->channel <= 64) { + rfcsr = 0x36; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, 0x36); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xeb); + } else if (rf->channel >= 100 && rf->channel <= 128) { + rfcsr = 0x32; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0xb3); + } else { + rfcsr = 0x30; + if (txbf_enabled) + rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 45, 0x9b); + } +} + +#define POWER_BOUND 0x27 +#define POWER_BOUND_5G 0x2b + +static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr; + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (info->default_power1 > POWER_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR49_TX, POWER_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + if (rf->channel <= 14) { + if (rf->channel == 6) + rt2800_bbp_write(rt2x00dev, 68, 0x0c); + else + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + if (rf->channel >= 1 && rf->channel <= 6) + rt2800_bbp_write(rt2x00dev, 59, 0x0f); + else if (rf->channel >= 7 && rf->channel <= 11) + rt2800_bbp_write(rt2x00dev, 59, 0x0e); + else if (rf->channel >= 12 && rf->channel <= 14) + rt2800_bbp_write(rt2x00dev, 59, 0x0d); + } +} + +static void rt2800_config_channel_rf3322(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr; + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); + + rt2800_rfcsr_write(rt2x00dev, 11, 0x42); + rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); + rt2800_rfcsr_write(rt2x00dev, 13, 0x00); + + if (info->default_power1 > POWER_BOUND) + rt2800_rfcsr_write(rt2x00dev, 47, POWER_BOUND); + else + rt2800_rfcsr_write(rt2x00dev, 47, info->default_power1); + + if (info->default_power2 > POWER_BOUND) + rt2800_rfcsr_write(rt2x00dev, 48, POWER_BOUND); + else + rt2800_rfcsr_write(rt2x00dev, 48, info->default_power2); + + rt2800_adjust_freq_offset(rt2x00dev); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + + if ( rt2x00dev->default_ant.tx_chain_num == 2 ) + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); + + if ( rt2x00dev->default_ant.rx_chain_num == 2 ) + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + else + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); + + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_write(rt2x00dev, 31, 80); +} + +static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr; + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (info->default_power1 > POWER_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR49_TX, POWER_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + if (info->default_power2 > POWER_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR50_TX, POWER_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR50_TX, + info->default_power2); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + } + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + } + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + if (rf->channel <= 14) { + int idx = rf->channel-1; + + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { + /* r55/r59 value array of channel 1~14 */ + static const char r55_bt_rev[] = {0x83, 0x83, + 0x83, 0x73, 0x73, 0x63, 0x53, 0x53, + 0x53, 0x43, 0x43, 0x43, 0x43, 0x43}; + static const char r59_bt_rev[] = {0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0b, 0x0a, 0x09, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}; + + rt2800_rfcsr_write(rt2x00dev, 55, + r55_bt_rev[idx]); + rt2800_rfcsr_write(rt2x00dev, 59, + r59_bt_rev[idx]); + } else { + static const char r59_bt[] = {0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, 0x89, + 0x88, 0x88, 0x86, 0x85, 0x84}; + + rt2800_rfcsr_write(rt2x00dev, 59, r59_bt[idx]); + } + } else { + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { + static const char r55_nonbt_rev[] = {0x23, 0x23, + 0x23, 0x23, 0x13, 0x13, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03}; + static const char r59_nonbt_rev[] = {0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x06, 0x05, 0x04, 0x04}; + + rt2800_rfcsr_write(rt2x00dev, 55, + r55_nonbt_rev[idx]); + rt2800_rfcsr_write(rt2x00dev, 59, + r59_nonbt_rev[idx]); + } else if (rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) { + static const char r59_non_bt[] = {0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8d, + 0x8a, 0x88, 0x88, 0x87, 0x87, 0x86}; + + rt2800_rfcsr_write(rt2x00dev, 59, + r59_non_bt[idx]); + } + } + } +} + +static void rt2800_config_channel_rf55xx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr, ep_reg; + u32 reg; + int power_bound; + + /* TODO */ + const bool is_11b = false; + const bool is_type_ep = false; + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, + (rf->channel > 14 || conf_is_ht40(conf)) ? 5 : 0); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + /* Order of values on rf_channel entry: N, K, mod, R */ + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1 & 0xff); + + rt2800_rfcsr_read(rt2x00dev, 9, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR9_K, rf->rf2 & 0xf); + rt2x00_set_field8(&rfcsr, RFCSR9_N, (rf->rf1 & 0x100) >> 8); + rt2x00_set_field8(&rfcsr, RFCSR9_MOD, ((rf->rf3 - 8) & 0x4) >> 2); + rt2800_rfcsr_write(rt2x00dev, 9, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf4 - 1); + rt2x00_set_field8(&rfcsr, RFCSR11_MOD, (rf->rf3 - 8) & 0x3); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + if (rf->channel <= 14) { + rt2800_rfcsr_write(rt2x00dev, 10, 0x90); + /* FIXME: RF11 owerwrite ? */ + rt2800_rfcsr_write(rt2x00dev, 11, 0x4A); + rt2800_rfcsr_write(rt2x00dev, 12, 0x52); + rt2800_rfcsr_write(rt2x00dev, 13, 0x42); + rt2800_rfcsr_write(rt2x00dev, 22, 0x40); + rt2800_rfcsr_write(rt2x00dev, 24, 0x4A); + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + rt2800_rfcsr_write(rt2x00dev, 27, 0x42); + rt2800_rfcsr_write(rt2x00dev, 36, 0x80); + rt2800_rfcsr_write(rt2x00dev, 37, 0x08); + rt2800_rfcsr_write(rt2x00dev, 38, 0x89); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1B); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0D); + rt2800_rfcsr_write(rt2x00dev, 41, 0x9B); + rt2800_rfcsr_write(rt2x00dev, 42, 0xD5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x72); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0E); + rt2800_rfcsr_write(rt2x00dev, 45, 0xA2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x6B); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 51, 0x3E); + rt2800_rfcsr_write(rt2x00dev, 52, 0x48); + rt2800_rfcsr_write(rt2x00dev, 54, 0x38); + rt2800_rfcsr_write(rt2x00dev, 56, 0xA1); + rt2800_rfcsr_write(rt2x00dev, 57, 0x00); + rt2800_rfcsr_write(rt2x00dev, 58, 0x39); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0x91); + rt2800_rfcsr_write(rt2x00dev, 62, 0x39); + + /* TODO RF27 <- tssi */ + + rfcsr = rf->channel <= 10 ? 0x07 : 0x06; + rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 59, rfcsr); + + if (is_11b) { + /* CCK */ + rt2800_rfcsr_write(rt2x00dev, 31, 0xF8); + rt2800_rfcsr_write(rt2x00dev, 32, 0xC0); + if (is_type_ep) + rt2800_rfcsr_write(rt2x00dev, 55, 0x06); + else + rt2800_rfcsr_write(rt2x00dev, 55, 0x47); + } else { + /* OFDM */ + if (is_type_ep) + rt2800_rfcsr_write(rt2x00dev, 55, 0x03); + else + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + } + + power_bound = POWER_BOUND; + ep_reg = 0x2; + } else { + rt2800_rfcsr_write(rt2x00dev, 10, 0x97); + /* FIMXE: RF11 overwrite */ + rt2800_rfcsr_write(rt2x00dev, 11, 0x40); + rt2800_rfcsr_write(rt2x00dev, 25, 0xBF); + rt2800_rfcsr_write(rt2x00dev, 27, 0x42); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x04); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 40, 0x42); + rt2800_rfcsr_write(rt2x00dev, 41, 0xBB); + rt2800_rfcsr_write(rt2x00dev, 42, 0xD7); + rt2800_rfcsr_write(rt2x00dev, 45, 0x41); + rt2800_rfcsr_write(rt2x00dev, 48, 0x00); + rt2800_rfcsr_write(rt2x00dev, 57, 0x77); + rt2800_rfcsr_write(rt2x00dev, 60, 0x05); + rt2800_rfcsr_write(rt2x00dev, 61, 0x01); + + /* TODO RF27 <- tssi */ + + if (rf->channel >= 36 && rf->channel <= 64) { + + rt2800_rfcsr_write(rt2x00dev, 12, 0x2E); + rt2800_rfcsr_write(rt2x00dev, 13, 0x22); + rt2800_rfcsr_write(rt2x00dev, 22, 0x60); + rt2800_rfcsr_write(rt2x00dev, 23, 0x7F); + if (rf->channel <= 50) + rt2800_rfcsr_write(rt2x00dev, 24, 0x09); + else if (rf->channel >= 52) + rt2800_rfcsr_write(rt2x00dev, 24, 0x07); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1C); + rt2800_rfcsr_write(rt2x00dev, 43, 0x5B); + rt2800_rfcsr_write(rt2x00dev, 44, 0X40); + rt2800_rfcsr_write(rt2x00dev, 46, 0X00); + rt2800_rfcsr_write(rt2x00dev, 51, 0xFE); + rt2800_rfcsr_write(rt2x00dev, 52, 0x0C); + rt2800_rfcsr_write(rt2x00dev, 54, 0xF8); + if (rf->channel <= 50) { + rt2800_rfcsr_write(rt2x00dev, 55, 0x06), + rt2800_rfcsr_write(rt2x00dev, 56, 0xD3); + } else if (rf->channel >= 52) { + rt2800_rfcsr_write(rt2x00dev, 55, 0x04); + rt2800_rfcsr_write(rt2x00dev, 56, 0xBB); + } + + rt2800_rfcsr_write(rt2x00dev, 58, 0x15); + rt2800_rfcsr_write(rt2x00dev, 59, 0x7F); + rt2800_rfcsr_write(rt2x00dev, 62, 0x15); + + } else if (rf->channel >= 100 && rf->channel <= 165) { + + rt2800_rfcsr_write(rt2x00dev, 12, 0x0E); + rt2800_rfcsr_write(rt2x00dev, 13, 0x42); + rt2800_rfcsr_write(rt2x00dev, 22, 0x40); + if (rf->channel <= 153) { + rt2800_rfcsr_write(rt2x00dev, 23, 0x3C); + rt2800_rfcsr_write(rt2x00dev, 24, 0x06); + } else if (rf->channel >= 155) { + rt2800_rfcsr_write(rt2x00dev, 23, 0x38); + rt2800_rfcsr_write(rt2x00dev, 24, 0x05); + } + if (rf->channel <= 138) { + rt2800_rfcsr_write(rt2x00dev, 39, 0x1A); + rt2800_rfcsr_write(rt2x00dev, 43, 0x3B); + rt2800_rfcsr_write(rt2x00dev, 44, 0x20); + rt2800_rfcsr_write(rt2x00dev, 46, 0x18); + } else if (rf->channel >= 140) { + rt2800_rfcsr_write(rt2x00dev, 39, 0x18); + rt2800_rfcsr_write(rt2x00dev, 43, 0x1B); + rt2800_rfcsr_write(rt2x00dev, 44, 0x10); + rt2800_rfcsr_write(rt2x00dev, 46, 0X08); + } + if (rf->channel <= 124) + rt2800_rfcsr_write(rt2x00dev, 51, 0xFC); + else if (rf->channel >= 126) + rt2800_rfcsr_write(rt2x00dev, 51, 0xEC); + if (rf->channel <= 138) + rt2800_rfcsr_write(rt2x00dev, 52, 0x06); + else if (rf->channel >= 140) + rt2800_rfcsr_write(rt2x00dev, 52, 0x06); + rt2800_rfcsr_write(rt2x00dev, 54, 0xEB); + if (rf->channel <= 138) + rt2800_rfcsr_write(rt2x00dev, 55, 0x01); + else if (rf->channel >= 140) + rt2800_rfcsr_write(rt2x00dev, 55, 0x00); + if (rf->channel <= 128) + rt2800_rfcsr_write(rt2x00dev, 56, 0xBB); + else if (rf->channel >= 130) + rt2800_rfcsr_write(rt2x00dev, 56, 0xAB); + if (rf->channel <= 116) + rt2800_rfcsr_write(rt2x00dev, 58, 0x1D); + else if (rf->channel >= 118) + rt2800_rfcsr_write(rt2x00dev, 58, 0x15); + if (rf->channel <= 138) + rt2800_rfcsr_write(rt2x00dev, 59, 0x3F); + else if (rf->channel >= 140) + rt2800_rfcsr_write(rt2x00dev, 59, 0x7C); + if (rf->channel <= 116) + rt2800_rfcsr_write(rt2x00dev, 62, 0x1D); + else if (rf->channel >= 118) + rt2800_rfcsr_write(rt2x00dev, 62, 0x15); + } + + power_bound = POWER_BOUND_5G; + ep_reg = 0x3; + } + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (info->default_power1 > power_bound) + rt2x00_set_field8(&rfcsr, RFCSR49_TX, power_bound); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); + if (is_type_ep) + rt2x00_set_field8(&rfcsr, RFCSR49_EP, ep_reg); + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + if (info->default_power2 > power_bound) + rt2x00_set_field8(&rfcsr, RFCSR50_TX, power_bound); + else + rt2x00_set_field8(&rfcsr, RFCSR50_TX, info->default_power2); + if (is_type_ep) + rt2x00_set_field8(&rfcsr, RFCSR50_EP, ep_reg); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, + rt2x00dev->default_ant.tx_chain_num >= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, + rt2x00dev->default_ant.tx_chain_num == 2); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); + + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, + rt2x00dev->default_ant.rx_chain_num >= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, + rt2x00dev->default_ant.rx_chain_num == 2); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); + + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + rt2800_rfcsr_write(rt2x00dev, 6, 0xe4); + + if (conf_is_ht40(conf)) + rt2800_rfcsr_write(rt2x00dev, 30, 0x16); + else + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + + if (!is_11b) { + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + } + + /* TODO proper frequency adjustment */ + rt2800_adjust_freq_offset(rt2x00dev); + + /* TODO merge with others */ + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + + /* BBP settings */ + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); + + rt2800_bbp_write(rt2x00dev, 79, (rf->channel <= 14) ? 0x1C : 0x18); + rt2800_bbp_write(rt2x00dev, 80, (rf->channel <= 14) ? 0x0E : 0x08); + rt2800_bbp_write(rt2x00dev, 81, (rf->channel <= 14) ? 0x3A : 0x38); + rt2800_bbp_write(rt2x00dev, 82, (rf->channel <= 14) ? 0x62 : 0x92); + + /* GLRT band configuration */ + rt2800_bbp_write(rt2x00dev, 195, 128); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0xE0 : 0xF0); + rt2800_bbp_write(rt2x00dev, 195, 129); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x1F : 0x1E); + rt2800_bbp_write(rt2x00dev, 195, 130); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x38 : 0x28); + rt2800_bbp_write(rt2x00dev, 195, 131); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x32 : 0x20); + rt2800_bbp_write(rt2x00dev, 195, 133); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x28 : 0x7F); + rt2800_bbp_write(rt2x00dev, 195, 124); + rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x19 : 0x7F); +} + +static void rt2800_bbp_write_with_rx_chain(struct rt2x00_dev *rt2x00dev, + const unsigned int word, + const u8 value) +{ + u8 chain, reg; + + for (chain = 0; chain < rt2x00dev->default_ant.rx_chain_num; chain++) { + rt2800_bbp_read(rt2x00dev, 27, ®); + rt2x00_set_field8(®, BBP27_RX_CHAIN_SEL, chain); + rt2800_bbp_write(rt2x00dev, 27, reg); + + rt2800_bbp_write(rt2x00dev, word, value); + } +} + +static void rt2800_iq_calibrate(struct rt2x00_dev *rt2x00dev, int channel) +{ + u8 cal; + + /* TX0 IQ Gain */ + rt2800_bbp_write(rt2x00dev, 158, 0x2c); + if (channel <= 14) + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_GAIN_CAL_TX0_2G); + else if (channel >= 36 && channel <= 64) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX0_CH36_TO_CH64_5G); + else if (channel >= 100 && channel <= 138) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX0_CH100_TO_CH138_5G); + else if (channel >= 140 && channel <= 165) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX0_CH140_TO_CH165_5G); + else + cal = 0; + rt2800_bbp_write(rt2x00dev, 159, cal); + + /* TX0 IQ Phase */ + rt2800_bbp_write(rt2x00dev, 158, 0x2d); + if (channel <= 14) + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_PHASE_CAL_TX0_2G); + else if (channel >= 36 && channel <= 64) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX0_CH36_TO_CH64_5G); + else if (channel >= 100 && channel <= 138) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX0_CH100_TO_CH138_5G); + else if (channel >= 140 && channel <= 165) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX0_CH140_TO_CH165_5G); + else + cal = 0; + rt2800_bbp_write(rt2x00dev, 159, cal); + + /* TX1 IQ Gain */ + rt2800_bbp_write(rt2x00dev, 158, 0x4a); + if (channel <= 14) + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_GAIN_CAL_TX1_2G); + else if (channel >= 36 && channel <= 64) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX1_CH36_TO_CH64_5G); + else if (channel >= 100 && channel <= 138) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX1_CH100_TO_CH138_5G); + else if (channel >= 140 && channel <= 165) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_GAIN_CAL_TX1_CH140_TO_CH165_5G); + else + cal = 0; + rt2800_bbp_write(rt2x00dev, 159, cal); + + /* TX1 IQ Phase */ + rt2800_bbp_write(rt2x00dev, 158, 0x4b); + if (channel <= 14) + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_IQ_PHASE_CAL_TX1_2G); + else if (channel >= 36 && channel <= 64) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX1_CH36_TO_CH64_5G); + else if (channel >= 100 && channel <= 138) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX1_CH100_TO_CH138_5G); + else if (channel >= 140 && channel <= 165) + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_IQ_PHASE_CAL_TX1_CH140_TO_CH165_5G); + else + cal = 0; + rt2800_bbp_write(rt2x00dev, 159, cal); + + /* FIXME: possible RX0, RX1 callibration ? */ + + /* RF IQ compensation control */ + rt2800_bbp_write(rt2x00dev, 158, 0x04); + cal = rt2x00_eeprom_byte(rt2x00dev, EEPROM_RF_IQ_COMPENSATION_CONTROL); + rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0); + + /* RF IQ imbalance compensation control */ + rt2800_bbp_write(rt2x00dev, 158, 0x03); + cal = rt2x00_eeprom_byte(rt2x00dev, + EEPROM_RF_IQ_IMBALANCE_COMPENSATION_CONTROL); + rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0); +} + +static char rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev, + unsigned int channel, + char txpower) +{ + if (rt2x00_rt(rt2x00dev, RT3593)) + txpower = rt2x00_get_field8(txpower, EEPROM_TXPOWER_ALC); + + if (channel <= 14) + return clamp_t(char, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER); + + if (rt2x00_rt(rt2x00dev, RT3593)) + return clamp_t(char, txpower, MIN_A_TXPOWER_3593, + MAX_A_TXPOWER_3593); + else + return clamp_t(char, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER); +} + +static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u32 reg; + unsigned int tx_pin; + u8 bbp, rfcsr; + + info->default_power1 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power1); + info->default_power2 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power2); + if (rt2x00dev->default_ant.tx_chain_num > 2) + info->default_power3 = + rt2800_txpower_to_dev(rt2x00dev, rf->channel, + info->default_power3); + + switch (rt2x00dev->chip.rf) { + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3320: + rt2800_config_channel_rf3xxx(rt2x00dev, conf, rf, info); + break; + case RF3052: + rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info); + break; + case RF3053: + rt2800_config_channel_rf3053(rt2x00dev, conf, rf, info); + break; + case RF3290: + rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info); + break; + case RF3322: + rt2800_config_channel_rf3322(rt2x00dev, conf, rf, info); + break; + case RF3070: + case RF5360: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info); + break; + case RF5592: + rt2800_config_channel_rf55xx(rt2x00dev, conf, rf, info); + break; + default: + rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info); + } + + if (rt2x00_rf(rt2x00dev, RF3070) || + rt2x00_rf(rt2x00dev, RF3290) || + rt2x00_rf(rt2x00dev, RF3322) || + rt2x00_rf(rt2x00dev, RF5360) || + rt2x00_rf(rt2x00dev, RF5370) || + rt2x00_rf(rt2x00dev, RF5372) || + rt2x00_rf(rt2x00dev, RF5390) || + rt2x00_rf(rt2x00dev, RF5392)) { + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, 0); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, 0); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + } + + /* + * Change BBP settings + */ + if (rt2x00_rt(rt2x00dev, RT3352)) { + rt2800_bbp_write(rt2x00dev, 27, 0x0); + rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 27, 0x20); + rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); + } else if (rt2x00_rt(rt2x00dev, RT3593)) { + if (rf->channel > 14) { + /* Disable CCK Packet detection on 5GHz */ + rt2800_bbp_write(rt2x00dev, 70, 0x00); + } else { + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + } + + if (conf_is_ht40(conf)) + rt2800_bbp_write(rt2x00dev, 105, 0x04); + else + rt2800_bbp_write(rt2x00dev, 105, 0x34); + + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 77, 0x98); + } else { + rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 86, 0); + } + + if (rf->channel <= 14) { + if (!rt2x00_rt(rt2x00dev, RT5390) && + !rt2x00_rt(rt2x00dev, RT5392)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { + rt2800_bbp_write(rt2x00dev, 82, 0x62); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + } else { + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 82, 0x62); + else + rt2800_bbp_write(rt2x00dev, 82, 0x84); + rt2800_bbp_write(rt2x00dev, 75, 0x50); + } + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 83, 0x8a); + } + + } else { + if (rt2x00_rt(rt2x00dev, RT3572)) + rt2800_bbp_write(rt2x00dev, 82, 0x94); + else if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 82, 0x82); + else + rt2800_bbp_write(rt2x00dev, 82, 0xf2); + + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_bbp_write(rt2x00dev, 83, 0x9a); + + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) + rt2800_bbp_write(rt2x00dev, 75, 0x46); + else + rt2800_bbp_write(rt2x00dev, 75, 0x50); + } + + rt2800_register_read(rt2x00dev, TX_BAND_CFG, ®); + rt2x00_set_field32(®, TX_BAND_CFG_HT40_MINUS, conf_is_ht40_minus(conf)); + rt2x00_set_field32(®, TX_BAND_CFG_A, rf->channel > 14); + rt2x00_set_field32(®, TX_BAND_CFG_BG, rf->channel <= 14); + rt2800_register_write(rt2x00dev, TX_BAND_CFG, reg); + + if (rt2x00_rt(rt2x00dev, RT3572)) + rt2800_rfcsr_write(rt2x00dev, 8, 0); + + tx_pin = 0; + + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + /* Turn on tertiary PAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN, + rf->channel > 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN, + rf->channel <= 14); + /* fall-through */ + case 2: + /* Turn on secondary PAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, + rf->channel > 14); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, + rf->channel <= 14); + /* fall-through */ + case 1: + /* Turn on primary PAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, + rf->channel > 14); + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); + else + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, + rf->channel <= 14); + break; + } + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 3: + /* Turn on tertiary LNAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A2_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G2_EN, 1); + /* fall-through */ + case 2: + /* Turn on secondary LNAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A1_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G1_EN, 1); + /* fall-through */ + case 1: + /* Turn on primary LNAs */ + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_A0_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_LNA_PE_G0_EN, 1); + break; + } + + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1); + + rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + + if (rt2x00_rt(rt2x00dev, RT3572)) { + rt2800_rfcsr_write(rt2x00dev, 8, 0x80); + + /* AGC init */ + if (rf->channel <= 14) + reg = 0x1c + (2 * rt2x00dev->lna_gain); + else + reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); + + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + } + + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + + /* Band selection */ + if (rt2x00_is_usb(rt2x00dev) || + rt2x00_is_pcie(rt2x00dev)) { + /* GPIO #8 controls all paths */ + rt2x00_set_field32(®, GPIO_CTRL_DIR8, 0); + if (rf->channel <= 14) + rt2x00_set_field32(®, GPIO_CTRL_VAL8, 1); + else + rt2x00_set_field32(®, GPIO_CTRL_VAL8, 0); + } + + /* LNA PE control. */ + if (rt2x00_is_usb(rt2x00dev)) { + /* GPIO #4 controls PE0 and PE1, + * GPIO #7 controls PE2 + */ + rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); + rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); + + rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); + rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); + } else if (rt2x00_is_pcie(rt2x00dev)) { + /* GPIO #4 controls PE0, PE1 and PE2 */ + rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); + } + + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + + /* AGC init */ + if (rf->channel <= 14) + reg = 0x1c + 2 * rt2x00dev->lna_gain; + else + reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); + + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + + usleep_range(1000, 1500); + } + + if (rt2x00_rt(rt2x00dev, RT5592)) { + rt2800_bbp_write(rt2x00dev, 195, 141); + rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a); + + /* AGC init */ + reg = (rf->channel <= 14 ? 0x1c : 0x24) + 2 * rt2x00dev->lna_gain; + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + + rt2800_iq_calibrate(rt2x00dev, rf->channel); + } + + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * conf_is_ht40(conf)); + rt2800_bbp_write(rt2x00dev, 4, bbp); + + rt2800_bbp_read(rt2x00dev, 3, &bbp); + rt2x00_set_field8(&bbp, BBP3_HT40_MINUS, conf_is_ht40_minus(conf)); + rt2800_bbp_write(rt2x00dev, 3, bbp); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { + if (conf_is_ht40(conf)) { + rt2800_bbp_write(rt2x00dev, 69, 0x1a); + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + rt2800_bbp_write(rt2x00dev, 73, 0x16); + } else { + rt2800_bbp_write(rt2x00dev, 69, 0x16); + rt2800_bbp_write(rt2x00dev, 70, 0x08); + rt2800_bbp_write(rt2x00dev, 73, 0x11); + } + } + + msleep(1); + + /* + * Clear channel statistic counters + */ + rt2800_register_read(rt2x00dev, CH_IDLE_STA, ®); + rt2800_register_read(rt2x00dev, CH_BUSY_STA, ®); + rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, ®); + + /* + * Clear update flag + */ + if (rt2x00_rt(rt2x00dev, RT3352)) { + rt2800_bbp_read(rt2x00dev, 49, &bbp); + rt2x00_set_field8(&bbp, BBP49_UPDATE_FLAG, 0); + rt2800_bbp_write(rt2x00dev, 49, bbp); + } +} + +static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) +{ + u8 tssi_bounds[9]; + u8 current_tssi; + u16 eeprom; + u8 step; + int i; + + /* + * First check if temperature compensation is supported. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + if (!rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC)) + return 0; + + /* + * Read TSSI boundaries for temperature compensation from + * the EEPROM. + * + * Array idx 0 1 2 3 4 5 6 7 8 + * Matching Delta value -4 -3 -2 -1 0 +1 +2 +3 +4 + * Example TSSI bounds 0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00 + */ + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom); + tssi_bounds[0] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG1_MINUS4); + tssi_bounds[1] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG1_MINUS3); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom); + tssi_bounds[2] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG2_MINUS2); + tssi_bounds[3] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG2_MINUS1); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom); + tssi_bounds[4] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG3_REF); + tssi_bounds[5] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG3_PLUS1); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom); + tssi_bounds[6] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG4_PLUS2); + tssi_bounds[7] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG4_PLUS3); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom); + tssi_bounds[8] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG5_PLUS4); + + step = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_BG5_AGC_STEP); + } else { + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom); + tssi_bounds[0] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A1_MINUS4); + tssi_bounds[1] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A1_MINUS3); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom); + tssi_bounds[2] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A2_MINUS2); + tssi_bounds[3] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A2_MINUS1); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom); + tssi_bounds[4] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A3_REF); + tssi_bounds[5] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A3_PLUS1); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom); + tssi_bounds[6] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A4_PLUS2); + tssi_bounds[7] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A4_PLUS3); + + rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom); + tssi_bounds[8] = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A5_PLUS4); + + step = rt2x00_get_field16(eeprom, + EEPROM_TSSI_BOUND_A5_AGC_STEP); + } + + /* + * Check if temperature compensation is supported. + */ + if (tssi_bounds[4] == 0xff || step == 0xff) + return 0; + + /* + * Read current TSSI (BBP 49). + */ + rt2800_bbp_read(rt2x00dev, 49, ¤t_tssi); + + /* + * Compare TSSI value (BBP49) with the compensation boundaries + * from the EEPROM and increase or decrease tx power. + */ + for (i = 0; i <= 3; i++) { + if (current_tssi > tssi_bounds[i]) + break; + } + + if (i == 4) { + for (i = 8; i >= 5; i--) { + if (current_tssi < tssi_bounds[i]) + break; + } + } + + return (i - 4) * step; +} + +static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev, + enum ieee80211_band band) +{ + u16 eeprom; + u8 comp_en; + u8 comp_type; + int comp_value = 0; + + rt2800_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom); + + /* + * HT40 compensation not required. + */ + if (eeprom == 0xffff || + !test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + return 0; + + if (band == IEEE80211_BAND_2GHZ) { + comp_en = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_ENABLE_2G); + if (comp_en) { + comp_type = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_TYPE_2G); + comp_value = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_VALUE_2G); + if (!comp_type) + comp_value = -comp_value; + } + } else { + comp_en = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_ENABLE_5G); + if (comp_en) { + comp_type = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_TYPE_5G); + comp_value = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_DELTA_VALUE_5G); + if (!comp_type) + comp_value = -comp_value; + } + } + + return comp_value; +} + +static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev, + int power_level, int max_power) +{ + int delta; + + if (rt2x00_has_cap_power_limit(rt2x00dev)) + return 0; + + /* + * XXX: We don't know the maximum transmit power of our hardware since + * the EEPROM doesn't expose it. We only know that we are calibrated + * to 100% tx power. + * + * Hence, we assume the regulatory limit that cfg80211 calulated for + * the current channel is our maximum and if we are requested to lower + * the value we just reduce our tx power accordingly. + */ + delta = power_level - max_power; + return min(delta, 0); +} + +static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, + enum ieee80211_band band, int power_level, + u8 txpower, int delta) +{ + u16 eeprom; + u8 criterion; + u8 eirp_txpower; + u8 eirp_txpower_criterion; + u8 reg_limit; + + if (rt2x00_rt(rt2x00dev, RT3593)) + return min_t(u8, txpower, 0xc); + + if (rt2x00_has_cap_power_limit(rt2x00dev)) { + /* + * Check if eirp txpower exceed txpower_limit. + * We use OFDM 6M as criterion and its eirp txpower + * is stored at EEPROM_EIRP_MAX_TX_POWER. + * .11b data rate need add additional 4dbm + * when calculating eirp txpower. + */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + 1, &eeprom); + criterion = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE0); + + rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, + &eeprom); + + if (band == IEEE80211_BAND_2GHZ) + eirp_txpower_criterion = rt2x00_get_field16(eeprom, + EEPROM_EIRP_MAX_TX_POWER_2GHZ); + else + eirp_txpower_criterion = rt2x00_get_field16(eeprom, + EEPROM_EIRP_MAX_TX_POWER_5GHZ); + + eirp_txpower = eirp_txpower_criterion + (txpower - criterion) + + (is_rate_b ? 4 : 0) + delta; + + reg_limit = (eirp_txpower > power_level) ? + (eirp_txpower - power_level) : 0; + } else + reg_limit = 0; + + txpower = max(0, txpower + delta - reg_limit); + return min_t(u8, txpower, 0xc); +} + + +enum { + TX_PWR_CFG_0_IDX, + TX_PWR_CFG_1_IDX, + TX_PWR_CFG_2_IDX, + TX_PWR_CFG_3_IDX, + TX_PWR_CFG_4_IDX, + TX_PWR_CFG_5_IDX, + TX_PWR_CFG_6_IDX, + TX_PWR_CFG_7_IDX, + TX_PWR_CFG_8_IDX, + TX_PWR_CFG_9_IDX, + TX_PWR_CFG_0_EXT_IDX, + TX_PWR_CFG_1_EXT_IDX, + TX_PWR_CFG_2_EXT_IDX, + TX_PWR_CFG_3_EXT_IDX, + TX_PWR_CFG_4_EXT_IDX, + TX_PWR_CFG_IDX_COUNT, +}; + +static void rt2800_config_txpower_rt3593(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + u8 txpower; + u16 eeprom; + u32 regs[TX_PWR_CFG_IDX_COUNT]; + unsigned int offset; + enum ieee80211_band band = chan->band; + int delta; + int i; + + memset(regs, '\0', sizeof(regs)); + + /* TODO: adapt TX power reduction from the rt28xx code */ + + /* calculate temperature compensation delta */ + delta = rt2800_get_gain_calibration_delta(rt2x00dev); + + if (band == IEEE80211_BAND_5GHZ) + offset = 16; + else + offset = 0; + + if (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + offset += 8; + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset, &eeprom); + + /* CCK 1MBS,2MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK1_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK1_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_CCK1_CH2, txpower); + + /* CCK 5.5MBS,11MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK5_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_CCK5_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_CCK5_CH2, txpower); + + /* OFDM 6MBS,9MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM6_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM6_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_OFDM6_CH2, txpower); + + /* OFDM 12MBS,18MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM12_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX], + TX_PWR_CFG_0_OFDM12_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX], + TX_PWR_CFG_0_EXT_OFDM12_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 1, &eeprom); + + /* OFDM 24MBS,36MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM24_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM24_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_OFDM24_CH2, txpower); + + /* OFDM 48MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM48_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_OFDM48_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_OFDM48_CH2, txpower); + + /* OFDM 54MBS */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_OFDM54_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 2, &eeprom); + + /* MCS 0,1 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS0_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS0_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_MCS0_CH2, txpower); + + /* MCS 2,3 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS2_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX], + TX_PWR_CFG_1_MCS2_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX], + TX_PWR_CFG_1_EXT_MCS2_CH2, txpower); + + /* MCS 4,5 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS4_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS4_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS4_CH2, txpower); + + /* MCS 6 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS6_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS6_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS6_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 3, &eeprom); + + /* MCS 7 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX], + TX_PWR_CFG_7_MCS7_CH2, txpower); + + /* MCS 8,9 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS8_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS8_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS8_CH2, txpower); + + /* MCS 10,11 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS10_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX], + TX_PWR_CFG_2_MCS10_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX], + TX_PWR_CFG_2_EXT_MCS10_CH2, txpower); + + /* MCS 12,13 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS12_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS12_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_MCS12_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 4, &eeprom); + + /* MCS 14 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS14_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_MCS14_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_MCS14_CH2, txpower); + + /* MCS 15 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS15_CH2, txpower); + + /* MCS 16,17 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS16_CH2, txpower); + + /* MCS 18,19 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX], + TX_PWR_CFG_5_MCS18_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 5, &eeprom); + + /* MCS 20,21 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS20_CH2, txpower); + + /* MCS 22 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX], + TX_PWR_CFG_6_MCS22_CH2, txpower); + + /* MCS 23 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX], + TX_PWR_CFG_8_MCS23_CH2, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 6, &eeprom); + + /* STBC, MCS 0,1 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC0_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC0_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_STBC0_CH2, txpower); + + /* STBC, MCS 2,3 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC2_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX], + TX_PWR_CFG_3_STBC2_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX], + TX_PWR_CFG_3_EXT_STBC2_CH2, txpower); + + /* STBC, MCS 4,5 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE0, + txpower); + + /* STBC, MCS 6 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE2, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE3, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE2, + txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + offset + 7, &eeprom); + + /* STBC, MCS 7 */ + txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level, + txpower, delta); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH0, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH1, txpower); + rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX], + TX_PWR_CFG_9_STBC7_CH2, txpower); + + rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, regs[TX_PWR_CFG_0_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, regs[TX_PWR_CFG_1_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, regs[TX_PWR_CFG_2_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, regs[TX_PWR_CFG_3_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, regs[TX_PWR_CFG_4_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_5, regs[TX_PWR_CFG_5_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_6, regs[TX_PWR_CFG_6_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_7, regs[TX_PWR_CFG_7_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_8, regs[TX_PWR_CFG_8_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_9, regs[TX_PWR_CFG_9_IDX]); + + rt2800_register_write(rt2x00dev, TX_PWR_CFG_0_EXT, + regs[TX_PWR_CFG_0_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_1_EXT, + regs[TX_PWR_CFG_1_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_2_EXT, + regs[TX_PWR_CFG_2_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_3_EXT, + regs[TX_PWR_CFG_3_EXT_IDX]); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_4_EXT, + regs[TX_PWR_CFG_4_EXT_IDX]); + + for (i = 0; i < TX_PWR_CFG_IDX_COUNT; i++) + rt2x00_dbg(rt2x00dev, + "band:%cGHz, BW:%c0MHz, TX_PWR_CFG_%d%s = %08lx\n", + (band == IEEE80211_BAND_5GHZ) ? '5' : '2', + (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) ? + '4' : '2', + (i > TX_PWR_CFG_9_IDX) ? + (i - TX_PWR_CFG_9_IDX - 1) : i, + (i > TX_PWR_CFG_9_IDX) ? "_EXT" : "", + (unsigned long) regs[i]); +} + +/* + * We configure transmit power using MAC TX_PWR_CFG_{0,...,N} registers and + * BBP R1 register. TX_PWR_CFG_X allow to configure per rate TX power values, + * 4 bits for each rate (tune from 0 to 15 dBm). BBP_R1 controls transmit power + * for all rates, but allow to set only 4 discrete values: -12, -6, 0 and 6 dBm. + * Reference per rate transmit power values are located in the EEPROM at + * EEPROM_TXPOWER_BYRATE offset. We adjust them and BBP R1 settings according to + * current conditions (i.e. band, bandwidth, temperature, user settings). + */ +static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + u8 txpower, r1; + u16 eeprom; + u32 reg, offset; + int i, is_rate_b, delta, power_ctrl; + enum ieee80211_band band = chan->band; + + /* + * Calculate HT40 compensation. For 40MHz we need to add or subtract + * value read from EEPROM (different for 2GHz and for 5GHz). + */ + delta = rt2800_get_txpower_bw_comp(rt2x00dev, band); + + /* + * Calculate temperature compensation. Depends on measurement of current + * TSSI (Transmitter Signal Strength Indication) we know TX power (due + * to temperature or maybe other factors) is smaller or bigger than + * expected. We adjust it, based on TSSI reference and boundaries values + * provided in EEPROM. + */ + delta += rt2800_get_gain_calibration_delta(rt2x00dev); + + /* + * Decrease power according to user settings, on devices with unknown + * maximum tx power. For other devices we take user power_level into + * consideration on rt2800_compensate_txpower(). + */ + delta += rt2800_get_txpower_reg_delta(rt2x00dev, power_level, + chan->max_power); + + /* + * BBP_R1 controls TX power for all rates, it allow to set the following + * gains -12, -6, 0, +6 dBm by setting values 2, 1, 0, 3 respectively. + * + * TODO: we do not use +6 dBm option to do not increase power beyond + * regulatory limit, however this could be utilized for devices with + * CAPABILITY_POWER_LIMIT. + * + * TODO: add different temperature compensation code for RT3290 & RT5390 + * to allow to use BBP_R1 for those chips. + */ + if (!rt2x00_rt(rt2x00dev, RT3290) && + !rt2x00_rt(rt2x00dev, RT5390)) { + rt2800_bbp_read(rt2x00dev, 1, &r1); + if (delta <= -12) { + power_ctrl = 2; + delta += 12; + } else if (delta <= -6) { + power_ctrl = 1; + delta += 6; + } else { + power_ctrl = 0; + } + rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl); + rt2800_bbp_write(rt2x00dev, 1, r1); + } + + offset = TX_PWR_CFG_0; + + for (i = 0; i < EEPROM_TXPOWER_BYRATE_SIZE; i += 2) { + /* just to be safe */ + if (offset > TX_PWR_CFG_4) + break; + + rt2800_register_read(rt2x00dev, offset, ®); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + i, &eeprom); + + is_rate_b = i ? 0 : 1; + /* + * TX_PWR_CFG_0: 1MBS, TX_PWR_CFG_1: 24MBS, + * TX_PWR_CFG_2: MCS4, TX_PWR_CFG_3: MCS12, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE0, txpower); + + /* + * TX_PWR_CFG_0: 2MBS, TX_PWR_CFG_1: 36MBS, + * TX_PWR_CFG_2: MCS5, TX_PWR_CFG_3: MCS13, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE1, txpower); + + /* + * TX_PWR_CFG_0: 5.5MBS, TX_PWR_CFG_1: 48MBS, + * TX_PWR_CFG_2: MCS6, TX_PWR_CFG_3: MCS14, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE2, txpower); + + /* + * TX_PWR_CFG_0: 11MBS, TX_PWR_CFG_1: 54MBS, + * TX_PWR_CFG_2: MCS7, TX_PWR_CFG_3: MCS15, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE3, txpower); + + /* read the next four txpower values */ + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + i + 1, &eeprom); + + is_rate_b = 0; + /* + * TX_PWR_CFG_0: 6MBS, TX_PWR_CFG_1: MCS0, + * TX_PWR_CFG_2: MCS8, TX_PWR_CFG_3: unknown, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE0); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE4, txpower); + + /* + * TX_PWR_CFG_0: 9MBS, TX_PWR_CFG_1: MCS1, + * TX_PWR_CFG_2: MCS9, TX_PWR_CFG_3: unknown, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE1); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE5, txpower); + + /* + * TX_PWR_CFG_0: 12MBS, TX_PWR_CFG_1: MCS2, + * TX_PWR_CFG_2: MCS10, TX_PWR_CFG_3: unknown, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE2); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE6, txpower); + + /* + * TX_PWR_CFG_0: 18MBS, TX_PWR_CFG_1: MCS3, + * TX_PWR_CFG_2: MCS11, TX_PWR_CFG_3: unknown, + * TX_PWR_CFG_4: unknown + */ + txpower = rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_BYRATE_RATE3); + txpower = rt2800_compensate_txpower(rt2x00dev, is_rate_b, band, + power_level, txpower, delta); + rt2x00_set_field32(®, TX_PWR_CFG_RATE7, txpower); + + rt2800_register_write(rt2x00dev, offset, reg); + + /* next TX_PWR_CFG register */ + offset += 4; + } +} + +static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + if (rt2x00_rt(rt2x00dev, RT3593)) + rt2800_config_txpower_rt3593(rt2x00dev, chan, power_level); + else + rt2800_config_txpower_rt28xx(rt2x00dev, chan, power_level); +} + +void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev) +{ + rt2800_config_txpower(rt2x00dev, rt2x00dev->hw->conf.chandef.chan, + rt2x00dev->tx_power); +} +EXPORT_SYMBOL_GPL(rt2800_gain_calibration); + +void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) +{ + u32 tx_pin; + u8 rfcsr; + + /* + * A voltage-controlled oscillator(VCO) is an electronic oscillator + * designed to be controlled in oscillation frequency by a voltage + * input. Maybe the temperature will affect the frequency of + * oscillation to be shifted. The VCO calibration will be called + * periodically to adjust the frequency to be precision. + */ + + rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); + tx_pin &= TX_PIN_CFG_PA_PE_DISABLE; + rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + + switch (rt2x00dev->chip.rf) { + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3320: + case RF3052: + rt2800_rfcsr_read(rt2x00dev, 7, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); + rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); + break; + case RF3053: + case RF3070: + case RF3290: + case RF5360: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + break; + default: + return; + } + + mdelay(1); + + rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); + if (rt2x00dev->rf_channel <= 14) { + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G2_EN, 1); + /* fall through */ + case 2: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G1_EN, 1); + /* fall through */ + case 1: + default: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); + break; + } + } else { + switch (rt2x00dev->default_ant.tx_chain_num) { + case 3: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A2_EN, 1); + /* fall through */ + case 2: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A1_EN, 1); + /* fall through */ + case 1: + default: + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, 1); + break; + } + } + rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + +} +EXPORT_SYMBOL_GPL(rt2800_vco_calibration); + +static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); + rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, + libconf->conf->short_frame_max_tx_count); + rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, + libconf->conf->long_frame_max_tx_count); + rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); +} + +static void rt2800_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; + + if (state == STATE_SLEEP) { + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0); + + rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 5); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, + libconf->conf->listen_interval - 1); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 1); + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); + } else { + rt2800_register_read(rt2x00dev, AUTOWAKEUP_CFG, ®); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTO_LEAD_TIME, 0); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE, 0); + rt2x00_set_field32(®, AUTOWAKEUP_CFG_AUTOWAKE, 0); + rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, reg); + + rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); + } +} + +void rt2800_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + /* Always recalculate LNA gain before changing configuration */ + rt2800_config_lna_gain(rt2x00dev, libconf); + + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) { + rt2800_config_channel(rt2x00dev, libconf->conf, + &libconf->rf, &libconf->channel); + rt2800_config_txpower(rt2x00dev, libconf->conf->chandef.chan, + libconf->conf->power_level); + } + if (flags & IEEE80211_CONF_CHANGE_POWER) + rt2800_config_txpower(rt2x00dev, libconf->conf->chandef.chan, + libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt2800_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt2800_config_ps(rt2x00dev, libconf); +} +EXPORT_SYMBOL_GPL(rt2800_config); + +/* + * Link tuning + */ +void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) +{ + u32 reg; + + /* + * Update FCS error count from register. + */ + rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); + qual->rx_failed = rt2x00_get_field32(reg, RX_STA_CNT0_CRC_ERR); +} +EXPORT_SYMBOL_GPL(rt2800_link_stats); + +static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) +{ + u8 vgc; + + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT3390) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592)) + vgc = 0x1c + (2 * rt2x00dev->lna_gain); + else + vgc = 0x2e + rt2x00dev->lna_gain; + } else { /* 5GHZ band */ + if (rt2x00_rt(rt2x00dev, RT3593)) + vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3; + else if (rt2x00_rt(rt2x00dev, RT5592)) + vgc = 0x24 + (2 * rt2x00dev->lna_gain); + else { + if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + vgc = 0x32 + (rt2x00dev->lna_gain * 5) / 3; + else + vgc = 0x3a + (rt2x00dev->lna_gain * 5) / 3; + } + } + + return vgc; +} + +static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) +{ + if (qual->vgc_level != vgc_level) { + if (rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, + vgc_level); + } else if (rt2x00_rt(rt2x00dev, RT5592)) { + rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a); + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level); + } else { + rt2800_bbp_write(rt2x00dev, 66, vgc_level); + } + + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } +} + +void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual) +{ + rt2800_set_vgc(rt2x00dev, qual, rt2800_get_default_vgc(rt2x00dev)); +} +EXPORT_SYMBOL_GPL(rt2800_reset_tuner); + +void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, + const u32 count) +{ + u8 vgc; + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) + return; + + /* When RSSI is better than a certain threshold, increase VGC + * with a chip specific value in order to improve the balance + * between sensibility and noise isolation. + */ + + vgc = rt2800_get_default_vgc(rt2x00dev); + + switch (rt2x00dev->chip.rt) { + case RT3572: + case RT3593: + if (qual->rssi > -65) { + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) + vgc += 0x20; + else + vgc += 0x10; + } + break; + + case RT5592: + if (qual->rssi > -65) + vgc += 0x20; + break; + + default: + if (qual->rssi > -80) + vgc += 0x10; + break; + } + + rt2800_set_vgc(rt2x00dev, qual, vgc); +} +EXPORT_SYMBOL_GPL(rt2800_link_tuner); + +/* + * Initialization functions. + */ +static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 eeprom; + unsigned int i; + int ret; + + rt2800_disable_wpdma(rt2x00dev); + + ret = rt2800_drv_init_registers(rt2x00dev); + if (ret) + return ret; + + rt2800_register_read(rt2x00dev, BCN_OFFSET0, ®); + rt2x00_set_field32(®, BCN_OFFSET0_BCN0, + rt2800_get_beacon_offset(rt2x00dev, 0)); + rt2x00_set_field32(®, BCN_OFFSET0_BCN1, + rt2800_get_beacon_offset(rt2x00dev, 1)); + rt2x00_set_field32(®, BCN_OFFSET0_BCN2, + rt2800_get_beacon_offset(rt2x00dev, 2)); + rt2x00_set_field32(®, BCN_OFFSET0_BCN3, + rt2800_get_beacon_offset(rt2x00dev, 3)); + rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg); + + rt2800_register_read(rt2x00dev, BCN_OFFSET1, ®); + rt2x00_set_field32(®, BCN_OFFSET1_BCN4, + rt2800_get_beacon_offset(rt2x00dev, 4)); + rt2x00_set_field32(®, BCN_OFFSET1_BCN5, + rt2800_get_beacon_offset(rt2x00dev, 5)); + rt2x00_set_field32(®, BCN_OFFSET1_BCN6, + rt2800_get_beacon_offset(rt2x00dev, 6)); + rt2x00_set_field32(®, BCN_OFFSET1_BCN7, + rt2800_get_beacon_offset(rt2x00dev, 7)); + rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg); + + rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f); + rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003); + + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 1600); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_SYNC, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TX_TIME_COMPENSATE, 0); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2800_config_filter(rt2x00dev, FIF_ALLMULTI); + + rt2800_register_read(rt2x00dev, BKOFF_SLOT_CFG, ®); + rt2x00_set_field32(®, BKOFF_SLOT_CFG_SLOT_TIME, 9); + rt2x00_set_field32(®, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2); + rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); + + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + if (rt2x00_get_field32(reg, WLAN_EN) == 1) { + rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + } + + rt2800_register_read(rt2x00dev, CMB_CTRL, ®); + if (!(rt2x00_get_field32(reg, LDO0_EN) == 1)) { + rt2x00_set_field32(®, LDO0_EN, 1); + rt2x00_set_field32(®, LDO_BGSEL, 3); + rt2800_register_write(rt2x00dev, CMB_CTRL, reg); + } + + rt2800_register_read(rt2x00dev, OSC_CTRL, ®); + rt2x00_set_field32(®, OSC_ROSC_EN, 1); + rt2x00_set_field32(®, OSC_CAL_REQ, 1); + rt2x00_set_field32(®, OSC_REF_CYCLE, 0x27); + rt2800_register_write(rt2x00dev, OSC_CTRL, reg); + + rt2800_register_read(rt2x00dev, COEX_CFG0, ®); + rt2x00_set_field32(®, COEX_CFG_ANT, 0x5e); + rt2800_register_write(rt2x00dev, COEX_CFG0, reg); + + rt2800_register_read(rt2x00dev, COEX_CFG2, ®); + rt2x00_set_field32(®, BT_COEX_CFG1, 0x00); + rt2x00_set_field32(®, BT_COEX_CFG0, 0x17); + rt2x00_set_field32(®, WL_COEX_CFG1, 0x93); + rt2x00_set_field32(®, WL_COEX_CFG0, 0x7f); + rt2800_register_write(rt2x00dev, COEX_CFG2, reg); + + rt2800_register_read(rt2x00dev, PLL_CTRL, ®); + rt2x00_set_field32(®, PLL_CONTROL, 1); + rt2800_register_write(rt2x00dev, PLL_CTRL, reg); + } + + if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT3390)) { + + if (rt2x00_rt(rt2x00dev, RT3290)) + rt2800_register_write(rt2x00dev, TX_SW_CFG0, + 0x00000404); + else + rt2800_register_write(rt2x00dev, TX_SW_CFG0, + 0x00000400); + + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000002c); + else + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000000f); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } + } else if (rt2x00_rt(rt2x00dev, RT3070)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x0000002c); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } + } else if (rt2800_is_305x_soc(rt2x00dev)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000030); + } else if (rt2x00_rt(rt2x00dev, RT3352)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } else if (rt2x00_rt(rt2x00dev, RT3572)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + } else if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3593, REV_RT3593E)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); + if (rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_DAC_TEST)) + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000001f); + else + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x0000000f); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG2, + 0x00000000); + } + } else if (rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + } else { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); + } + + rt2800_register_read(rt2x00dev, TX_LINK_CFG, ®); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB_LIFETIME, 32); + rt2x00_set_field32(®, TX_LINK_CFG_MFB_ENABLE, 0); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_UMFS_ENABLE, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_MRQ_EN, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_RDG_EN, 0); + rt2x00_set_field32(®, TX_LINK_CFG_TX_CF_ACK_EN, 1); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFB, 0); + rt2x00_set_field32(®, TX_LINK_CFG_REMOTE_MFS, 0); + rt2800_register_write(rt2x00dev, TX_LINK_CFG, reg); + + rt2800_register_read(rt2x00dev, TX_TIMEOUT_CFG, ®); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_MPDU_LIFETIME, 9); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_RX_ACK_TIMEOUT, 32); + rt2x00_set_field32(®, TX_TIMEOUT_CFG_TX_OP_TIMEOUT, 10); + rt2800_register_write(rt2x00dev, TX_TIMEOUT_CFG, reg); + + rt2800_register_read(rt2x00dev, MAX_LEN_CFG, ®); + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_MPDU, AGGREGATION_SIZE); + if (rt2x00_rt_rev_gte(rt2x00dev, RT2872, REV_RT2872E) || + rt2x00_rt(rt2x00dev, RT2883) || + rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070E)) + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 2); + else + rt2x00_set_field32(®, MAX_LEN_CFG_MAX_PSDU, 1); + rt2x00_set_field32(®, MAX_LEN_CFG_MIN_PSDU, 0); + rt2x00_set_field32(®, MAX_LEN_CFG_MIN_MPDU, 0); + rt2800_register_write(rt2x00dev, MAX_LEN_CFG, reg); + + rt2800_register_read(rt2x00dev, LED_CFG, ®); + rt2x00_set_field32(®, LED_CFG_ON_PERIOD, 70); + rt2x00_set_field32(®, LED_CFG_OFF_PERIOD, 30); + rt2x00_set_field32(®, LED_CFG_SLOW_BLINK_PERIOD, 3); + rt2x00_set_field32(®, LED_CFG_R_LED_MODE, 3); + rt2x00_set_field32(®, LED_CFG_G_LED_MODE, 3); + rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, 3); + rt2x00_set_field32(®, LED_CFG_LED_POLAR, 1); + rt2800_register_write(rt2x00dev, LED_CFG, reg); + + rt2800_register_write(rt2x00dev, PBF_MAX_PCNT, 0x1f3fbf9f); + + rt2800_register_read(rt2x00dev, TX_RTY_CFG, ®); + rt2x00_set_field32(®, TX_RTY_CFG_SHORT_RTY_LIMIT, 15); + rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_LIMIT, 31); + rt2x00_set_field32(®, TX_RTY_CFG_LONG_RTY_THRE, 2000); + rt2x00_set_field32(®, TX_RTY_CFG_NON_AGG_RTY_MODE, 0); + rt2x00_set_field32(®, TX_RTY_CFG_AGG_RTY_MODE, 0); + rt2x00_set_field32(®, TX_RTY_CFG_TX_AUTO_FB_ENABLE, 1); + rt2800_register_write(rt2x00dev, TX_RTY_CFG, reg); + + rt2800_register_read(rt2x00dev, AUTO_RSP_CFG, ®); + rt2x00_set_field32(®, AUTO_RSP_CFG_AUTORESPONDER, 1); + rt2x00_set_field32(®, AUTO_RSP_CFG_BAC_ACK_POLICY, 1); + rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MMODE, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_CTS_40_MREF, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_AR_PREAMBLE, 1); + rt2x00_set_field32(®, AUTO_RSP_CFG_DUAL_CTS_EN, 0); + rt2x00_set_field32(®, AUTO_RSP_CFG_ACK_CTS_PSM_BIT, 0); + rt2800_register_write(rt2x00dev, AUTO_RSP_CFG, reg); + + rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_RATE, 3); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, CCK_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, CCK_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, 1); + rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_RATE, 3); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, OFDM_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, OFDM_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, 1); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_RATE, 0x4004); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, MM20_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, 0); + rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_RATE, 0x4084); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, MM40_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_MM40, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_TX_OP_ALLOW_GF40, 1); + rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, 0); + rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_RATE, 0x4004); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_MM40, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, GF20_PROT_CFG_TX_OP_ALLOW_GF40, 0); + rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, 0); + rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_RATE, 0x4084); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_CTRL, 0); + rt2x00_set_field32(®, GF40_PROT_CFG_PROTECT_NAV_SHORT, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_CCK, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_OFDM, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM20, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_MM40, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF20, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_TX_OP_ALLOW_GF40, 1); + rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, 0); + rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); + + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_register_write(rt2x00dev, PBF_CFG, 0xf40006); + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 3); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_BIG_ENDIAN, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_HDR_SCATTER, 0); + rt2x00_set_field32(®, WPDMA_GLO_CFG_HDR_SEG_LEN, 0); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + } + + /* + * The legacy driver also sets TXOP_CTRL_CFG_RESERVED_TRUN_EN to 1 + * although it is reserved. + */ + rt2800_register_read(rt2x00dev, TXOP_CTRL_CFG, ®); + rt2x00_set_field32(®, TXOP_CTRL_CFG_TIMEOUT_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_AC_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_TXRATEGRP_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_USER_MODE_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_MIMO_PS_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_RESERVED_TRUN_EN, 1); + rt2x00_set_field32(®, TXOP_CTRL_CFG_LSIG_TXOP_EN, 0); + rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CCA_EN, 0); + rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CCA_DLY, 88); + rt2x00_set_field32(®, TXOP_CTRL_CFG_EXT_CWMIN, 0); + rt2800_register_write(rt2x00dev, TXOP_CTRL_CFG, reg); + + reg = rt2x00_rt(rt2x00dev, RT5592) ? 0x00000082 : 0x00000002; + rt2800_register_write(rt2x00dev, TXOP_HLDR_ET, reg); + + rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); + rt2x00_set_field32(®, TX_RTS_CFG_AUTO_RTS_RETRY_LIMIT, 32); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, + IEEE80211_MAX_RTS_THRESHOLD); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_FBK_EN, 0); + rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); + + rt2800_register_write(rt2x00dev, EXP_ACK_TIME, 0x002400ca); + + /* + * Usually the CCK SIFS time should be set to 10 and the OFDM SIFS + * time should be set to 16. However, the original Ralink driver uses + * 16 for both and indeed using a value of 10 for CCK SIFS results in + * connection problems with 11g + CTS protection. Hence, use the same + * defaults as the Ralink driver: 16 for both, CCK and OFDM SIFS. + */ + rt2800_register_read(rt2x00dev, XIFS_TIME_CFG, ®); + rt2x00_set_field32(®, XIFS_TIME_CFG_CCKM_SIFS_TIME, 16); + rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_SIFS_TIME, 16); + rt2x00_set_field32(®, XIFS_TIME_CFG_OFDM_XIFS_TIME, 4); + rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, 314); + rt2x00_set_field32(®, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1); + rt2800_register_write(rt2x00dev, XIFS_TIME_CFG, reg); + + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); + + /* + * ASIC will keep garbage value after boot, clear encryption keys. + */ + for (i = 0; i < 4; i++) + rt2800_register_write(rt2x00dev, + SHARED_KEY_MODE_ENTRY(i), 0); + + for (i = 0; i < 256; i++) { + rt2800_config_wcid(rt2x00dev, NULL, i); + rt2800_delete_wcid_attr(rt2x00dev, i); + rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0); + } + + /* + * Clear all beacons + */ + for (i = 0; i < 8; i++) + rt2800_clear_beacon_register(rt2x00dev, i); + + if (rt2x00_is_usb(rt2x00dev)) { + rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); + rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 30); + rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); + } else if (rt2x00_is_pcie(rt2x00dev)) { + rt2800_register_read(rt2x00dev, US_CYC_CNT, ®); + rt2x00_set_field32(®, US_CYC_CNT_CLOCK_CYCLE, 125); + rt2800_register_write(rt2x00dev, US_CYC_CNT, reg); + } + + rt2800_register_read(rt2x00dev, HT_FBK_CFG0, ®); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS0FBK, 0); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS1FBK, 0); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS2FBK, 1); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS3FBK, 2); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS4FBK, 3); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS5FBK, 4); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS6FBK, 5); + rt2x00_set_field32(®, HT_FBK_CFG0_HTMCS7FBK, 6); + rt2800_register_write(rt2x00dev, HT_FBK_CFG0, reg); + + rt2800_register_read(rt2x00dev, HT_FBK_CFG1, ®); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS8FBK, 8); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS9FBK, 8); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS10FBK, 9); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS11FBK, 10); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS12FBK, 11); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS13FBK, 12); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS14FBK, 13); + rt2x00_set_field32(®, HT_FBK_CFG1_HTMCS15FBK, 14); + rt2800_register_write(rt2x00dev, HT_FBK_CFG1, reg); + + rt2800_register_read(rt2x00dev, LG_FBK_CFG0, ®); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS0FBK, 8); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS1FBK, 8); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS2FBK, 9); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS3FBK, 10); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS4FBK, 11); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS5FBK, 12); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS6FBK, 13); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS7FBK, 14); + rt2800_register_write(rt2x00dev, LG_FBK_CFG0, reg); + + rt2800_register_read(rt2x00dev, LG_FBK_CFG1, ®); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS0FBK, 0); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS1FBK, 0); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS2FBK, 1); + rt2x00_set_field32(®, LG_FBK_CFG0_CCKMCS3FBK, 2); + rt2800_register_write(rt2x00dev, LG_FBK_CFG1, reg); + + /* + * Do not force the BA window size, we use the TXWI to set it + */ + rt2800_register_read(rt2x00dev, AMPDU_BA_WINSIZE, ®); + rt2x00_set_field32(®, AMPDU_BA_WINSIZE_FORCE_WINSIZE_ENABLE, 0); + rt2x00_set_field32(®, AMPDU_BA_WINSIZE_FORCE_WINSIZE, 0); + rt2800_register_write(rt2x00dev, AMPDU_BA_WINSIZE, reg); + + /* + * We must clear the error counters. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2800_register_read(rt2x00dev, RX_STA_CNT0, ®); + rt2800_register_read(rt2x00dev, RX_STA_CNT1, ®); + rt2800_register_read(rt2x00dev, RX_STA_CNT2, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT0, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT1, ®); + rt2800_register_read(rt2x00dev, TX_STA_CNT2, ®); + + /* + * Setup leadtime for pre tbtt interrupt to 6ms + */ + rt2800_register_read(rt2x00dev, INT_TIMER_CFG, ®); + rt2x00_set_field32(®, INT_TIMER_CFG_PRE_TBTT_TIMER, 6 << 4); + rt2800_register_write(rt2x00dev, INT_TIMER_CFG, reg); + + /* + * Set up channel statistics timer + */ + rt2800_register_read(rt2x00dev, CH_TIME_CFG, ®); + rt2x00_set_field32(®, CH_TIME_CFG_EIFS_BUSY, 1); + rt2x00_set_field32(®, CH_TIME_CFG_NAV_BUSY, 1); + rt2x00_set_field32(®, CH_TIME_CFG_RX_BUSY, 1); + rt2x00_set_field32(®, CH_TIME_CFG_TX_BUSY, 1); + rt2x00_set_field32(®, CH_TIME_CFG_TMR_EN, 1); + rt2800_register_write(rt2x00dev, CH_TIME_CFG, reg); + + return 0; +} + +static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, MAC_STATUS_CFG, ®); + if (!rt2x00_get_field32(reg, MAC_STATUS_CFG_BBP_RF_BUSY)) + return 0; + + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP/RF register access failed, aborting\n"); + return -EACCES; +} + +static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u8 value; + + /* + * BBP was enabled after firmware was loaded, + * but we need to reactivate it now. + */ + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + msleep(1); + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + return 0; + udelay(REGISTER_BUSY_DELAY); + } + + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); + return -EACCES; +} + +static void rt2800_bbp4_mac_if_ctrl(struct rt2x00_dev *rt2x00dev) +{ + u8 value; + + rt2800_bbp_read(rt2x00dev, 4, &value); + rt2x00_set_field8(&value, BBP4_MAC_IF_CTRL, 1); + rt2800_bbp_write(rt2x00dev, 4, value); +} + +static void rt2800_init_freq_calibration(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 142, 1); + rt2800_bbp_write(rt2x00dev, 143, 57); +} + +static void rt2800_init_bbp_5592_glrt(struct rt2x00_dev *rt2x00dev) +{ + const u8 glrt_table[] = { + 0xE0, 0x1F, 0X38, 0x32, 0x08, 0x28, 0x19, 0x0A, 0xFF, 0x00, /* 128 ~ 137 */ + 0x16, 0x10, 0x10, 0x0B, 0x36, 0x2C, 0x26, 0x24, 0x42, 0x36, /* 138 ~ 147 */ + 0x30, 0x2D, 0x4C, 0x46, 0x3D, 0x40, 0x3E, 0x42, 0x3D, 0x40, /* 148 ~ 157 */ + 0X3C, 0x34, 0x2C, 0x2F, 0x3C, 0x35, 0x2E, 0x2A, 0x49, 0x41, /* 158 ~ 167 */ + 0x36, 0x31, 0x30, 0x30, 0x0E, 0x0D, 0x28, 0x21, 0x1C, 0x16, /* 168 ~ 177 */ + 0x50, 0x4A, 0x43, 0x40, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, /* 178 ~ 187 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 188 ~ 197 */ + 0x00, 0x00, 0x7D, 0x14, 0x32, 0x2C, 0x36, 0x4C, 0x43, 0x2C, /* 198 ~ 207 */ + 0x2E, 0x36, 0x30, 0x6E, /* 208 ~ 211 */ + }; + int i; + + for (i = 0; i < ARRAY_SIZE(glrt_table); i++) { + rt2800_bbp_write(rt2x00dev, 195, 128 + i); + rt2800_bbp_write(rt2x00dev, 196, glrt_table[i]); + } +}; + +static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2C); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + rt2800_bbp_write(rt2x00dev, 68, 0x0B); + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + rt2800_bbp_write(rt2x00dev, 81, 0x37); + rt2800_bbp_write(rt2x00dev, 82, 0x62); + rt2800_bbp_write(rt2x00dev, 83, 0x6A); + rt2800_bbp_write(rt2x00dev, 84, 0x99); + rt2800_bbp_write(rt2x00dev, 86, 0x00); + rt2800_bbp_write(rt2x00dev, 91, 0x04); + rt2800_bbp_write(rt2x00dev, 92, 0x00); + rt2800_bbp_write(rt2x00dev, 103, 0x00); + rt2800_bbp_write(rt2x00dev, 105, 0x05); + rt2800_bbp_write(rt2x00dev, 106, 0x35); +} + +static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev) +{ + u16 eeprom; + u8 value; + + rt2800_bbp_read(rt2x00dev, 138, &value); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) + value |= 0x20; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) + value &= ~0x02; + rt2800_bbp_write(rt2x00dev, 138, value); +} + +static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 105, 0x01); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); +} + +static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { + rt2800_bbp_write(rt2x00dev, 69, 0x16); + rt2800_bbp_write(rt2x00dev, 73, 0x12); + } else { + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + } + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 81, 0x37); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) + rt2800_bbp_write(rt2x00dev, 84, 0x19); + else + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); +} + +static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || + rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + else + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090)) + rt2800_disable_unused_dac_adc(rt2x00dev); +} + +static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) +{ + u8 value; + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x58); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 74, 0x0b); + rt2800_bbp_write(rt2x00dev, 79, 0x18); + rt2800_bbp_write(rt2x00dev, 80, 0x09); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x7a); + + rt2800_bbp_write(rt2x00dev, 84, 0x9a); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x1c); + + rt2800_bbp_write(rt2x00dev, 106, 0x03); + + rt2800_bbp_write(rt2x00dev, 128, 0x12); + + rt2800_bbp_write(rt2x00dev, 67, 0x24); + rt2800_bbp_write(rt2x00dev, 143, 0x04); + rt2800_bbp_write(rt2x00dev, 142, 0x99); + rt2800_bbp_write(rt2x00dev, 150, 0x30); + rt2800_bbp_write(rt2x00dev, 151, 0x2e); + rt2800_bbp_write(rt2x00dev, 152, 0x20); + rt2800_bbp_write(rt2x00dev, 153, 0x34); + rt2800_bbp_write(rt2x00dev, 154, 0x40); + rt2800_bbp_write(rt2x00dev, 155, 0x3b); + rt2800_bbp_write(rt2x00dev, 253, 0x04); + + rt2800_bbp_read(rt2x00dev, 47, &value); + rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); + rt2800_bbp_write(rt2x00dev, 47, value); + + /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ + rt2800_bbp_read(rt2x00dev, 3, &value); + rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); + rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); + rt2800_bbp_write(rt2x00dev, 3, value); +} + +static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 3, 0x00); + rt2800_bbp_write(rt2x00dev, 4, 0x50); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 47, 0x48); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x59); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); + rt2800_bbp_write(rt2x00dev, 81, 0x37); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); + + rt2800_bbp_write(rt2x00dev, 88, 0x90); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x34); + + rt2800_bbp_write(rt2x00dev, 106, 0x05); + + rt2800_bbp_write(rt2x00dev, 120, 0x50); + + rt2800_bbp_write(rt2x00dev, 137, 0x0f); + + rt2800_bbp_write(rt2x00dev, 163, 0xbd); + /* Set ITxBF timeout to 0x9c40=1000msec */ + rt2800_bbp_write(rt2x00dev, 179, 0x02); + rt2800_bbp_write(rt2x00dev, 180, 0x00); + rt2800_bbp_write(rt2x00dev, 182, 0x40); + rt2800_bbp_write(rt2x00dev, 180, 0x01); + rt2800_bbp_write(rt2x00dev, 182, 0x9c); + rt2800_bbp_write(rt2x00dev, 179, 0x00); + /* Reprogram the inband interface to put right values in RXWI */ + rt2800_bbp_write(rt2x00dev, 142, 0x04); + rt2800_bbp_write(rt2x00dev, 143, 0x3b); + rt2800_bbp_write(rt2x00dev, 142, 0x06); + rt2800_bbp_write(rt2x00dev, 143, 0xa0); + rt2800_bbp_write(rt2x00dev, 142, 0x07); + rt2800_bbp_write(rt2x00dev, 143, 0xa1); + rt2800_bbp_write(rt2x00dev, 142, 0x08); + rt2800_bbp_write(rt2x00dev, 143, 0xa2); + + rt2800_bbp_write(rt2x00dev, 148, 0xc8); +} + +static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + else + rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + rt2800_disable_unused_dac_adc(rt2x00dev); +} + +static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) +{ + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); + + rt2800_disable_unused_dac_adc(rt2x00dev); +} + +static void rt2800_init_bbp_3593(struct rt2x00_dev *rt2x00dev) +{ + rt2800_init_bbp_early(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + rt2800_bbp_write(rt2x00dev, 137, 0x0f); + + rt2800_bbp_write(rt2x00dev, 84, 0x19); + + /* Enable DC filter */ + if (rt2x00_rt_rev_gte(rt2x00dev, RT3593, REV_RT3593E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); +} + +static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) +{ + int ant, div_mode; + u16 eeprom; + u8 value; + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x59); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x7a); + + rt2800_bbp_write(rt2x00dev, 84, 0x9a); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); + + if (rt2x00_rt(rt2x00dev, RT5392)) + rt2800_bbp_write(rt2x00dev, 88, 0x90); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_bbp_write(rt2x00dev, 95, 0x9a); + rt2800_bbp_write(rt2x00dev, 98, 0x12); + } + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x3c); + + if (rt2x00_rt(rt2x00dev, RT5390)) + rt2800_bbp_write(rt2x00dev, 106, 0x03); + else if (rt2x00_rt(rt2x00dev, RT5392)) + rt2800_bbp_write(rt2x00dev, 106, 0x12); + else + WARN_ON(1); + + rt2800_bbp_write(rt2x00dev, 128, 0x12); + + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_bbp_write(rt2x00dev, 134, 0xd0); + rt2800_bbp_write(rt2x00dev, 135, 0xf6); + } + + rt2800_disable_unused_dac_adc(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + div_mode = rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_ANT_DIVERSITY); + ant = (div_mode == 3) ? 1 : 0; + + /* check if this is a Bluetooth combo card */ + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { + u32 reg; + + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); + rt2x00_set_field32(®, GPIO_CTRL_DIR6, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL3, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL6, 0); + if (ant == 0) + rt2x00_set_field32(®, GPIO_CTRL_VAL3, 1); + else if (ant == 1) + rt2x00_set_field32(®, GPIO_CTRL_VAL6, 1); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + } + + /* This chip has hardware antenna diversity*/ + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { + rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */ + rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */ + rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */ + } + + rt2800_bbp_read(rt2x00dev, 152, &value); + if (ant == 0) + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); + else + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); + rt2800_bbp_write(rt2x00dev, 152, value); + + rt2800_init_freq_calibration(rt2x00dev); +} + +static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) +{ + int ant, div_mode; + u16 eeprom; + u8 value; + + rt2800_init_bbp_early(rt2x00dev); + + rt2800_bbp_read(rt2x00dev, 105, &value); + rt2x00_set_field8(&value, BBP105_MLD, + rt2x00dev->default_ant.rx_chain_num == 2); + rt2800_bbp_write(rt2x00dev, 105, value); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 20, 0x06); + rt2800_bbp_write(rt2x00dev, 31, 0x08); + rt2800_bbp_write(rt2x00dev, 65, 0x2C); + rt2800_bbp_write(rt2x00dev, 68, 0xDD); + rt2800_bbp_write(rt2x00dev, 69, 0x1A); + rt2800_bbp_write(rt2x00dev, 70, 0x05); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 74, 0x0F); + rt2800_bbp_write(rt2x00dev, 75, 0x4F); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + rt2800_bbp_write(rt2x00dev, 77, 0x59); + rt2800_bbp_write(rt2x00dev, 84, 0x9A); + rt2800_bbp_write(rt2x00dev, 86, 0x38); + rt2800_bbp_write(rt2x00dev, 88, 0x90); + rt2800_bbp_write(rt2x00dev, 91, 0x04); + rt2800_bbp_write(rt2x00dev, 92, 0x02); + rt2800_bbp_write(rt2x00dev, 95, 0x9a); + rt2800_bbp_write(rt2x00dev, 98, 0x12); + rt2800_bbp_write(rt2x00dev, 103, 0xC0); + rt2800_bbp_write(rt2x00dev, 104, 0x92); + /* FIXME BBP105 owerwrite */ + rt2800_bbp_write(rt2x00dev, 105, 0x3C); + rt2800_bbp_write(rt2x00dev, 106, 0x35); + rt2800_bbp_write(rt2x00dev, 128, 0x12); + rt2800_bbp_write(rt2x00dev, 134, 0xD0); + rt2800_bbp_write(rt2x00dev, 135, 0xF6); + rt2800_bbp_write(rt2x00dev, 137, 0x0F); + + /* Initialize GLRT (Generalized Likehood Radio Test) */ + rt2800_init_bbp_5592_glrt(rt2x00dev); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY); + ant = (div_mode == 3) ? 1 : 0; + rt2800_bbp_read(rt2x00dev, 152, &value); + if (ant == 0) { + /* Main antenna */ + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); + } else { + /* Auxiliary antenna */ + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); + } + rt2800_bbp_write(rt2x00dev, 152, value); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) { + rt2800_bbp_read(rt2x00dev, 254, &value); + rt2x00_set_field8(&value, BBP254_BIT7, 1); + rt2800_bbp_write(rt2x00dev, 254, value); + } + + rt2800_init_freq_calibration(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 84, 0x19); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); +} + +static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (rt2800_is_305x_soc(rt2x00dev)) + rt2800_init_bbp_305x_soc(rt2x00dev); + + switch (rt2x00dev->chip.rt) { + case RT2860: + case RT2872: + case RT2883: + rt2800_init_bbp_28xx(rt2x00dev); + break; + case RT3070: + case RT3071: + case RT3090: + rt2800_init_bbp_30xx(rt2x00dev); + break; + case RT3290: + rt2800_init_bbp_3290(rt2x00dev); + break; + case RT3352: + rt2800_init_bbp_3352(rt2x00dev); + break; + case RT3390: + rt2800_init_bbp_3390(rt2x00dev); + break; + case RT3572: + rt2800_init_bbp_3572(rt2x00dev); + break; + case RT3593: + rt2800_init_bbp_3593(rt2x00dev); + return; + case RT5390: + case RT5392: + rt2800_init_bbp_53xx(rt2x00dev); + break; + case RT5592: + rt2800_init_bbp_5592(rt2x00dev); + return; + } + + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_BBP_START, i, + &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + rt2800_bbp_write(rt2x00dev, reg_id, value); + } + } +} + +static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_register_read(rt2x00dev, OPT_14_CSR, ®); + rt2x00_set_field32(®, OPT_14_CSR_BIT0, 1); + rt2800_register_write(rt2x00dev, OPT_14_CSR, reg); +} + +static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev, bool bw40, + u8 filter_target) +{ + unsigned int i; + u8 bbp; + u8 rfcsr; + u8 passband; + u8 stopband; + u8 overtuned = 0; + u8 rfcsr24 = (bw40) ? 0x27 : 0x07; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 2 * bw40); + rt2800_bbp_write(rt2x00dev, 4, bbp); + + rt2800_rfcsr_read(rt2x00dev, 31, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR31_RX_H20M, bw40); + rt2800_rfcsr_write(rt2x00dev, 31, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 1); + rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); + + /* + * Set power & frequency of passband test tone + */ + rt2800_bbp_write(rt2x00dev, 24, 0); + + for (i = 0; i < 100; i++) { + rt2800_bbp_write(rt2x00dev, 25, 0x90); + msleep(1); + + rt2800_bbp_read(rt2x00dev, 55, &passband); + if (passband) + break; + } + + /* + * Set power & frequency of stopband test tone + */ + rt2800_bbp_write(rt2x00dev, 24, 0x06); + + for (i = 0; i < 100; i++) { + rt2800_bbp_write(rt2x00dev, 25, 0x90); + msleep(1); + + rt2800_bbp_read(rt2x00dev, 55, &stopband); + + if ((passband - stopband) <= filter_target) { + rfcsr24++; + overtuned += ((passband - stopband) == filter_target); + } else + break; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + } + + rfcsr24 -= !!overtuned; + + rt2800_rfcsr_write(rt2x00dev, 24, rfcsr24); + return rfcsr24; +} + +static void rt2800_rf_init_calibration(struct rt2x00_dev *rt2x00dev, + const unsigned int rf_reg) +{ + u8 rfcsr; + + rt2800_rfcsr_read(rt2x00dev, rf_reg, &rfcsr); + rt2x00_set_field8(&rfcsr, FIELD8(0x80), 1); + rt2800_rfcsr_write(rt2x00dev, rf_reg, rfcsr); + msleep(1); + rt2x00_set_field8(&rfcsr, FIELD8(0x80), 0); + rt2800_rfcsr_write(rt2x00dev, rf_reg, rfcsr); +} + +static void rt2800_rx_filter_calibration(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 filter_tgt_bw20; + u8 filter_tgt_bw40; + u8 rfcsr, bbp; + + /* + * TODO: sync filter_tgt values with vendor driver + */ + if (rt2x00_rt(rt2x00dev, RT3070)) { + filter_tgt_bw20 = 0x16; + filter_tgt_bw40 = 0x19; + } else { + filter_tgt_bw20 = 0x13; + filter_tgt_bw40 = 0x15; + } + + drv_data->calibration_bw20 = + rt2800_init_rx_filter(rt2x00dev, false, filter_tgt_bw20); + drv_data->calibration_bw40 = + rt2800_init_rx_filter(rt2x00dev, true, filter_tgt_bw40); + + /* + * Save BBP 25 & 26 values for later use in channel switching (for 3052) + */ + rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25); + rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26); + + /* + * Set back to initial state + */ + rt2800_bbp_write(rt2x00dev, 24, 0); + + rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR22_BASEBAND_LOOPBACK, 0); + rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); + + /* + * Set BBP back to BW20 + */ + rt2800_bbp_read(rt2x00dev, 4, &bbp); + rt2x00_set_field8(&bbp, BBP4_BANDWIDTH, 0); + rt2800_bbp_write(rt2x00dev, 4, bbp); +} + +static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 min_gain, rfcsr, bbp; + u16 eeprom; + + rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); + + rt2x00_set_field8(&rfcsr, RFCSR17_TX_LO1_EN, 0); + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { + if (!rt2x00_has_cap_external_lna_bg(rt2x00dev)) + rt2x00_set_field8(&rfcsr, RFCSR17_R, 1); + } + + min_gain = rt2x00_rt(rt2x00dev, RT3070) ? 1 : 2; + if (drv_data->txmixer_gain_24g >= min_gain) { + rt2x00_set_field8(&rfcsr, RFCSR17_TXMIXER_GAIN, + drv_data->txmixer_gain_24g); + } + + rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); + + if (rt2x00_rt(rt2x00dev, RT3090)) { + /* Turn off unused DAC1 and ADC1 to reduce power consumption */ + rt2800_bbp_read(rt2x00dev, 138, &bbp); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) + rt2x00_set_field8(&bbp, BBP138_RX_ADC1, 0); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) + rt2x00_set_field8(&bbp, BBP138_TX_DAC1, 1); + rt2800_bbp_write(rt2x00dev, 138, bbp); + } + + if (rt2x00_rt(rt2x00dev, RT3070)) { + rt2800_rfcsr_read(rt2x00dev, 27, &rfcsr); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) + rt2x00_set_field8(&rfcsr, RFCSR27_R1, 3); + else + rt2x00_set_field8(&rfcsr, RFCSR27_R1, 0); + rt2x00_set_field8(&rfcsr, RFCSR27_R2, 0); + rt2x00_set_field8(&rfcsr, RFCSR27_R3, 0); + rt2x00_set_field8(&rfcsr, RFCSR27_R4, 0); + rt2800_rfcsr_write(rt2x00dev, 27, rfcsr); + } else if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3390)) { + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 15, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR15_TX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 15, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 20, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR20_RX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 20, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 21, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR21_RX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 21, rfcsr); + } +} + +static void rt2800_normal_mode_setup_3593(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 rfcsr; + u8 tx_gain; + + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr); + tx_gain = rt2x00_get_field8(drv_data->txmixer_gain_24g, + RFCSR17_TXMIXER_GAIN); + rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, tx_gain); + rt2800_rfcsr_write(rt2x00dev, 51, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 38, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 39, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR39_RX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 39, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + /* TODO: enable stream mode */ +} + +static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev) +{ + u8 reg; + u16 eeprom; + + /* Turn off unused DAC1 and ADC1 to reduce power consumption */ + rt2800_bbp_read(rt2x00dev, 138, ®); + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) + rt2x00_set_field8(®, BBP138_RX_ADC1, 0); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) + rt2x00_set_field8(®, BBP138_TX_DAC1, 1); + rt2800_bbp_write(rt2x00dev, 138, reg); + + rt2800_rfcsr_read(rt2x00dev, 38, ®); + rt2x00_set_field8(®, RFCSR38_RX_LO1_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 38, reg); + + rt2800_rfcsr_read(rt2x00dev, 39, ®); + rt2x00_set_field8(®, RFCSR39_RX_LO2_EN, 0); + rt2800_rfcsr_write(rt2x00dev, 39, reg); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_rfcsr_read(rt2x00dev, 30, ®); + rt2x00_set_field8(®, RFCSR30_RX_VCM, 2); + rt2800_rfcsr_write(rt2x00dev, 30, reg); +} + +static void rt2800_init_rfcsr_305x_soc(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 0, 0x50); + rt2800_rfcsr_write(rt2x00dev, 1, 0x01); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf7); + rt2800_rfcsr_write(rt2x00dev, 3, 0x75); + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x03); + rt2800_rfcsr_write(rt2x00dev, 6, 0x02); + rt2800_rfcsr_write(rt2x00dev, 7, 0x50); + rt2800_rfcsr_write(rt2x00dev, 8, 0x39); + rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 10, 0x60); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x75); + rt2800_rfcsr_write(rt2x00dev, 13, 0x75); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x58); + rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 17, 0x92); + rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0xba); + rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x31); + rt2800_rfcsr_write(rt2x00dev, 24, 0x08); + rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + rt2800_rfcsr_write(rt2x00dev, 26, 0x25); + rt2800_rfcsr_write(rt2x00dev, 27, 0x23); + rt2800_rfcsr_write(rt2x00dev, 28, 0x13); + rt2800_rfcsr_write(rt2x00dev, 29, 0x83); + rt2800_rfcsr_write(rt2x00dev, 30, 0x00); + rt2800_rfcsr_write(rt2x00dev, 31, 0x00); +} + +static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev) +{ + u8 rfcsr; + u16 eeprom; + u32 reg; + + /* XXX vendor driver do this only for 3070 */ + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x03); + rt2800_rfcsr_write(rt2x00dev, 6, 0x02); + rt2800_rfcsr_write(rt2x00dev, 7, 0x60); + rt2800_rfcsr_write(rt2x00dev, 9, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 10, 0x41); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x58); + rt2800_rfcsr_write(rt2x00dev, 16, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 17, 0x92); + rt2800_rfcsr_write(rt2x00dev, 18, 0x2c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0xba); + rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 24, 0x16); + rt2800_rfcsr_write(rt2x00dev, 25, 0x03); + rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + } else if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090)) { + rt2800_rfcsr_write(rt2x00dev, 31, 0x14); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, + &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST)) + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + else + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); + } + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + rt2x00_set_field32(®, GPIO_SWITCH_5, 0); + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + } + + rt2800_rx_filter_calibration(rt2x00dev); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F) || + rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt2800_init_rfcsr_3290(struct rt2x00_dev *rt2x00dev) +{ + u8 rfcsr; + + rt2800_rf_init_calibration(rt2x00dev, 2); + + rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + rt2800_rfcsr_write(rt2x00dev, 3, 0x08); + rt2800_rfcsr_write(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf3); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 18, 0x02); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 25, 0x83); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x05); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + rt2800_rfcsr_write(rt2x00dev, 47, 0x00); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x98); + rt2800_rfcsr_write(rt2x00dev, 52, 0x38); + rt2800_rfcsr_write(rt2x00dev, 53, 0x00); + rt2800_rfcsr_write(rt2x00dev, 54, 0x78); + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + rt2800_rfcsr_write(rt2x00dev, 56, 0x02); + rt2800_rfcsr_write(rt2x00dev, 57, 0x80); + rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 59, 0x09); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0xc1); + + rt2800_rfcsr_read(rt2x00dev, 29, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR29_RSSI_GAIN, 3); + rt2800_rfcsr_write(rt2x00dev, 29, rfcsr); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 0, 0xf0); + rt2800_rfcsr_write(rt2x00dev, 1, 0x23); + rt2800_rfcsr_write(rt2x00dev, 2, 0x50); + rt2800_rfcsr_write(rt2x00dev, 3, 0x18); + rt2800_rfcsr_write(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0x33); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0xd2); + rt2800_rfcsr_write(rt2x00dev, 11, 0x42); + rt2800_rfcsr_write(rt2x00dev, 12, 0x1c); + rt2800_rfcsr_write(rt2x00dev, 13, 0x00); + rt2800_rfcsr_write(rt2x00dev, 14, 0x5a); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x01); + rt2800_rfcsr_write(rt2x00dev, 18, 0x45); + rt2800_rfcsr_write(rt2x00dev, 19, 0x02); + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x00); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x00); + rt2800_rfcsr_write(rt2x00dev, 24, 0x00); + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + rt2800_rfcsr_write(rt2x00dev, 26, 0x00); + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + rt2800_rfcsr_write(rt2x00dev, 28, 0x03); + rt2800_rfcsr_write(rt2x00dev, 29, 0x00); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x01); + rt2800_rfcsr_write(rt2x00dev, 35, 0x03); + rt2800_rfcsr_write(rt2x00dev, 36, 0xbd); + rt2800_rfcsr_write(rt2x00dev, 37, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 38, 0x5f); + rt2800_rfcsr_write(rt2x00dev, 39, 0xc5); + rt2800_rfcsr_write(rt2x00dev, 40, 0x33); + rt2800_rfcsr_write(rt2x00dev, 41, 0x5b); + rt2800_rfcsr_write(rt2x00dev, 42, 0x5b); + rt2800_rfcsr_write(rt2x00dev, 43, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 44, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 45, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 46, 0xdd); + rt2800_rfcsr_write(rt2x00dev, 47, 0x0d); + rt2800_rfcsr_write(rt2x00dev, 48, 0x14); + rt2800_rfcsr_write(rt2x00dev, 49, 0x00); + rt2800_rfcsr_write(rt2x00dev, 50, 0x2d); + rt2800_rfcsr_write(rt2x00dev, 51, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 52, 0x00); + rt2800_rfcsr_write(rt2x00dev, 53, 0x52); + rt2800_rfcsr_write(rt2x00dev, 54, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 55, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 56, 0x00); + rt2800_rfcsr_write(rt2x00dev, 57, 0x52); + rt2800_rfcsr_write(rt2x00dev, 58, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 59, 0x00); + rt2800_rfcsr_write(rt2x00dev, 60, 0x00); + rt2800_rfcsr_write(rt2x00dev, 61, 0x00); + rt2800_rfcsr_write(rt2x00dev, 62, 0x00); + rt2800_rfcsr_write(rt2x00dev, 63, 0x00); + + rt2800_rx_filter_calibration(rt2x00dev); + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt2800_init_rfcsr_3390(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 0, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 1, 0xe1); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 3, 0x62); + rt2800_rfcsr_write(rt2x00dev, 4, 0x40); + rt2800_rfcsr_write(rt2x00dev, 5, 0x8b); + rt2800_rfcsr_write(rt2x00dev, 6, 0x42); + rt2800_rfcsr_write(rt2x00dev, 7, 0x34); + rt2800_rfcsr_write(rt2x00dev, 8, 0x00); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc0); + rt2800_rfcsr_write(rt2x00dev, 10, 0x61); + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + rt2800_rfcsr_write(rt2x00dev, 12, 0x3b); + rt2800_rfcsr_write(rt2x00dev, 13, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 14, 0x90); + rt2800_rfcsr_write(rt2x00dev, 15, 0x53); + rt2800_rfcsr_write(rt2x00dev, 16, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 17, 0x94); + rt2800_rfcsr_write(rt2x00dev, 18, 0x5c); + rt2800_rfcsr_write(rt2x00dev, 19, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 20, 0xb2); + rt2800_rfcsr_write(rt2x00dev, 21, 0xf6); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x14); + rt2800_rfcsr_write(rt2x00dev, 24, 0x08); + rt2800_rfcsr_write(rt2x00dev, 25, 0x3d); + rt2800_rfcsr_write(rt2x00dev, 26, 0x85); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 28, 0x41); + rt2800_rfcsr_write(rt2x00dev, 29, 0x8f); + rt2800_rfcsr_write(rt2x00dev, 30, 0x20); + rt2800_rfcsr_write(rt2x00dev, 31, 0x0f); + + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + rt2x00_set_field32(®, GPIO_SWITCH_5, 0); + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + + rt2800_rx_filter_calibration(rt2x00dev); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt2800_init_rfcsr_3572(struct rt2x00_dev *rt2x00dev) +{ + u8 rfcsr; + u32 reg; + + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 0, 0x70); + rt2800_rfcsr_write(rt2x00dev, 1, 0x81); + rt2800_rfcsr_write(rt2x00dev, 2, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 3, 0x02); + rt2800_rfcsr_write(rt2x00dev, 4, 0x4c); + rt2800_rfcsr_write(rt2x00dev, 5, 0x05); + rt2800_rfcsr_write(rt2x00dev, 6, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 7, 0xd8); + rt2800_rfcsr_write(rt2x00dev, 9, 0xc3); + rt2800_rfcsr_write(rt2x00dev, 10, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 11, 0xb9); + rt2800_rfcsr_write(rt2x00dev, 12, 0x70); + rt2800_rfcsr_write(rt2x00dev, 13, 0x65); + rt2800_rfcsr_write(rt2x00dev, 14, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 15, 0x53); + rt2800_rfcsr_write(rt2x00dev, 16, 0x4c); + rt2800_rfcsr_write(rt2x00dev, 17, 0x23); + rt2800_rfcsr_write(rt2x00dev, 18, 0xac); + rt2800_rfcsr_write(rt2x00dev, 19, 0x93); + rt2800_rfcsr_write(rt2x00dev, 20, 0xb3); + rt2800_rfcsr_write(rt2x00dev, 21, 0xd0); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 24, 0x16); + rt2800_rfcsr_write(rt2x00dev, 25, 0x15); + rt2800_rfcsr_write(rt2x00dev, 26, 0x85); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 30, 0x09); + rt2800_rfcsr_write(rt2x00dev, 31, 0x10); + + rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR6_R2, 1); + rt2800_rfcsr_write(rt2x00dev, 6, rfcsr); + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + msleep(1); + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + rt2800_rx_filter_calibration(rt2x00dev); + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3xxx(rt2x00dev); +} + +static void rt3593_post_bbp_init(struct rt2x00_dev *rt2x00dev) +{ + u8 bbp; + bool txbf_enabled = false; /* FIXME */ + + rt2800_bbp_read(rt2x00dev, 105, &bbp); + if (rt2x00dev->default_ant.rx_chain_num == 1) + rt2x00_set_field8(&bbp, BBP105_MLD, 0); + else + rt2x00_set_field8(&bbp, BBP105_MLD, 1); + rt2800_bbp_write(rt2x00dev, 105, bbp); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); + rt2800_bbp_write(rt2x00dev, 82, 0x82); + rt2800_bbp_write(rt2x00dev, 106, 0x05); + rt2800_bbp_write(rt2x00dev, 104, 0x92); + rt2800_bbp_write(rt2x00dev, 88, 0x90); + rt2800_bbp_write(rt2x00dev, 148, 0xc8); + rt2800_bbp_write(rt2x00dev, 47, 0x48); + rt2800_bbp_write(rt2x00dev, 120, 0x50); + + if (txbf_enabled) + rt2800_bbp_write(rt2x00dev, 163, 0xbd); + else + rt2800_bbp_write(rt2x00dev, 163, 0x9d); + + /* SNR mapping */ + rt2800_bbp_write(rt2x00dev, 142, 6); + rt2800_bbp_write(rt2x00dev, 143, 160); + rt2800_bbp_write(rt2x00dev, 142, 7); + rt2800_bbp_write(rt2x00dev, 143, 161); + rt2800_bbp_write(rt2x00dev, 142, 8); + rt2800_bbp_write(rt2x00dev, 143, 162); + + /* ADC/DAC control */ + rt2800_bbp_write(rt2x00dev, 31, 0x08); + + /* RX AGC energy lower bound in log2 */ + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + /* FIXME: BBP 105 owerwrite? */ + rt2800_bbp_write(rt2x00dev, 105, 0x04); + +} + +static void rt2800_init_rfcsr_3593(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u32 reg; + u8 rfcsr; + + /* Disable GPIO #4 and #7 function for LAN PE control */ + rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®); + rt2x00_set_field32(®, GPIO_SWITCH_4, 0); + rt2x00_set_field32(®, GPIO_SWITCH_7, 0); + rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg); + + /* Initialize default register values */ + rt2800_rfcsr_write(rt2x00dev, 1, 0x03); + rt2800_rfcsr_write(rt2x00dev, 3, 0x80); + rt2800_rfcsr_write(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0x40); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf1); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 11, 0x40); + rt2800_rfcsr_write(rt2x00dev, 12, 0x4e); + rt2800_rfcsr_write(rt2x00dev, 13, 0x12); + rt2800_rfcsr_write(rt2x00dev, 18, 0x40); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x78); + rt2800_rfcsr_write(rt2x00dev, 33, 0x3b); + rt2800_rfcsr_write(rt2x00dev, 34, 0x3c); + rt2800_rfcsr_write(rt2x00dev, 35, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 38, 0x86); + rt2800_rfcsr_write(rt2x00dev, 39, 0x23); + rt2800_rfcsr_write(rt2x00dev, 44, 0xd3); + rt2800_rfcsr_write(rt2x00dev, 45, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 46, 0x60); + rt2800_rfcsr_write(rt2x00dev, 49, 0x8e); + rt2800_rfcsr_write(rt2x00dev, 50, 0x86); + rt2800_rfcsr_write(rt2x00dev, 51, 0x75); + rt2800_rfcsr_write(rt2x00dev, 52, 0x45); + rt2800_rfcsr_write(rt2x00dev, 53, 0x18); + rt2800_rfcsr_write(rt2x00dev, 54, 0x18); + rt2800_rfcsr_write(rt2x00dev, 55, 0x18); + rt2800_rfcsr_write(rt2x00dev, 56, 0xdb); + rt2800_rfcsr_write(rt2x00dev, 57, 0x6e); + + /* Initiate calibration */ + /* TODO: use rt2800_rf_init_calibration ? */ + rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 2, rfcsr); + + rt2800_adjust_freq_offset(rt2x00dev); + + rt2800_rfcsr_read(rt2x00dev, 18, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR18_XO_TUNE_BYPASS, 1); + rt2800_rfcsr_write(rt2x00dev, 18, rfcsr); + + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3); + rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + usleep_range(1000, 1500); + rt2800_register_read(rt2x00dev, LDO_CFG0, ®); + rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0); + rt2800_register_write(rt2x00dev, LDO_CFG0, reg); + + /* Set initial values for RX filter calibration */ + drv_data->calibration_bw20 = 0x1f; + drv_data->calibration_bw40 = 0x2f; + + /* Save BBP 25 & 26 values for later use in channel switching */ + rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25); + rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26); + + rt2800_led_open_drain_enable(rt2x00dev); + rt2800_normal_mode_setup_3593(rt2x00dev); + + rt3593_post_bbp_init(rt2x00dev); + + /* TODO: enable stream mode support */ +} + +static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 2); + + rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + rt2800_rfcsr_write(rt2x00dev, 3, 0x88); + rt2800_rfcsr_write(rt2x00dev, 5, 0x10); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); + else + rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 14, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x03); + rt2800_rfcsr_write(rt2x00dev, 19, 0x00); + + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x00); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 23, 0x00); + rt2800_rfcsr_write(rt2x00dev, 24, 0x00); + if (rt2x00_is_usb(rt2x00dev) && + rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + else + rt2800_rfcsr_write(rt2x00dev, 25, 0xc0); + rt2800_rfcsr_write(rt2x00dev, 26, 0x00); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x07); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x08); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + + rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd2); + rt2800_rfcsr_write(rt2x00dev, 43, 0x9a); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + else + rt2800_rfcsr_write(rt2x00dev, 46, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 47, 0x00); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x94); + + rt2800_rfcsr_write(rt2x00dev, 52, 0x38); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 53, 0x00); + else + rt2800_rfcsr_write(rt2x00dev, 53, 0x84); + rt2800_rfcsr_write(rt2x00dev, 54, 0x78); + rt2800_rfcsr_write(rt2x00dev, 55, 0x44); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 56, 0x42); + else + rt2800_rfcsr_write(rt2x00dev, 56, 0x22); + rt2800_rfcsr_write(rt2x00dev, 57, 0x80); + rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 59, 0x8f); + + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { + if (rt2x00_is_usb(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 61, 0xd1); + else + rt2800_rfcsr_write(rt2x00dev, 61, 0xd5); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 61, 0xdd); + else + rt2800_rfcsr_write(rt2x00dev, 61, 0xb5); + } + rt2800_rfcsr_write(rt2x00dev, 62, 0x00); + rt2800_rfcsr_write(rt2x00dev, 63, 0x00); + + rt2800_normal_mode_setup_5xxx(rt2x00dev); + + rt2800_led_open_drain_enable(rt2x00dev); +} + +static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 2); + + rt2800_rfcsr_write(rt2x00dev, 1, 0x17); + rt2800_rfcsr_write(rt2x00dev, 3, 0x88); + rt2800_rfcsr_write(rt2x00dev, 5, 0x10); + rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 14, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x03); + rt2800_rfcsr_write(rt2x00dev, 19, 0x4d); + rt2800_rfcsr_write(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write(rt2x00dev, 21, 0x8d); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 23, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 24, 0x44); + rt2800_rfcsr_write(rt2x00dev, 25, 0x80); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x20); + rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); + rt2800_rfcsr_write(rt2x00dev, 34, 0x07); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x08); + rt2800_rfcsr_write(rt2x00dev, 38, 0x89); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x9b); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + rt2800_rfcsr_write(rt2x00dev, 47, 0x0c); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x94); + rt2800_rfcsr_write(rt2x00dev, 50, 0x94); + rt2800_rfcsr_write(rt2x00dev, 51, 0x3a); + rt2800_rfcsr_write(rt2x00dev, 52, 0x48); + rt2800_rfcsr_write(rt2x00dev, 53, 0x44); + rt2800_rfcsr_write(rt2x00dev, 54, 0x38); + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + rt2800_rfcsr_write(rt2x00dev, 56, 0xa1); + rt2800_rfcsr_write(rt2x00dev, 57, 0x00); + rt2800_rfcsr_write(rt2x00dev, 58, 0x39); + rt2800_rfcsr_write(rt2x00dev, 59, 0x07); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0x91); + rt2800_rfcsr_write(rt2x00dev, 62, 0x39); + rt2800_rfcsr_write(rt2x00dev, 63, 0x07); + + rt2800_normal_mode_setup_5xxx(rt2x00dev); + + rt2800_led_open_drain_enable(rt2x00dev); +} + +static void rt2800_init_rfcsr_5592(struct rt2x00_dev *rt2x00dev) +{ + rt2800_rf_init_calibration(rt2x00dev, 30); + + rt2800_rfcsr_write(rt2x00dev, 1, 0x3F); + rt2800_rfcsr_write(rt2x00dev, 3, 0x08); + rt2800_rfcsr_write(rt2x00dev, 5, 0x10); + rt2800_rfcsr_write(rt2x00dev, 6, 0xE4); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 14, 0x00); + rt2800_rfcsr_write(rt2x00dev, 15, 0x00); + rt2800_rfcsr_write(rt2x00dev, 16, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x03); + rt2800_rfcsr_write(rt2x00dev, 19, 0x4D); + rt2800_rfcsr_write(rt2x00dev, 20, 0x10); + rt2800_rfcsr_write(rt2x00dev, 21, 0x8D); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 33, 0xC0); + rt2800_rfcsr_write(rt2x00dev, 34, 0x07); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 47, 0x0C); + rt2800_rfcsr_write(rt2x00dev, 53, 0x22); + rt2800_rfcsr_write(rt2x00dev, 63, 0x07); + + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + msleep(1); + + rt2800_adjust_freq_offset(rt2x00dev); + + /* Enable DC filter */ + if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_normal_mode_setup_5xxx(rt2x00dev); + + if (rt2x00_rt_rev_lt(rt2x00dev, RT5592, REV_RT5592C)) + rt2800_rfcsr_write(rt2x00dev, 27, 0x03); + + rt2800_led_open_drain_enable(rt2x00dev); +} + +static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) +{ + if (rt2800_is_305x_soc(rt2x00dev)) { + rt2800_init_rfcsr_305x_soc(rt2x00dev); + return; + } + + switch (rt2x00dev->chip.rt) { + case RT3070: + case RT3071: + case RT3090: + rt2800_init_rfcsr_30xx(rt2x00dev); + break; + case RT3290: + rt2800_init_rfcsr_3290(rt2x00dev); + break; + case RT3352: + rt2800_init_rfcsr_3352(rt2x00dev); + break; + case RT3390: + rt2800_init_rfcsr_3390(rt2x00dev); + break; + case RT3572: + rt2800_init_rfcsr_3572(rt2x00dev); + break; + case RT3593: + rt2800_init_rfcsr_3593(rt2x00dev); + break; + case RT5390: + rt2800_init_rfcsr_5390(rt2x00dev); + break; + case RT5392: + rt2800_init_rfcsr_5392(rt2x00dev); + break; + case RT5592: + rt2800_init_rfcsr_5592(rt2x00dev); + break; + } +} + +int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 word; + + /* + * Initialize MAC registers. + */ + if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || + rt2800_init_registers(rt2x00dev))) + return -EIO; + + /* + * Wait BBP/RF to wake up. + */ + if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev))) + return -EIO; + + /* + * Send signal during boot time to initialize firmware. + */ + rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + if (rt2x00_is_usb(rt2x00dev)) + rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0); + rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); + msleep(1); + + /* + * Make sure BBP is up and running. + */ + if (unlikely(rt2800_wait_bbp_ready(rt2x00dev))) + return -EIO; + + /* + * Initialize BBP/RF registers. + */ + rt2800_init_bbp(rt2x00dev); + rt2800_init_rfcsr(rt2x00dev); + + if (rt2x00_is_usb(rt2x00dev) && + (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3572))) { + udelay(200); + rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0); + udelay(10); + } + + /* + * Enable RX. + */ + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + udelay(50); + + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1); + rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1); + rt2x00_set_field32(®, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2); + rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); + rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); + + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + /* + * Initialize LED control + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_AG_CONF, 0xff, + word & 0xff, (word >> 8) & 0xff); + + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_ACT_CONF, 0xff, + word & 0xff, (word >> 8) & 0xff); + + rt2800_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word); + rt2800_mcu_request(rt2x00dev, MCU_LED_LED_POLARITY, 0xff, + word & 0xff, (word >> 8) & 0xff); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_enable_radio); + +void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2800_disable_wpdma(rt2x00dev); + + /* Wait for DMA, ignore error */ + rt2800_wait_wpdma_ready(rt2x00dev); + + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 0); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); +} +EXPORT_SYMBOL_GPL(rt2800_disable_radio); + +int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 efuse_ctrl_reg; + + if (rt2x00_rt(rt2x00dev, RT3290)) + efuse_ctrl_reg = EFUSE_CTRL_3290; + else + efuse_ctrl_reg = EFUSE_CTRL; + + rt2800_register_read(rt2x00dev, efuse_ctrl_reg, ®); + return rt2x00_get_field32(reg, EFUSE_CTRL_PRESENT); +} +EXPORT_SYMBOL_GPL(rt2800_efuse_detect); + +static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i) +{ + u32 reg; + u16 efuse_ctrl_reg; + u16 efuse_data0_reg; + u16 efuse_data1_reg; + u16 efuse_data2_reg; + u16 efuse_data3_reg; + + if (rt2x00_rt(rt2x00dev, RT3290)) { + efuse_ctrl_reg = EFUSE_CTRL_3290; + efuse_data0_reg = EFUSE_DATA0_3290; + efuse_data1_reg = EFUSE_DATA1_3290; + efuse_data2_reg = EFUSE_DATA2_3290; + efuse_data3_reg = EFUSE_DATA3_3290; + } else { + efuse_ctrl_reg = EFUSE_CTRL; + efuse_data0_reg = EFUSE_DATA0; + efuse_data1_reg = EFUSE_DATA1; + efuse_data2_reg = EFUSE_DATA2; + efuse_data3_reg = EFUSE_DATA3; + } + mutex_lock(&rt2x00dev->csr_mutex); + + rt2800_register_read_lock(rt2x00dev, efuse_ctrl_reg, ®); + rt2x00_set_field32(®, EFUSE_CTRL_ADDRESS_IN, i); + rt2x00_set_field32(®, EFUSE_CTRL_MODE, 0); + rt2x00_set_field32(®, EFUSE_CTRL_KICK, 1); + rt2800_register_write_lock(rt2x00dev, efuse_ctrl_reg, reg); + + /* Wait until the EEPROM has been loaded */ + rt2800_regbusy_read(rt2x00dev, efuse_ctrl_reg, EFUSE_CTRL_KICK, ®); + /* Apparently the data is read from end to start */ + rt2800_register_read_lock(rt2x00dev, efuse_data3_reg, ®); + /* The returned value is in CPU order, but eeprom is le */ + *(u32 *)&rt2x00dev->eeprom[i] = cpu_to_le32(reg); + rt2800_register_read_lock(rt2x00dev, efuse_data2_reg, ®); + *(u32 *)&rt2x00dev->eeprom[i + 2] = cpu_to_le32(reg); + rt2800_register_read_lock(rt2x00dev, efuse_data1_reg, ®); + *(u32 *)&rt2x00dev->eeprom[i + 4] = cpu_to_le32(reg); + rt2800_register_read_lock(rt2x00dev, efuse_data0_reg, ®); + *(u32 *)&rt2x00dev->eeprom[i + 6] = cpu_to_le32(reg); + + mutex_unlock(&rt2x00dev->csr_mutex); +} + +int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + for (i = 0; i < EEPROM_SIZE / sizeof(u16); i += 8) + rt2800_efuse_read(rt2x00dev, i); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse); + +static u8 rt2800_get_txmixer_gain_24g(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + + if (rt2x00_rt(rt2x00dev, RT3593)) + return 0; + + rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word); + if ((word & 0x00ff) != 0x00ff) + return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL); + + return 0; +} + +static u8 rt2800_get_txmixer_gain_5g(struct rt2x00_dev *rt2x00dev) +{ + u16 word; + + if (rt2x00_rt(rt2x00dev, RT3593)) + return 0; + + rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word); + if ((word & 0x00ff) != 0x00ff) + return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL); + + return 0; +} + +static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u16 word; + u8 *mac; + u8 default_lna_gain; + int retval; + + /* + * Read the EEPROM. + */ + retval = rt2800_read_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Start validation of the data that has been read. + */ + mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + if (!is_valid_ether_addr(mac)) { + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); + } + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); + rt2x00_set_field16(&word, EEPROM_NIC_CONF0_TXPATH, 1); + rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RF_TYPE, RF2820); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); + } else if (rt2x00_rt(rt2x00dev, RT2860) || + rt2x00_rt(rt2x00dev, RT2872)) { + /* + * There is a max of 2 RX streams for RT28x0 series + */ + if (rt2x00_get_field16(word, EEPROM_NIC_CONF0_RXPATH) > 2) + rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word); + } + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word); + if (word == 0xffff) { + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_HW_RADIO, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_LNA_2G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_LNA_5G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_CARDBUS_ACCEL, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_SB_2G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_SB_5G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_WPS_PBC, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_2G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BW40M_5G, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BROADBAND_EXT_LNA, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_ANT_DIVERSITY, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_INTERNAL_TX_ALC, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BT_COEXIST, 0); + rt2x00_set_field16(&word, EEPROM_NIC_CONF1_DAC_TEST, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); + } + + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); + if ((word & 0x00ff) == 0x00ff) { + rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); + } + if ((word & 0xff00) == 0xff00) { + rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE, + LED_MODE_TXRX_ACTIVITY); + rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221); + rt2800_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8); + rt2x00_eeprom_dbg(rt2x00dev, "Led Mode: 0x%04x\n", word); + } + + /* + * During the LNA validation we are going to use + * lna0 as correct value. Note that EEPROM_LNA + * is never validated. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &word); + default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word); + + drv_data->txmixer_gain_24g = rt2800_get_txmixer_gain_24g(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0); + if (!rt2x00_rt(rt2x00dev, RT3593)) { + if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 || + rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff) + rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1, + default_lna_gain); + } + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word); + + drv_data->txmixer_gain_5g = rt2800_get_txmixer_gain_5g(rt2x00dev); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0); + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word); + + rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word); + if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10) + rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0); + if (!rt2x00_rt(rt2x00dev, RT3593)) { + if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 || + rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff) + rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2, + default_lna_gain); + } + rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word); + + if (rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &word); + if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0x00 || + rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0xff) + rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1, + default_lna_gain); + if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0x00 || + rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0xff) + rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1, + default_lna_gain); + rt2800_eeprom_write(rt2x00dev, EEPROM_EXT_LNA2, word); + } + + return 0; +} + +static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u16 value; + u16 eeprom; + u16 rf; + + /* + * Read EEPROM word for configuration. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + + /* + * Identify RF chipset by EEPROM value + * RT28xx/RT30xx: defined in "EEPROM_NIC_CONF0_RF_TYPE" field + * RT53xx: defined in "EEPROM_CHIP_ID" field + */ + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) + rt2800_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf); + else + rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); + + switch (rf) { + case RF2820: + case RF2850: + case RF2720: + case RF2750: + case RF3020: + case RF2020: + case RF3021: + case RF3022: + case RF3052: + case RF3053: + case RF3070: + case RF3290: + case RF3320: + case RF3322: + case RF5360: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + case RF5592: + break; + default: + rt2x00_err(rt2x00dev, "Invalid RF chipset 0x%04x detected\n", + rf); + return -ENODEV; + } + + rt2x00_set_rf(rt2x00dev, rf); + + /* + * Identify default antenna configuration. + */ + rt2x00dev->default_ant.tx_chain_num = + rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH); + rt2x00dev->default_ant.rx_chain_num = + rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH); + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + + if (rt2x00_rt(rt2x00dev, RT3070) || + rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3352) || + rt2x00_rt(rt2x00dev, RT3390)) { + value = rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_ANT_DIVERSITY); + switch (value) { + case 0: + case 1: + case 2: + rt2x00dev->default_ant.tx = ANTENNA_A; + rt2x00dev->default_ant.rx = ANTENNA_A; + break; + case 3: + rt2x00dev->default_ant.tx = ANTENNA_A; + rt2x00dev->default_ant.rx = ANTENNA_B; + break; + } + } else { + rt2x00dev->default_ant.tx = ANTENNA_A; + rt2x00dev->default_ant.rx = ANTENNA_A; + } + + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { + rt2x00dev->default_ant.tx = ANTENNA_HW_DIVERSITY; /* Unused */ + rt2x00dev->default_ant.rx = ANTENNA_HW_DIVERSITY; /* Unused */ + } + + /* + * Determine external LNA informations. + */ + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_LNA_5G)) + __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_LNA_2G)) + __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_HW_RADIO)) + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + + /* + * Detect if this device has Bluetooth co-existence. + */ + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_BT_COEXIST)) + __set_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags); + + /* + * Read frequency offset and RF programming sequence. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); + + /* + * Store led settings, for correct led behaviour. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + rt2800_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + rt2800_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); + rt2800_init_led(rt2x00dev, &rt2x00dev->led_qual, LED_TYPE_QUALITY); + + rt2x00dev->led_mcu_reg = eeprom; +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + /* + * Check if support EIRP tx power limit feature. + */ + rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom); + + if (rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ) < + EIRP_MAX_TX_POWER_LIMIT) + __set_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags); + + return 0; +} + +/* + * RF value list for rt28xx + * Supports: 2.4 GHz (all) & 5.2 GHz (RF2850 & RF2750) + */ +static const struct rf_channel rf_vals[] = { + { 1, 0x18402ecc, 0x184c0786, 0x1816b455, 0x1800510b }, + { 2, 0x18402ecc, 0x184c0786, 0x18168a55, 0x1800519f }, + { 3, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800518b }, + { 4, 0x18402ecc, 0x184c078a, 0x18168a55, 0x1800519f }, + { 5, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800518b }, + { 6, 0x18402ecc, 0x184c078e, 0x18168a55, 0x1800519f }, + { 7, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800518b }, + { 8, 0x18402ecc, 0x184c0792, 0x18168a55, 0x1800519f }, + { 9, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800518b }, + { 10, 0x18402ecc, 0x184c0796, 0x18168a55, 0x1800519f }, + { 11, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800518b }, + { 12, 0x18402ecc, 0x184c079a, 0x18168a55, 0x1800519f }, + { 13, 0x18402ecc, 0x184c079e, 0x18168a55, 0x1800518b }, + { 14, 0x18402ecc, 0x184c07a2, 0x18168a55, 0x18005193 }, + + /* 802.11 UNI / HyperLan 2 */ + { 36, 0x18402ecc, 0x184c099a, 0x18158a55, 0x180ed1a3 }, + { 38, 0x18402ecc, 0x184c099e, 0x18158a55, 0x180ed193 }, + { 40, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed183 }, + { 44, 0x18402ec8, 0x184c0682, 0x18158a55, 0x180ed1a3 }, + { 46, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed18b }, + { 48, 0x18402ec8, 0x184c0686, 0x18158a55, 0x180ed19b }, + { 52, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed193 }, + { 54, 0x18402ec8, 0x184c068a, 0x18158a55, 0x180ed1a3 }, + { 56, 0x18402ec8, 0x184c068e, 0x18158a55, 0x180ed18b }, + { 60, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed183 }, + { 62, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed193 }, + { 64, 0x18402ec8, 0x184c0692, 0x18158a55, 0x180ed1a3 }, + + /* 802.11 HyperLan 2 */ + { 100, 0x18402ec8, 0x184c06b2, 0x18178a55, 0x180ed783 }, + { 102, 0x18402ec8, 0x184c06b2, 0x18578a55, 0x180ed793 }, + { 104, 0x18402ec8, 0x185c06b2, 0x18578a55, 0x180ed1a3 }, + { 108, 0x18402ecc, 0x185c0a32, 0x18578a55, 0x180ed193 }, + { 110, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed183 }, + { 112, 0x18402ecc, 0x184c0a36, 0x18178a55, 0x180ed19b }, + { 116, 0x18402ecc, 0x184c0a3a, 0x18178a55, 0x180ed1a3 }, + { 118, 0x18402ecc, 0x184c0a3e, 0x18178a55, 0x180ed193 }, + { 120, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed183 }, + { 124, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed193 }, + { 126, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed15b }, + { 128, 0x18402ec4, 0x184c0382, 0x18178a55, 0x180ed1a3 }, + { 132, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed18b }, + { 134, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed193 }, + { 136, 0x18402ec4, 0x184c0386, 0x18178a55, 0x180ed19b }, + { 140, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed183 }, + + /* 802.11 UNII */ + { 149, 0x18402ec4, 0x184c038a, 0x18178a55, 0x180ed1a7 }, + { 151, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed187 }, + { 153, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed18f }, + { 157, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed19f }, + { 159, 0x18402ec4, 0x184c038e, 0x18178a55, 0x180ed1a7 }, + { 161, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed187 }, + { 165, 0x18402ec4, 0x184c0392, 0x18178a55, 0x180ed197 }, + { 167, 0x18402ec4, 0x184c03d2, 0x18179855, 0x1815531f }, + { 169, 0x18402ec4, 0x184c03d2, 0x18179855, 0x18155327 }, + { 171, 0x18402ec4, 0x184c03d6, 0x18179855, 0x18155307 }, + { 173, 0x18402ec4, 0x184c03d6, 0x18179855, 0x1815530f }, + + /* 802.11 Japan */ + { 184, 0x15002ccc, 0x1500491e, 0x1509be55, 0x150c0a0b }, + { 188, 0x15002ccc, 0x15004922, 0x1509be55, 0x150c0a13 }, + { 192, 0x15002ccc, 0x15004926, 0x1509be55, 0x150c0a1b }, + { 196, 0x15002ccc, 0x1500492a, 0x1509be55, 0x150c0a23 }, + { 208, 0x15002ccc, 0x1500493a, 0x1509be55, 0x150c0a13 }, + { 212, 0x15002ccc, 0x1500493e, 0x1509be55, 0x150c0a1b }, + { 216, 0x15002ccc, 0x15004982, 0x1509be55, 0x150c0a23 }, +}; + +/* + * RF value list for rt3xxx + * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053) + */ +static const struct rf_channel rf_vals_3x[] = { + {1, 241, 2, 2 }, + {2, 241, 2, 7 }, + {3, 242, 2, 2 }, + {4, 242, 2, 7 }, + {5, 243, 2, 2 }, + {6, 243, 2, 7 }, + {7, 244, 2, 2 }, + {8, 244, 2, 7 }, + {9, 245, 2, 2 }, + {10, 245, 2, 7 }, + {11, 246, 2, 2 }, + {12, 246, 2, 7 }, + {13, 247, 2, 2 }, + {14, 248, 2, 4 }, + + /* 802.11 UNI / HyperLan 2 */ + {36, 0x56, 0, 4}, + {38, 0x56, 0, 6}, + {40, 0x56, 0, 8}, + {44, 0x57, 0, 0}, + {46, 0x57, 0, 2}, + {48, 0x57, 0, 4}, + {52, 0x57, 0, 8}, + {54, 0x57, 0, 10}, + {56, 0x58, 0, 0}, + {60, 0x58, 0, 4}, + {62, 0x58, 0, 6}, + {64, 0x58, 0, 8}, + + /* 802.11 HyperLan 2 */ + {100, 0x5b, 0, 8}, + {102, 0x5b, 0, 10}, + {104, 0x5c, 0, 0}, + {108, 0x5c, 0, 4}, + {110, 0x5c, 0, 6}, + {112, 0x5c, 0, 8}, + {116, 0x5d, 0, 0}, + {118, 0x5d, 0, 2}, + {120, 0x5d, 0, 4}, + {124, 0x5d, 0, 8}, + {126, 0x5d, 0, 10}, + {128, 0x5e, 0, 0}, + {132, 0x5e, 0, 4}, + {134, 0x5e, 0, 6}, + {136, 0x5e, 0, 8}, + {140, 0x5f, 0, 0}, + + /* 802.11 UNII */ + {149, 0x5f, 0, 9}, + {151, 0x5f, 0, 11}, + {153, 0x60, 0, 1}, + {157, 0x60, 0, 5}, + {159, 0x60, 0, 7}, + {161, 0x60, 0, 9}, + {165, 0x61, 0, 1}, + {167, 0x61, 0, 3}, + {169, 0x61, 0, 5}, + {171, 0x61, 0, 7}, + {173, 0x61, 0, 9}, +}; + +static const struct rf_channel rf_vals_5592_xtal20[] = { + /* Channel, N, K, mod, R */ + {1, 482, 4, 10, 3}, + {2, 483, 4, 10, 3}, + {3, 484, 4, 10, 3}, + {4, 485, 4, 10, 3}, + {5, 486, 4, 10, 3}, + {6, 487, 4, 10, 3}, + {7, 488, 4, 10, 3}, + {8, 489, 4, 10, 3}, + {9, 490, 4, 10, 3}, + {10, 491, 4, 10, 3}, + {11, 492, 4, 10, 3}, + {12, 493, 4, 10, 3}, + {13, 494, 4, 10, 3}, + {14, 496, 8, 10, 3}, + {36, 172, 8, 12, 1}, + {38, 173, 0, 12, 1}, + {40, 173, 4, 12, 1}, + {42, 173, 8, 12, 1}, + {44, 174, 0, 12, 1}, + {46, 174, 4, 12, 1}, + {48, 174, 8, 12, 1}, + {50, 175, 0, 12, 1}, + {52, 175, 4, 12, 1}, + {54, 175, 8, 12, 1}, + {56, 176, 0, 12, 1}, + {58, 176, 4, 12, 1}, + {60, 176, 8, 12, 1}, + {62, 177, 0, 12, 1}, + {64, 177, 4, 12, 1}, + {100, 183, 4, 12, 1}, + {102, 183, 8, 12, 1}, + {104, 184, 0, 12, 1}, + {106, 184, 4, 12, 1}, + {108, 184, 8, 12, 1}, + {110, 185, 0, 12, 1}, + {112, 185, 4, 12, 1}, + {114, 185, 8, 12, 1}, + {116, 186, 0, 12, 1}, + {118, 186, 4, 12, 1}, + {120, 186, 8, 12, 1}, + {122, 187, 0, 12, 1}, + {124, 187, 4, 12, 1}, + {126, 187, 8, 12, 1}, + {128, 188, 0, 12, 1}, + {130, 188, 4, 12, 1}, + {132, 188, 8, 12, 1}, + {134, 189, 0, 12, 1}, + {136, 189, 4, 12, 1}, + {138, 189, 8, 12, 1}, + {140, 190, 0, 12, 1}, + {149, 191, 6, 12, 1}, + {151, 191, 10, 12, 1}, + {153, 192, 2, 12, 1}, + {155, 192, 6, 12, 1}, + {157, 192, 10, 12, 1}, + {159, 193, 2, 12, 1}, + {161, 193, 6, 12, 1}, + {165, 194, 2, 12, 1}, + {184, 164, 0, 12, 1}, + {188, 164, 4, 12, 1}, + {192, 165, 8, 12, 1}, + {196, 166, 0, 12, 1}, +}; + +static const struct rf_channel rf_vals_5592_xtal40[] = { + /* Channel, N, K, mod, R */ + {1, 241, 2, 10, 3}, + {2, 241, 7, 10, 3}, + {3, 242, 2, 10, 3}, + {4, 242, 7, 10, 3}, + {5, 243, 2, 10, 3}, + {6, 243, 7, 10, 3}, + {7, 244, 2, 10, 3}, + {8, 244, 7, 10, 3}, + {9, 245, 2, 10, 3}, + {10, 245, 7, 10, 3}, + {11, 246, 2, 10, 3}, + {12, 246, 7, 10, 3}, + {13, 247, 2, 10, 3}, + {14, 248, 4, 10, 3}, + {36, 86, 4, 12, 1}, + {38, 86, 6, 12, 1}, + {40, 86, 8, 12, 1}, + {42, 86, 10, 12, 1}, + {44, 87, 0, 12, 1}, + {46, 87, 2, 12, 1}, + {48, 87, 4, 12, 1}, + {50, 87, 6, 12, 1}, + {52, 87, 8, 12, 1}, + {54, 87, 10, 12, 1}, + {56, 88, 0, 12, 1}, + {58, 88, 2, 12, 1}, + {60, 88, 4, 12, 1}, + {62, 88, 6, 12, 1}, + {64, 88, 8, 12, 1}, + {100, 91, 8, 12, 1}, + {102, 91, 10, 12, 1}, + {104, 92, 0, 12, 1}, + {106, 92, 2, 12, 1}, + {108, 92, 4, 12, 1}, + {110, 92, 6, 12, 1}, + {112, 92, 8, 12, 1}, + {114, 92, 10, 12, 1}, + {116, 93, 0, 12, 1}, + {118, 93, 2, 12, 1}, + {120, 93, 4, 12, 1}, + {122, 93, 6, 12, 1}, + {124, 93, 8, 12, 1}, + {126, 93, 10, 12, 1}, + {128, 94, 0, 12, 1}, + {130, 94, 2, 12, 1}, + {132, 94, 4, 12, 1}, + {134, 94, 6, 12, 1}, + {136, 94, 8, 12, 1}, + {138, 94, 10, 12, 1}, + {140, 95, 0, 12, 1}, + {149, 95, 9, 12, 1}, + {151, 95, 11, 12, 1}, + {153, 96, 1, 12, 1}, + {155, 96, 3, 12, 1}, + {157, 96, 5, 12, 1}, + {159, 96, 7, 12, 1}, + {161, 96, 9, 12, 1}, + {165, 97, 1, 12, 1}, + {184, 82, 0, 12, 1}, + {188, 82, 4, 12, 1}, + {192, 82, 8, 12, 1}, + {196, 83, 0, 12, 1}, +}; + +static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + struct channel_info *info; + char *default_power1; + char *default_power2; + char *default_power3; + unsigned int i; + u32 reg; + + /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + /* + * Initialize all hw fields. + */ + rt2x00dev->hw->flags = + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SUPPORTS_HT_CCK_RATES; + + /* + * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices + * unless we are capable of sending the buffered frames out after the + * DTIM transmission using rt2x00lib_beacondone. This will send out + * multicast and broadcast traffic immediately instead of buffering it + * infinitly and thus dropping it after some time. + */ + if (!rt2x00_is_usb(rt2x00dev)) + rt2x00dev->hw->flags |= + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, + rt2800_eeprom_addr(rt2x00dev, + EEPROM_MAC_ADDR_0)); + + /* + * As rt2800 has a global fallback table we cannot specify + * more then one tx rate per frame but since the hw will + * try several rates (based on the fallback table) we should + * initialize max_report_rates to the maximum number of rates + * we are going to try. Otherwise mac80211 will truncate our + * reported tx rates and the rc algortihm will end up with + * incorrect data. + */ + rt2x00dev->hw->max_rates = 1; + rt2x00dev->hw->max_report_rates = 7; + rt2x00dev->hw->max_rate_tries = 1; + + /* + * Initialize hw_mode information. + */ + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; + + switch (rt2x00dev->chip.rf) { + case RF2720: + case RF2820: + spec->num_channels = 14; + spec->channels = rf_vals; + break; + + case RF2750: + case RF2850: + spec->num_channels = ARRAY_SIZE(rf_vals); + spec->channels = rf_vals; + break; + + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3070: + case RF3290: + case RF3320: + case RF3322: + case RF5360: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + spec->num_channels = 14; + spec->channels = rf_vals_3x; + break; + + case RF3052: + case RF3053: + spec->num_channels = ARRAY_SIZE(rf_vals_3x); + spec->channels = rf_vals_3x; + break; + + case RF5592: + rt2800_register_read(rt2x00dev, MAC_DEBUG_INDEX, ®); + if (rt2x00_get_field32(reg, MAC_DEBUG_INDEX_XTAL)) { + spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal40); + spec->channels = rf_vals_5592_xtal40; + } else { + spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal20); + spec->channels = rf_vals_5592_xtal20; + } + break; + } + + if (WARN_ON_ONCE(!spec->channels)) + return -ENODEV; + + spec->supported_bands = SUPPORT_BAND_2GHZ; + if (spec->num_channels > 14) + spec->supported_bands |= SUPPORT_BAND_5GHZ; + + /* + * Initialize HT information. + */ + if (!rt2x00_rf(rt2x00dev, RF2020)) + spec->ht.ht_supported = true; + else + spec->ht.ht_supported = false; + + spec->ht.cap = + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40; + + if (rt2x00dev->default_ant.tx_chain_num >= 2) + spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC; + + spec->ht.cap |= rt2x00dev->default_ant.rx_chain_num << + IEEE80211_HT_CAP_RX_STBC_SHIFT; + + spec->ht.ampdu_factor = 3; + spec->ht.ampdu_density = 4; + spec->ht.mcs.tx_params = + IEEE80211_HT_MCS_TX_DEFINED | + IEEE80211_HT_MCS_TX_RX_DIFF | + ((rt2x00dev->default_ant.tx_chain_num - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + + switch (rt2x00dev->default_ant.rx_chain_num) { + case 3: + spec->ht.mcs.rx_mask[2] = 0xff; + case 2: + spec->ht.mcs.rx_mask[1] = 0xff; + case 1: + spec->ht.mcs.rx_mask[0] = 0xff; + spec->ht.mcs.rx_mask[4] = 0x1; /* MCS32 */ + break; + } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + spec->channels_info = info; + + default_power1 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1); + default_power2 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2); + + if (rt2x00dev->default_ant.tx_chain_num > 2) + default_power3 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_EXT_TXPOWER_BG3); + else + default_power3 = NULL; + + for (i = 0; i < 14; i++) { + info[i].default_power1 = default_power1[i]; + info[i].default_power2 = default_power2[i]; + if (default_power3) + info[i].default_power3 = default_power3[i]; + } + + if (spec->num_channels > 14) { + default_power1 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_TXPOWER_A1); + default_power2 = rt2800_eeprom_addr(rt2x00dev, + EEPROM_TXPOWER_A2); + + if (rt2x00dev->default_ant.tx_chain_num > 2) + default_power3 = + rt2800_eeprom_addr(rt2x00dev, + EEPROM_EXT_TXPOWER_A3); + else + default_power3 = NULL; + + for (i = 14; i < spec->num_channels; i++) { + info[i].default_power1 = default_power1[i - 14]; + info[i].default_power2 = default_power2[i - 14]; + if (default_power3) + info[i].default_power3 = default_power3[i - 14]; + } + } + + switch (rt2x00dev->chip.rf) { + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3320: + case RF3052: + case RF3053: + case RF3070: + case RF3290: + case RF5360: + case RF5370: + case RF5372: + case RF5390: + case RF5392: + __set_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags); + break; + } + + return 0; +} + +static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u32 rt; + u32 rev; + + if (rt2x00_rt(rt2x00dev, RT3290)) + rt2800_register_read(rt2x00dev, MAC_CSR0_3290, ®); + else + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); + + rt = rt2x00_get_field32(reg, MAC_CSR0_CHIPSET); + rev = rt2x00_get_field32(reg, MAC_CSR0_REVISION); + + switch (rt) { + case RT2860: + case RT2872: + case RT2883: + case RT3070: + case RT3071: + case RT3090: + case RT3290: + case RT3352: + case RT3390: + case RT3572: + case RT3593: + case RT5390: + case RT5392: + case RT5592: + break; + default: + rt2x00_err(rt2x00dev, "Invalid RT chipset 0x%04x, rev %04x detected\n", + rt, rev); + return -ENODEV; + } + + rt2x00_set_rt(rt2x00dev, rt, rev); + + return 0; +} + +int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + u32 reg; + + retval = rt2800_probe_rt(rt2x00dev); + if (retval) + return retval; + + /* + * Allocate eeprom data. + */ + retval = rt2800_validate_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2800_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR2, 1); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + + /* + * Initialize hw specifications. + */ + retval = rt2800_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * Set device capabilities. + */ + __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags); + if (!rt2x00_is_usb(rt2x00dev)) + __set_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags); + + /* + * Set device requirements. + */ + if (!rt2x00_is_soc(rt2x00dev)) + __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags); + if (!rt2800_hwcrypt_disabled(rt2x00dev)) + __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags); + if (rt2x00_is_usb(rt2x00dev)) + __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); + else { + __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags); + } + + /* + * Set the rssi offset. + */ + rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_probe_hw); + +/* + * IEEE80211 stack callback functions. + */ +void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, + u16 *iv16) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct mac_iveiv_entry iveiv_entry; + u32 offset; + + offset = MAC_IVEIV_ENTRY(hw_key_idx); + rt2800_register_multiread(rt2x00dev, offset, + &iveiv_entry, sizeof(iveiv_entry)); + + memcpy(iv16, &iveiv_entry.iv[0], sizeof(*iv16)); + memcpy(iv32, &iveiv_entry.iv[4], sizeof(*iv32)); +} +EXPORT_SYMBOL_GPL(rt2800_get_tkip_seq); + +int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + bool enabled = (value < IEEE80211_MAX_RTS_THRESHOLD); + + rt2800_register_read(rt2x00dev, TX_RTS_CFG, ®); + rt2x00_set_field32(®, TX_RTS_CFG_RTS_THRES, value); + rt2800_register_write(rt2x00dev, TX_RTS_CFG, reg); + + rt2800_register_read(rt2x00dev, CCK_PROT_CFG, ®); + rt2x00_set_field32(®, CCK_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, CCK_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, OFDM_PROT_CFG, ®); + rt2x00_set_field32(®, OFDM_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, OFDM_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM20_PROT_CFG, ®); + rt2x00_set_field32(®, MM20_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, MM20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, MM40_PROT_CFG, ®); + rt2x00_set_field32(®, MM40_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, MM40_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF20_PROT_CFG, ®); + rt2x00_set_field32(®, GF20_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, GF20_PROT_CFG, reg); + + rt2800_register_read(rt2x00dev, GF40_PROT_CFG, ®); + rt2x00_set_field32(®, GF40_PROT_CFG_RTS_TH_EN, enabled); + rt2800_register_write(rt2x00dev, GF40_PROT_CFG, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_set_rts_threshold); + +int rt2800_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + struct rt2x00_field32 field; + int retval; + u32 reg; + u32 offset; + + /* + * First pass the configuration through rt2x00lib, that will + * update the queue settings and validate the input. After that + * we are free to update the registers based on the value + * in the queue parameter. + */ + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); + if (retval) + return retval; + + /* + * We only need to perform additional register initialization + * for WMM queues/ + */ + if (queue_idx >= 4) + return 0; + + queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + + /* Update WMM TXOP register */ + offset = WMM_TXOP0_CFG + (sizeof(u32) * (!!(queue_idx & 2))); + field.bit_offset = (queue_idx & 1) * 16; + field.bit_mask = 0xffff << field.bit_offset; + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, queue->txop); + rt2800_register_write(rt2x00dev, offset, reg); + + /* Update WMM registers */ + field.bit_offset = queue_idx * 4; + field.bit_mask = 0xf << field.bit_offset; + + rt2800_register_read(rt2x00dev, WMM_AIFSN_CFG, ®); + rt2x00_set_field32(®, field, queue->aifs); + rt2800_register_write(rt2x00dev, WMM_AIFSN_CFG, reg); + + rt2800_register_read(rt2x00dev, WMM_CWMIN_CFG, ®); + rt2x00_set_field32(®, field, queue->cw_min); + rt2800_register_write(rt2x00dev, WMM_CWMIN_CFG, reg); + + rt2800_register_read(rt2x00dev, WMM_CWMAX_CFG, ®); + rt2x00_set_field32(®, field, queue->cw_max); + rt2800_register_write(rt2x00dev, WMM_CWMAX_CFG, reg); + + /* Update EDCA registers */ + offset = EDCA_AC0_CFG + (sizeof(u32) * queue_idx); + + rt2800_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, EDCA_AC0_CFG_TX_OP, queue->txop); + rt2x00_set_field32(®, EDCA_AC0_CFG_AIFSN, queue->aifs); + rt2x00_set_field32(®, EDCA_AC0_CFG_CWMIN, queue->cw_min); + rt2x00_set_field32(®, EDCA_AC0_CFG_CWMAX, queue->cw_max); + rt2800_register_write(rt2x00dev, offset, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800_conf_tx); + +u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2800_register_read(rt2x00dev, TSF_TIMER_DW1, ®); + tsf = (u64) rt2x00_get_field32(reg, TSF_TIMER_DW1_HIGH_WORD) << 32; + rt2800_register_read(rt2x00dev, TSF_TIMER_DW0, ®); + tsf |= rt2x00_get_field32(reg, TSF_TIMER_DW0_LOW_WORD); + + return tsf; +} +EXPORT_SYMBOL_GPL(rt2800_get_tsf); + +int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv; + int ret = 0; + + /* + * Don't allow aggregation for stations the hardware isn't aware + * of because tx status reports for frames to an unknown station + * always contain wcid=255 and thus we can't distinguish between + * multiple stations which leads to unwanted situations when the + * hw reorders frames due to aggregation. + */ + if (sta_priv->wcid < 0) + return 1; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + /* + * The hw itself takes care of setting up BlockAck mechanisms. + * So, we only have to allow mac80211 to nagotiate a BlockAck + * agreement. Once that is done, the hw will BlockAck incoming + * AMPDUs without further setup. + */ + break; + case IEEE80211_AMPDU_TX_START: + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + break; + default: + rt2x00_warn((struct rt2x00_dev *)hw->priv, + "Unknown AMPDU action\n"); + } + + return ret; +} +EXPORT_SYMBOL_GPL(rt2800_ampdu_action); + +int rt2800_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + u32 idle, busy, busy_ext; + + if (idx != 0) + return -ENOENT; + + survey->channel = conf->chandef.chan; + + rt2800_register_read(rt2x00dev, CH_IDLE_STA, &idle); + rt2800_register_read(rt2x00dev, CH_BUSY_STA, &busy); + rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &busy_ext); + + if (idle || busy) { + survey->filled = SURVEY_INFO_CHANNEL_TIME | + SURVEY_INFO_CHANNEL_TIME_BUSY | + SURVEY_INFO_CHANNEL_TIME_EXT_BUSY; + + survey->channel_time = (idle + busy) / 1000; + survey->channel_time_busy = busy / 1000; + survey->channel_time_ext_busy = busy_ext / 1000; + } + + if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + survey->filled |= SURVEY_INFO_IN_USE; + + return 0; + +} +EXPORT_SYMBOL_GPL(rt2800_get_survey); + +MODULE_AUTHOR(DRV_PROJECT ", Bartlomiej Zolnierkiewicz"); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2800 library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h new file mode 100644 index 00000000000..3019db637a4 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -0,0 +1,231 @@ +/* + Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> + Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Bartlomiej Zolnierkiewicz + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef RT2800LIB_H +#define RT2800LIB_H + +struct rt2800_ops { + void (*register_read)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 *value); + void (*register_read_lock)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 *value); + void (*register_write)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 value); + void (*register_write_lock)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, u32 value); + + void (*register_multiread)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length); + void (*register_multiwrite)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, const u32 length); + + int (*regbusy_read)(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, u32 *reg); + + int (*read_eeprom)(struct rt2x00_dev *rt2x00dev); + bool (*hwcrypt_disabled)(struct rt2x00_dev *rt2x00dev); + + int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); + int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev); + __le32 *(*drv_get_txwi)(struct queue_entry *entry); +}; + +static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_read(rt2x00dev, offset, value); +} + +static inline void rt2800_register_read_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_read_lock(rt2x00dev, offset, value); +} + +static inline void rt2800_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_write(rt2x00dev, offset, value); +} + +static inline void rt2800_register_write_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_write_lock(rt2x00dev, offset, value); +} + +static inline void rt2800_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_multiread(rt2x00dev, offset, value, length); +} + +static inline void rt2800_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, + const u32 length) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + rt2800ops->register_multiwrite(rt2x00dev, offset, value, length); +} + +static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg); +} + +static inline int rt2800_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->read_eeprom(rt2x00dev); +} + +static inline bool rt2800_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->hwcrypt_disabled(rt2x00dev); +} + +static inline int rt2800_drv_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->drv_write_firmware(rt2x00dev, data, len); +} + +static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev) +{ + const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv; + + return rt2800ops->drv_init_registers(rt2x00dev); +} + +static inline __le32 *rt2800_drv_get_txwi(struct queue_entry *entry) +{ + const struct rt2800_ops *rt2800ops = entry->queue->rt2x00dev->ops->drv; + + return rt2800ops->drv_get_txwi(entry); +} + +void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, + const u8 command, const u8 token, + const u8 arg0, const u8 arg1); + +int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev); +int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev); + +int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); +int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); + +void rt2800_write_tx_data(struct queue_entry *entry, + struct txentry_desc *txdesc); +void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc); + +void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32* txwi); + +void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc); +void rt2800_clear_beacon(struct queue_entry *entry); + +extern const struct rt2x00debug rt2800_rt2x00debug; + +int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev); +int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); +int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); +int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid); +void rt2800_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags); +void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, const unsigned int flags); +void rt2800_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_erp *erp, + u32 changed); +void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant); +void rt2800_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags); +void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); +void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); +void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, + const u32 count); +void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev); +void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev); + +int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev); +void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev); + +int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev); +int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); + +int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev); + +void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, + u16 *iv16); +int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value); +int rt2800_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params); +u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); +int rt2800_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey); +void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev); + +void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, + unsigned short *txwi_size, + unsigned short *rxwi_size); + +#endif /* RT2800LIB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/rt2x00/rt2800mmio.c new file mode 100644 index 00000000000..de4790b41be --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800mmio.c @@ -0,0 +1,871 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + * Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + * Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + * Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + * Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + * Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + * <http://rt2x00.serialmonkey.com> + * + * 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Module: rt2800mmio + * Abstract: rt2800 MMIO device routines. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/export.h> + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2800.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" + +/* + * TX descriptor initialization + */ +__le32 *rt2800mmio_get_txwi(struct queue_entry *entry) +{ + return (__le32 *) entry->skb->data; +} +EXPORT_SYMBOL_GPL(rt2800mmio_get_txwi); + +void rt2800mmio_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; + u32 word; + const unsigned int txwi_size = entry->queue->winfo_size; + + /* + * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 + * must contains a TXWI structure + 802.11 header + padding + 802.11 + * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and + * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 + * data. It means that LAST_SEC0 is always 0. + */ + + /* + * Initialize TX descriptor + */ + word = 0; + rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); + rt2x00_desc_write(txd, 0, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, + !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); + rt2x00_desc_write(txd, 1, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W2_SD_PTR1, + skbdesc->skb_dma + txwi_size); + rt2x00_desc_write(txd, 2, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W3_WIV, + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W3_QSEL, 2); + rt2x00_desc_write(txd, 3, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; +} +EXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc); + +/* + * RX control handlers + */ +void rt2800mmio_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *rxd = entry_priv->desc; + u32 word; + + rt2x00_desc_read(rxd, 3, &word); + + if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + /* + * Unfortunately we don't know the cipher type used during + * decryption. This prevents us from correct providing + * correct statistics through debugfs. + */ + rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); + + if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. Unfortunately the descriptor doesn't contain + * any fields with the EIV/IV data either, so they can't + * be restored by rt2x00lib. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + if (rt2x00_get_field32(word, RXD_W3_L2PAD)) + rxdesc->dev_flags |= RXDONE_L2PAD; + + /* + * Process the RXWI structure that is at the start of the buffer. + */ + rt2800_process_rxwi(entry, rxdesc); +} +EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); + +/* + * Interrupt functions. + */ +static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_conf conf = { .flags = 0 }; + struct rt2x00lib_conf libconf = { .conf = &conf }; + + rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); +} + +static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) +{ + __le32 *txwi; + u32 word; + int wcid, tx_wcid; + + wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); + + txwi = rt2800_drv_get_txwi(entry); + rt2x00_desc_read(txwi, 1, &word); + tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); + + return (tx_wcid == wcid); +} + +static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) +{ + u32 status = *(u32 *)data; + + /* + * rt2800pci hardware might reorder frames when exchanging traffic + * with multiple BA enabled STAs. + * + * For example, a tx queue + * [ STA1 | STA2 | STA1 | STA2 ] + * can result in tx status reports + * [ STA1 | STA1 | STA2 | STA2 ] + * when the hw decides to aggregate the frames for STA1 into one AMPDU. + * + * To mitigate this effect, associate the tx status to the first frame + * in the tx queue with a matching wcid. + */ + if (rt2800mmio_txdone_entry_check(entry, status) && + !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + /* + * Got a matching frame, associate the tx status with + * the frame + */ + entry->status = status; + set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); + return true; + } + + /* Check the next frame */ + return false; +} + +static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) +{ + u32 status = *(u32 *)data; + + /* + * Find the first frame without tx status and assign this status to it + * regardless if it matches or not. + */ + if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + /* + * Got a matching frame, associate the tx status with + * the frame + */ + entry->status = status; + set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); + return true; + } + + /* Check the next frame */ + return false; +} +static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, + void *data) +{ + if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + rt2800_txdone_entry(entry, entry->status, + rt2800mmio_get_txwi(entry)); + return false; + } + + /* No more frames to release */ + return true; +} + +static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + u32 status; + u8 qid; + int max_tx_done = 16; + + while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { + qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); + if (unlikely(qid >= QID_RX)) { + /* + * Unknown queue, this shouldn't happen. Just drop + * this tx status. + */ + rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", + qid); + break; + } + + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + if (unlikely(queue == NULL)) { + /* + * The queue is NULL, this shouldn't happen. Stop + * processing here and drop the tx status + */ + rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", + qid); + break; + } + + if (unlikely(rt2x00queue_empty(queue))) { + /* + * The queue is empty. Stop processing here + * and drop the tx status. + */ + rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", + qid); + break; + } + + /* + * Let's associate this tx status with the first + * matching frame. + */ + if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, &status, + rt2800mmio_txdone_find_entry)) { + /* + * We cannot match the tx status to any frame, so just + * use the first one. + */ + if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, &status, + rt2800mmio_txdone_match_first)) { + rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", + qid); + break; + } + } + + /* + * Release all frames with a valid tx status. + */ + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, NULL, + rt2800mmio_txdone_release_entries); + + if (--max_tx_done == 0) + break; + } + + return !max_tx_done; +} + +static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + rt2x00_set_field32(®, irq_field, 1); + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +void rt2800mmio_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2800mmio_txdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + + /* + * No need to enable the tx status interrupt here as we always + * leave it enabled to minimize the possibility of a tx status + * register overflow. See comment in interrupt handler. + */ +} +EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); + +void rt2800mmio_pretbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_pretbtt(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); +} +EXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet); + +void rt2800mmio_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u32 reg; + + rt2x00lib_beacondone(rt2x00dev); + + if (rt2x00dev->intf_ap_count) { + /* + * The rt2800pci hardware tbtt timer is off by 1us per tbtt + * causing beacon skew and as a result causing problems with + * some powersaving clients over time. Shorten the beacon + * interval every 64 beacons by 64us to mitigate this effect. + */ + if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + (rt2x00dev->beacon_int * 16) - 1); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + (rt2x00dev->beacon_int * 16)); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } + drv_data->tbtt_tick++; + drv_data->tbtt_tick %= BCN_TBTT_OFFSET; + } + + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); +} +EXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet); + +void rt2800mmio_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); +} +EXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet); + +void rt2800mmio_autowake_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2800mmio_wakeup(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, + INT_MASK_CSR_AUTO_WAKEUP); +} +EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); + +static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) +{ + u32 status; + int i; + + /* + * The TX_FIFO_STATUS interrupt needs special care. We should + * read TX_STA_FIFO but we should do it immediately as otherwise + * the register can overflow and we would lose status reports. + * + * Hence, read the TX_STA_FIFO register and copy all tx status + * reports into a kernel FIFO which is handled in the txstatus + * tasklet. We use a tasklet to process the tx status reports + * because we can schedule the tasklet multiple times (when the + * interrupt fires again during tx status processing). + * + * Furthermore we don't disable the TX_FIFO_STATUS + * interrupt here but leave it enabled so that the TX_STA_FIFO + * can also be read while the tx status tasklet gets executed. + * + * Since we have only one producer and one consumer we don't + * need to lock the kfifo. + */ + for (i = 0; i < rt2x00dev->tx->limit; i++) { + rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); + + if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) + break; + + if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) { + rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); + break; + } + } + + /* Schedule the tasklet for processing the tx status. */ + tasklet_schedule(&rt2x00dev->txstatus_tasklet); +} + +irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg, mask; + + /* Read status and ACK all interrupts */ + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + if (!reg) + return IRQ_NONE; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return IRQ_HANDLED; + + /* + * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits + * for interrupts and interrupt masks we can just use the value of + * INT_SOURCE_CSR to create the interrupt mask. + */ + mask = ~reg; + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { + rt2800mmio_txstatus_interrupt(rt2x00dev); + /* + * Never disable the TX_FIFO_STATUS interrupt. + */ + rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); + } + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) + tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) + tasklet_schedule(&rt2x00dev->autowake_tasklet); + + /* + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. + */ + spin_lock(&rt2x00dev->irqmask_lock); + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + reg &= mask; + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock(&rt2x00dev->irqmask_lock); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(rt2800mmio_interrupt); + +void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + unsigned long flags; + + /* + * When interrupts are being enabled, the interrupt registers + * should clear the register to assure a clean state. + */ + if (state == STATE_RADIO_IRQ_ON) { + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + } + + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + reg = 0; + if (state == STATE_RADIO_IRQ_ON) { + rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); + rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); + } + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Wait for possibly running tasklets to finish. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->autowake_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq); + +/* + * Queue handlers. + */ +void rt2800mmio_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); + rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); + rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_start_queue); + +void rt2800mmio_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct queue_entry *entry; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + entry = rt2x00queue_get_entry(queue, Q_INDEX); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), + entry->entry_idx); + break; + case QID_MGMT: + entry = rt2x00queue_get_entry(queue, Q_INDEX); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), + entry->entry_idx); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue); + +void rt2800mmio_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); + rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); + rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); + + /* + * Wait for current invocation to finish. The tasklet + * won't be scheduled anymore afterwards since we disabled + * the TBTT and PRE TBTT timer. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_stop_queue); + +void rt2800mmio_queue_init(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; + + rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); + + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->winfo_size = rxwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 64; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 8; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_queue_init); + +/* + * Initialization functions. + */ +bool rt2800mmio_get_entry_state(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state); + +void rt2800mmio_clear_entry(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 0, word); + + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); + rt2x00_desc_write(entry_priv->desc, 1, word); + + /* + * Set RX IDX in register to inform hardware that we have + * handled this entry and it is available for reuse again. + */ + rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, + entry->entry_idx); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); + rt2x00_desc_write(entry_priv->desc, 1, word); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_clear_entry); + +int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct queue_entry_priv_mmio *entry_priv; + + /* + * Initialize registers. + */ + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, + rt2x00dev->tx[0].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, + rt2x00dev->tx[1].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); + + entry_priv = rt2x00dev->tx[2].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, + rt2x00dev->tx[2].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); + + entry_priv = rt2x00dev->tx[3].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, + rt2x00dev->tx[3].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); + + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); + + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); + + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, + rt2x00dev->rx[0].limit); + rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, + rt2x00dev->rx[0].limit - 1); + rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); + + rt2800_disable_wpdma(rt2x00dev); + + rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800mmio_init_queues); + +int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Reset DMA indexes + */ + rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, ®); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); + rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); + + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + + if (rt2x00_is_pcie(rt2x00dev) && + (rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3390) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592))) { + rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, ®); + rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); + rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); + rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); + } + + rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); + + reg = 0; + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800mmio_init_registers); + +/* + * Device state switch handlers. + */ +int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* Wait for DMA, ignore error until we initialize queues. */ + rt2800_wait_wpdma_ready(rt2x00dev); + + if (unlikely(rt2800mmio_init_queues(rt2x00dev))) + return -EIO; + + return rt2800_enable_radio(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2800mmio_enable_radio); + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2800 MMIO library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/rt2x00/rt2800mmio.h new file mode 100644 index 00000000000..b63312ce3f2 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800mmio.h @@ -0,0 +1,163 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + * Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + * Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + * Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + * Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + * Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + * <http://rt2x00.serialmonkey.com> + * + * 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Module: rt2800mmio + * Abstract: forward declarations for the rt2800mmio module. + */ + +#ifndef RT2800MMIO_H +#define RT2800MMIO_H + +/* + * Queue register offset macros + */ +#define TX_QUEUE_REG_OFFSET 0x10 +#define TX_BASE_PTR(__x) (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_MAX_CNT(__x) (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_CTX_IDX(__x) (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_DTX_IDX(__x) (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE (4 * sizeof(__le32)) +#define RXD_DESC_SIZE (4 * sizeof(__le32)) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) +#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) +#define TXD_W1_BURST FIELD32(0x00008000) +#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) +#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) +#define TXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) + +/* + * Word3 + * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI + * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. + * 0:MGMT, 1:HCCA 2:EDCA + */ +#define TXD_W3_WIV FIELD32(0x01000000) +#define TXD_W3_QSEL FIELD32(0x06000000) +#define TXD_W3_TCO FIELD32(0x20000000) +#define TXD_W3_UCO FIELD32(0x40000000) +#define TXD_W3_ICO FIELD32(0x80000000) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_SDP0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define RXD_W1_SDL1 FIELD32(0x00003fff) +#define RXD_W1_SDL0 FIELD32(0x3fff0000) +#define RXD_W1_LS0 FIELD32(0x40000000) +#define RXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define RXD_W2_SDP1 FIELD32(0xffffffff) + +/* + * Word3 + * AMSDU: RX with 802.3 header, not 802.11 header. + * DECRYPTED: This frame is being decrypted. + */ +#define RXD_W3_BA FIELD32(0x00000001) +#define RXD_W3_DATA FIELD32(0x00000002) +#define RXD_W3_NULLDATA FIELD32(0x00000004) +#define RXD_W3_FRAG FIELD32(0x00000008) +#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) +#define RXD_W3_MULTICAST FIELD32(0x00000020) +#define RXD_W3_BROADCAST FIELD32(0x00000040) +#define RXD_W3_MY_BSS FIELD32(0x00000080) +#define RXD_W3_CRC_ERROR FIELD32(0x00000100) +#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) +#define RXD_W3_AMSDU FIELD32(0x00000800) +#define RXD_W3_HTC FIELD32(0x00001000) +#define RXD_W3_RSSI FIELD32(0x00002000) +#define RXD_W3_L2PAD FIELD32(0x00004000) +#define RXD_W3_AMPDU FIELD32(0x00008000) +#define RXD_W3_DECRYPTED FIELD32(0x00010000) +#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) +#define RXD_W3_PLCP_RSSI FIELD32(0x00040000) + +/* TX descriptor initialization */ +__le32 *rt2800mmio_get_txwi(struct queue_entry *entry); +void rt2800mmio_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc); + +/* RX control handlers */ +void rt2800mmio_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc); + +/* Interrupt functions */ +void rt2800mmio_txstatus_tasklet(unsigned long data); +void rt2800mmio_pretbtt_tasklet(unsigned long data); +void rt2800mmio_tbtt_tasklet(unsigned long data); +void rt2800mmio_rxdone_tasklet(unsigned long data); +void rt2800mmio_autowake_tasklet(unsigned long data); +irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance); +void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state); + +/* Queue handlers */ +void rt2800mmio_start_queue(struct data_queue *queue); +void rt2800mmio_kick_queue(struct data_queue *queue); +void rt2800mmio_stop_queue(struct data_queue *queue); +void rt2800mmio_queue_init(struct data_queue *queue); + +/* Initialization functions */ +bool rt2800mmio_get_entry_state(struct queue_entry *entry); +void rt2800mmio_clear_entry(struct queue_entry *entry); +int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev); +int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev); + +/* Device state switch handlers. */ +int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev); + +#endif /* RT2800MMIO_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c new file mode 100644 index 00000000000..a5b32ca2cf0 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -0,0 +1,471 @@ +/* + Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2800pci + Abstract: rt2800pci device specific routines. + Supported chipsets: RT2800E & RT2800ED. + */ + +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/eeprom_93cx6.h> + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00pci.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" +#include "rt2800.h" +#include "rt2800pci.h" + +/* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt = false; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static bool rt2800pci_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + return modparam_nohwcrypt; +} + +static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) +{ + unsigned int i; + u32 reg; + + /* + * SOC devices don't support MCU requests. + */ + if (rt2x00_is_soc(rt2x00dev)) + return; + + for (i = 0; i < 200; i++) { + rt2x00mmio_register_read(rt2x00dev, H2M_MAILBOX_CID, ®); + + if ((rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD0) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD1) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD2) == token) || + (rt2x00_get_field32(reg, H2M_MAILBOX_CID_CMD3) == token)) + break; + + udelay(REGISTER_BUSY_DELAY); + } + + if (i == 200) + rt2x00_err(rt2x00dev, "MCU request failed, no response from hardware\n"); + + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); +} + +static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); + eeprom->reg_data_clock = + !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_CLOCK); + eeprom->reg_chip_select = + !!rt2x00_get_field32(reg, E2PROM_CSR_CHIP_SELECT); +} + +static void rt2800pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, !!eeprom->reg_data_in); + rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, !!eeprom->reg_data_out); + rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2x00mmio_register_write(rt2x00dev, E2PROM_CSR, reg); +} + +static int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + + rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt2800pci_eepromregister_read; + eeprom.register_write = rt2800pci_eepromregister_write; + switch (rt2x00_get_field32(reg, E2PROM_CSR_TYPE)) + { + case 0: + eeprom.width = PCI_EEPROM_WIDTH_93C46; + break; + case 1: + eeprom.width = PCI_EEPROM_WIDTH_93C66; + break; + default: + eeprom.width = PCI_EEPROM_WIDTH_93C86; + break; + } + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); + + return 0; +} + +static int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + return rt2800_efuse_detect(rt2x00dev); +} + +static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) +{ + return rt2800_read_eeprom_efuse(rt2x00dev); +} + +/* + * Firmware functions + */ +static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + /* + * Chip rt3290 use specific 4KB firmware named rt3290.bin. + */ + if (rt2x00_rt(rt2x00dev, RT3290)) + return FIRMWARE_RT3290; + else + return FIRMWARE_RT2860; +} + +static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + u32 reg; + + /* + * enable Host program ram write selection + */ + reg = 0; + rt2x00_set_field32(®, PBF_SYS_CTRL_HOST_RAM_WRITE, 1); + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, reg); + + /* + * Write firmware to device. + */ + rt2x00mmio_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data, len); + + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000); + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001); + + rt2x00mmio_register_write(rt2x00dev, H2M_BBP_AGENT, 0); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + + return 0; +} + +/* + * Device state switch handlers. + */ +static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800mmio_enable_radio(rt2x00dev); + if (retval) + return retval; + + /* After resume MCU_BOOT_SIGNAL will trash these. */ + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); + + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, TOKEN_RADIO_OFF, 0xff, 0x02); + rt2800pci_mcu_status(rt2x00dev, TOKEN_RADIO_OFF); + + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKEUP, 0, 0); + rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKEUP); + + return retval; +} + +static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + if (state == STATE_AWAKE) { + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKEUP, + 0, 0x02); + rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKEUP); + } else if (state == STATE_SLEEP) { + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_STATUS, + 0xffffffff); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, + 0xffffffff); + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, TOKEN_SLEEP, + 0xff, 0x01); + } + + return 0; +} + +static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2800pci_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + /* + * After the radio has been disabled, the device should + * be put to sleep for powersaving. + */ + rt2800pci_set_state(rt2x00dev, STATE_SLEEP); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2800mmio_toggle_irq(rt2x00dev, state); + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt2800pci_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * Device probe functions. + */ +static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + if (rt2800pci_efuse_detect(rt2x00dev)) + retval = rt2800pci_read_eeprom_efuse(rt2x00dev); + else + retval = rt2800pci_read_eeprom_pci(rt2x00dev); + + return retval; +} + +static const struct ieee80211_ops rt2800pci_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .get_tkip_seq = rt2800_get_tkip_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .sta_add = rt2x00mac_sta_add, + .sta_remove = rt2x00mac_sta_remove, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .ampdu_action = rt2800_ampdu_action, + .flush = rt2x00mac_flush, + .get_survey = rt2800_get_survey, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2800_ops rt2800pci_rt2800_ops = { + .register_read = rt2x00mmio_register_read, + .register_read_lock = rt2x00mmio_register_read, /* same for PCI */ + .register_write = rt2x00mmio_register_write, + .register_write_lock = rt2x00mmio_register_write, /* same for PCI */ + .register_multiread = rt2x00mmio_register_multiread, + .register_multiwrite = rt2x00mmio_register_multiwrite, + .regbusy_read = rt2x00mmio_regbusy_read, + .read_eeprom = rt2800pci_read_eeprom, + .hwcrypt_disabled = rt2800pci_hwcrypt_disabled, + .drv_write_firmware = rt2800pci_write_firmware, + .drv_init_registers = rt2800mmio_init_registers, + .drv_get_txwi = rt2800mmio_get_txwi, +}; + +static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { + .irq_handler = rt2800mmio_interrupt, + .txstatus_tasklet = rt2800mmio_txstatus_tasklet, + .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, + .tbtt_tasklet = rt2800mmio_tbtt_tasklet, + .rxdone_tasklet = rt2800mmio_rxdone_tasklet, + .autowake_tasklet = rt2800mmio_autowake_tasklet, + .probe_hw = rt2800_probe_hw, + .get_firmware_name = rt2800pci_get_firmware_name, + .check_firmware = rt2800_check_firmware, + .load_firmware = rt2800_load_firmware, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2800mmio_get_entry_state, + .clear_entry = rt2800mmio_clear_entry, + .set_device_state = rt2800pci_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .gain_calibration = rt2800_gain_calibration, + .vco_calibration = rt2800_vco_calibration, + .start_queue = rt2800mmio_start_queue, + .kick_queue = rt2800mmio_kick_queue, + .stop_queue = rt2800mmio_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt2800mmio_write_tx_desc, + .write_tx_data = rt2800_write_tx_data, + .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, + .fill_rxdone = rt2800mmio_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, + .sta_add = rt2800_sta_add, + .sta_remove = rt2800_sta_remove, +}; + +static const struct rt2x00_ops rt2800pci_ops = { + .name = KBUILD_MODNAME, + .drv_data_size = sizeof(struct rt2800_drv_data), + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2800mmio_queue_init, + .lib = &rt2800pci_rt2x00_ops, + .drv = &rt2800pci_rt2800_ops, + .hw = &rt2800pci_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * RT2800pci module information. + */ +static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { + { PCI_DEVICE(0x1814, 0x0601) }, + { PCI_DEVICE(0x1814, 0x0681) }, + { PCI_DEVICE(0x1814, 0x0701) }, + { PCI_DEVICE(0x1814, 0x0781) }, + { PCI_DEVICE(0x1814, 0x3090) }, + { PCI_DEVICE(0x1814, 0x3091) }, + { PCI_DEVICE(0x1814, 0x3092) }, + { PCI_DEVICE(0x1432, 0x7708) }, + { PCI_DEVICE(0x1432, 0x7727) }, + { PCI_DEVICE(0x1432, 0x7728) }, + { PCI_DEVICE(0x1432, 0x7738) }, + { PCI_DEVICE(0x1432, 0x7748) }, + { PCI_DEVICE(0x1432, 0x7758) }, + { PCI_DEVICE(0x1432, 0x7768) }, + { PCI_DEVICE(0x1462, 0x891a) }, + { PCI_DEVICE(0x1a3b, 0x1059) }, +#ifdef CONFIG_RT2800PCI_RT3290 + { PCI_DEVICE(0x1814, 0x3290) }, +#endif +#ifdef CONFIG_RT2800PCI_RT33XX + { PCI_DEVICE(0x1814, 0x3390) }, +#endif +#ifdef CONFIG_RT2800PCI_RT35XX + { PCI_DEVICE(0x1432, 0x7711) }, + { PCI_DEVICE(0x1432, 0x7722) }, + { PCI_DEVICE(0x1814, 0x3060) }, + { PCI_DEVICE(0x1814, 0x3062) }, + { PCI_DEVICE(0x1814, 0x3562) }, + { PCI_DEVICE(0x1814, 0x3592) }, + { PCI_DEVICE(0x1814, 0x3593) }, + { PCI_DEVICE(0x1814, 0x359f) }, +#endif +#ifdef CONFIG_RT2800PCI_RT53XX + { PCI_DEVICE(0x1814, 0x5360) }, + { PCI_DEVICE(0x1814, 0x5362) }, + { PCI_DEVICE(0x1814, 0x5390) }, + { PCI_DEVICE(0x1814, 0x5392) }, + { PCI_DEVICE(0x1814, 0x539a) }, + { PCI_DEVICE(0x1814, 0x539b) }, + { PCI_DEVICE(0x1814, 0x539f) }, +#endif + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards"); +MODULE_FIRMWARE(FIRMWARE_RT2860); +MODULE_DEVICE_TABLE(pci, rt2800pci_device_table); +MODULE_LICENSE("GPL"); + +static int rt2800pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + return rt2x00pci_probe(pci_dev, &rt2800pci_ops); +} + +static struct pci_driver rt2800pci_driver = { + .name = KBUILD_MODNAME, + .id_table = rt2800pci_device_table, + .probe = rt2800pci_probe, + .remove = rt2x00pci_remove, + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +}; + +module_pci_driver(rt2800pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/rt2x00/rt2800pci.h new file mode 100644 index 00000000000..9dfef4607d6 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800pci.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2800pci + Abstract: Data structures and registers for the rt2800pci module. + Supported chipsets: RT2800E & RT2800ED. + */ + +#ifndef RT2800PCI_H +#define RT2800PCI_H + +/* + * 8051 firmware image. + */ +#define FIRMWARE_RT2860 "rt2860.bin" +#define FIRMWARE_RT3290 "rt3290.bin" +#define FIRMWARE_IMAGE_BASE 0x2000 + +#endif /* RT2800PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/rt2x00/rt2800soc.c new file mode 100644 index 00000000000..f6d1bf5be00 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800soc.c @@ -0,0 +1,261 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + * Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + * Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + * Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + * Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + * Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + * <http://rt2x00.serialmonkey.com> + * + * 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Module: rt2800soc + * Abstract: rt2800 WiSoC specific routines. + */ + +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00soc.h" +#include "rt2800.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" + +/* Allow hardware encryption to be disabled. */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static bool rt2800soc_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + return modparam_nohwcrypt; +} + +static void rt2800soc_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2800_disable_radio(rt2x00dev); + rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0); +} + +static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2800mmio_enable_radio(rt2x00dev); + break; + + case STATE_RADIO_OFF: + rt2800soc_disable_radio(rt2x00dev); + break; + + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2800mmio_toggle_irq(rt2x00dev, state); + break; + + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + /* These states are not supported, but don't report an error */ + retval = 0; + break; + + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); + + if (!base_addr) + return -ENOMEM; + + memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); + + iounmap(base_addr); + return 0; +} + +/* Firmware functions */ +static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + WARN_ON_ONCE(1); + return NULL; +} + +static int rt2800soc_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static int rt2800soc_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static const struct ieee80211_ops rt2800soc_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .get_tkip_seq = rt2800_get_tkip_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .sta_add = rt2x00mac_sta_add, + .sta_remove = rt2x00mac_sta_remove, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .ampdu_action = rt2800_ampdu_action, + .flush = rt2x00mac_flush, + .get_survey = rt2800_get_survey, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2800_ops rt2800soc_rt2800_ops = { + .register_read = rt2x00mmio_register_read, + .register_read_lock = rt2x00mmio_register_read, /* same for SoCs */ + .register_write = rt2x00mmio_register_write, + .register_write_lock = rt2x00mmio_register_write, /* same for SoCs */ + .register_multiread = rt2x00mmio_register_multiread, + .register_multiwrite = rt2x00mmio_register_multiwrite, + .regbusy_read = rt2x00mmio_regbusy_read, + .read_eeprom = rt2800soc_read_eeprom, + .hwcrypt_disabled = rt2800soc_hwcrypt_disabled, + .drv_write_firmware = rt2800soc_write_firmware, + .drv_init_registers = rt2800mmio_init_registers, + .drv_get_txwi = rt2800mmio_get_txwi, +}; + +static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = { + .irq_handler = rt2800mmio_interrupt, + .txstatus_tasklet = rt2800mmio_txstatus_tasklet, + .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, + .tbtt_tasklet = rt2800mmio_tbtt_tasklet, + .rxdone_tasklet = rt2800mmio_rxdone_tasklet, + .autowake_tasklet = rt2800mmio_autowake_tasklet, + .probe_hw = rt2800_probe_hw, + .get_firmware_name = rt2800soc_get_firmware_name, + .check_firmware = rt2800soc_check_firmware, + .load_firmware = rt2800soc_load_firmware, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2800mmio_get_entry_state, + .clear_entry = rt2800mmio_clear_entry, + .set_device_state = rt2800soc_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .gain_calibration = rt2800_gain_calibration, + .vco_calibration = rt2800_vco_calibration, + .start_queue = rt2800mmio_start_queue, + .kick_queue = rt2800mmio_kick_queue, + .stop_queue = rt2800mmio_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt2800mmio_write_tx_desc, + .write_tx_data = rt2800_write_tx_data, + .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, + .fill_rxdone = rt2800mmio_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, + .sta_add = rt2800_sta_add, + .sta_remove = rt2800_sta_remove, +}; + +static const struct rt2x00_ops rt2800soc_ops = { + .name = KBUILD_MODNAME, + .drv_data_size = sizeof(struct rt2800_drv_data), + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2800mmio_queue_init, + .lib = &rt2800soc_rt2x00_ops, + .drv = &rt2800soc_rt2800_ops, + .hw = &rt2800soc_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +static int rt2800soc_probe(struct platform_device *pdev) +{ + return rt2x00soc_probe(pdev, &rt2800soc_ops); +} + +static struct platform_driver rt2800soc_driver = { + .driver = { + .name = "rt2800_wmac", + .owner = THIS_MODULE, + .mod_name = KBUILD_MODNAME, + }, + .probe = rt2800soc_probe, + .remove = rt2x00soc_remove, + .suspend = rt2x00soc_suspend, + .resume = rt2x00soc_resume, +}; + +module_platform_driver(rt2800soc_driver); + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink WiSoC Wireless LAN driver."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c new file mode 100644 index 00000000000..832006b5aab --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -0,0 +1,1451 @@ +/* + Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> + Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Axel Kollhofer <rain_maker@root-forum.org> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2800usb + Abstract: rt2800usb device specific routines. + Supported chipsets: RT2800U. + */ + +#include <linux/delay.h> +#include <linux/etherdevice.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "rt2x00.h" +#include "rt2x00usb.h" +#include "rt2800lib.h" +#include "rt2800.h" +#include "rt2800usb.h" + +/* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static bool rt2800usb_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + return modparam_nohwcrypt; +} + +/* + * Queue handlers. + */ +static void rt2800usb_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); + rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); + break; + default: + break; + } +} + +static void rt2800usb_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00usb_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); + break; + default: + break; + } +} + +/* + * test if there is an entry in any TX queue for which DMA is done + * but the TX status has not been returned yet + */ +static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) { + if (rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE) != + rt2x00queue_get_entry(queue, Q_INDEX_DONE)) + return true; + } + return false; +} + +static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry) +{ + bool tout; + + if (!test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + return false; + + tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); + if (unlikely(tout)) + rt2x00_dbg(entry->queue->rt2x00dev, + "TX status timeout for entry %d in queue %d\n", + entry->entry_idx, entry->queue->qid); + return tout; + +} + +static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + + tx_queue_for_each(rt2x00dev, queue) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + if (rt2800usb_entry_txstatus_timeout(entry)) + return true; + } + return false; +} + +#define TXSTATUS_READ_INTERVAL 1000000 + +static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, + int urb_status, u32 tx_status) +{ + bool valid; + + if (urb_status) { + rt2x00_warn(rt2x00dev, "TX status read failed %d\n", + urb_status); + + goto stop_reading; + } + + valid = rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID); + if (valid) { + if (!kfifo_put(&rt2x00dev->txstatus_fifo, tx_status)) + rt2x00_warn(rt2x00dev, "TX status FIFO overrun\n"); + + queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); + + /* Reschedule urb to read TX status again instantly */ + return true; + } + + /* Check if there is any entry that timedout waiting on TX status */ + if (rt2800usb_txstatus_timeout(rt2x00dev)) + queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); + + if (rt2800usb_txstatus_pending(rt2x00dev)) { + /* Read register after 1 ms */ + hrtimer_start(&rt2x00dev->txstatus_timer, + ktime_set(0, TXSTATUS_READ_INTERVAL), + HRTIMER_MODE_REL); + return false; + } + +stop_reading: + clear_bit(TX_STATUS_READING, &rt2x00dev->flags); + /* + * There is small race window above, between txstatus pending check and + * clear_bit someone could do rt2x00usb_interrupt_txdone, so recheck + * here again if status reading is needed. + */ + if (rt2800usb_txstatus_pending(rt2x00dev) && + !test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) + return true; + else + return false; +} + +static void rt2800usb_async_read_tx_status(struct rt2x00_dev *rt2x00dev) +{ + + if (test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) + return; + + /* Read TX_STA_FIFO register after 2 ms */ + hrtimer_start(&rt2x00dev->txstatus_timer, + ktime_set(0, 2*TXSTATUS_READ_INTERVAL), + HRTIMER_MODE_REL); +} + +static void rt2800usb_tx_dma_done(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + + rt2800usb_async_read_tx_status(rt2x00dev); +} + +static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer) +{ + struct rt2x00_dev *rt2x00dev = + container_of(timer, struct rt2x00_dev, txstatus_timer); + + rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO, + rt2800usb_tx_sta_fifo_read_completed); + + return HRTIMER_NORESTART; +} + +/* + * Firmware functions + */ +static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) +{ + __le32 *reg; + u32 fw_mode; + + reg = kmalloc(sizeof(*reg), GFP_KERNEL); + if (reg == NULL) + return -ENOMEM; + /* cannot use rt2x00usb_register_read here as it uses different + * mode (MULTI_READ vs. DEVICE_MODE) and does not pass the + * magic value USB_MODE_AUTORUN (0x11) to the device, thus the + * returned value would be invalid. + */ + rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE, + USB_VENDOR_REQUEST_IN, 0, USB_MODE_AUTORUN, + reg, sizeof(*reg), REGISTER_TIMEOUT_FIRMWARE); + fw_mode = le32_to_cpu(*reg); + kfree(reg); + + if ((fw_mode & 0x00000003) == 2) + return 1; + + return 0; +} + +static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + return FIRMWARE_RT2870; +} + +static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + int status; + u32 offset; + u32 length; + int retval; + + /* + * Check which section of the firmware we need. + */ + if (rt2x00_rt(rt2x00dev, RT2860) || + rt2x00_rt(rt2x00dev, RT2872) || + rt2x00_rt(rt2x00dev, RT3070)) { + offset = 0; + length = 4096; + } else { + offset = 4096; + length = 4096; + } + + /* + * Write firmware to device. + */ + retval = rt2800usb_autorun_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) { + rt2x00_info(rt2x00dev, + "Firmware loading not required - NIC in AutoRun mode\n"); + } else { + rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data + offset, length); + } + + rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); + rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); + + /* + * Send firmware request to device to load firmware, + * we need to specify a long timeout time. + */ + status = rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, + 0, USB_MODE_FIRMWARE, + REGISTER_TIMEOUT_FIRMWARE); + if (status < 0) { + rt2x00_err(rt2x00dev, "Failed to write Firmware to device\n"); + return status; + } + + msleep(10); + rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + + return 0; +} + +/* + * Device state switch handlers. + */ +static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Wait until BBP and RF are ready. + */ + if (rt2800_wait_csr_ready(rt2x00dev)) + return -EBUSY; + + rt2x00usb_register_read(rt2x00dev, PBF_SYS_CTRL, ®); + rt2x00usb_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000); + + reg = 0; + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); + rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, 0x00000000); + + rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, + USB_MODE_RESET, REGISTER_TIMEOUT); + + rt2x00usb_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + return 0; +} + +static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev))) + return -EIO; + + rt2x00usb_register_read(rt2x00dev, USB_DMA_CFG, ®); + rt2x00_set_field32(®, USB_DMA_CFG_PHY_CLEAR, 0); + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_EN, 0); + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_TIMEOUT, 128); + /* + * Total room for RX frames in kilobytes, PBF might still exceed + * this limit so reduce the number to prevent errors. + */ + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_LIMIT, + ((rt2x00dev->rx->limit * DATA_FRAME_SIZE) + / 1024) - 3); + rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_EN, 1); + rt2x00_set_field32(®, USB_DMA_CFG_TX_BULK_EN, 1); + rt2x00usb_register_write(rt2x00dev, USB_DMA_CFG, reg); + + return rt2800_enable_radio(rt2x00dev); +} + +static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2800_disable_radio(rt2x00dev); + rt2x00usb_disable_radio(rt2x00dev); +} + +static int rt2800usb_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + if (state == STATE_AWAKE) + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 2); + else + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0xff, 2); + + return 0; +} + +static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + /* + * Before the radio can be enabled, the device first has + * to be woken up. After that it needs a bit of time + * to be fully awake and then the radio can be enabled. + */ + rt2800usb_set_state(rt2x00dev, STATE_AWAKE); + msleep(1); + retval = rt2800usb_enable_radio(rt2x00dev); + break; + case STATE_RADIO_OFF: + /* + * After the radio has been disabled, the device should + * be put to sleep for powersaving. + */ + rt2800usb_disable_radio(rt2x00dev); + rt2800usb_set_state(rt2x00dev, STATE_SLEEP); + break; + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + /* No support, but no error either */ + break; + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + retval = rt2800usb_set_state(rt2x00dev, state); + break; + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +/* + * Watchdog handlers + */ +static void rt2800usb_watchdog(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); + if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) { + rt2x00_warn(rt2x00dev, "TX HW queue 0 timed out, invoke forced kick\n"); + + rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40012); + + for (i = 0; i < 10; i++) { + udelay(10); + if (!rt2x00_get_field32(reg, TXRXQ_PCNT_TX0Q)) + break; + } + + rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); + } + + rt2x00usb_register_read(rt2x00dev, TXRXQ_PCNT, ®); + if (rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) { + rt2x00_warn(rt2x00dev, "TX HW queue 1 timed out, invoke forced kick\n"); + + rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf4000a); + + for (i = 0; i < 10; i++) { + udelay(10); + if (!rt2x00_get_field32(reg, TXRXQ_PCNT_TX1Q)) + break; + } + + rt2x00usb_register_write(rt2x00dev, PBF_CFG, 0xf40006); + } + + rt2x00usb_watchdog(rt2x00dev); +} + +/* + * TX descriptor initialization + */ +static __le32 *rt2800usb_get_txwi(struct queue_entry *entry) +{ + if (entry->queue->qid == QID_BEACON) + return (__le32 *) (entry->skb->data); + else + return (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE); +} + +static void rt2800usb_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *txi = (__le32 *) entry->skb->data; + u32 word; + + /* + * Initialize TXINFO descriptor + */ + rt2x00_desc_read(txi, 0, &word); + + /* + * The size of TXINFO_W0_USB_DMA_TX_PKT_LEN is + * TXWI + 802.11 header + L2 pad + payload + pad, + * so need to decrease size of TXINFO. + */ + rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_PKT_LEN, + roundup(entry->skb->len, 4) - TXINFO_DESC_SIZE); + rt2x00_set_field32(&word, TXINFO_W0_WIV, + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); + rt2x00_set_field32(&word, TXINFO_W0_QSEL, 2); + rt2x00_set_field32(&word, TXINFO_W0_SW_USE_LAST_ROUND, 0); + rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_NEXT_VALID, 0); + rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_desc_write(txi, 0, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->flags |= SKBDESC_DESC_IN_SKB; + skbdesc->desc = txi; + skbdesc->desc_len = TXINFO_DESC_SIZE + entry->queue->winfo_size; +} + +/* + * TX data initialization + */ +static int rt2800usb_get_tx_data_len(struct queue_entry *entry) +{ + /* + * pad(1~3 bytes) is needed after each 802.11 payload. + * USB end pad(4 bytes) is needed at each USB bulk out packet end. + * TX frame format is : + * | TXINFO | TXWI | 802.11 header | L2 pad | payload | pad | USB end pad | + * |<------------- tx_pkt_len ------------->| + */ + + return roundup(entry->skb->len, 4) + 4; +} + +/* + * TX control handlers + */ +static enum txdone_entry_desc_flags +rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) +{ + __le32 *txwi; + u32 word; + int wcid, ack, pid; + int tx_wcid, tx_ack, tx_pid, is_agg; + + /* + * This frames has returned with an IO error, + * so the status report is not intended for this + * frame. + */ + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + return TXDONE_FAILURE; + + wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID); + ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED); + pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE); + is_agg = rt2x00_get_field32(reg, TX_STA_FIFO_TX_AGGRE); + + /* + * Validate if this TX status report is intended for + * this entry by comparing the WCID/ACK/PID fields. + */ + txwi = rt2800usb_get_txwi(entry); + + rt2x00_desc_read(txwi, 1, &word); + tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); + tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK); + tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID); + + if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) { + rt2x00_dbg(entry->queue->rt2x00dev, + "TX status report missed for queue %d entry %d\n", + entry->queue->qid, entry->entry_idx); + return TXDONE_UNKNOWN; + } + + return TXDONE_SUCCESS; +} + +static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + u32 reg; + u8 qid; + enum txdone_entry_desc_flags done_status; + + while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) { + /* + * TX_STA_FIFO_PID_QUEUE is a 2-bit field, thus qid is + * guaranteed to be one of the TX QIDs . + */ + qid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_QUEUE); + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + + if (unlikely(rt2x00queue_empty(queue))) { + rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", + qid); + break; + } + + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + + if (unlikely(test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))) { + rt2x00_warn(rt2x00dev, "Data pending for entry %u in queue %u\n", + entry->entry_idx, qid); + break; + } + + done_status = rt2800usb_txdone_entry_check(entry, reg); + if (likely(done_status == TXDONE_SUCCESS)) + rt2800_txdone_entry(entry, reg, rt2800usb_get_txwi(entry)); + else + rt2x00lib_txdone_noinfo(entry, done_status); + } +} + +static void rt2800usb_txdone_nostatus(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + struct queue_entry *entry; + + /* + * Process any trailing TX status reports for IO failures, + * we loop until we find the first non-IO error entry. This + * can either be a frame which is free, is being uploaded, + * or has completed the upload but didn't have an entry + * in the TX_STAT_FIFO register yet. + */ + tx_queue_for_each(rt2x00dev, queue) { + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); + + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + break; + + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); + else if (rt2800usb_entry_txstatus_timeout(entry)) + rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); + else + break; + } + } +} + +static void rt2800usb_work_txdone(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, txdone_work); + + while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) || + rt2800usb_txstatus_timeout(rt2x00dev)) { + + rt2800usb_txdone(rt2x00dev); + + rt2800usb_txdone_nostatus(rt2x00dev); + + /* + * The hw may delay sending the packet after DMA complete + * if the medium is busy, thus the TX_STA_FIFO entry is + * also delayed -> use a timer to retrieve it. + */ + if (rt2800usb_txstatus_pending(rt2x00dev)) + rt2800usb_async_read_tx_status(rt2x00dev); + } +} + +/* + * RX control handlers + */ +static void rt2800usb_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *rxi = (__le32 *)entry->skb->data; + __le32 *rxd; + u32 word; + int rx_pkt_len; + + /* + * Copy descriptor to the skbdesc->desc buffer, making it safe from + * moving of frame data in rt2x00usb. + */ + memcpy(skbdesc->desc, rxi, skbdesc->desc_len); + + /* + * RX frame format is : + * | RXINFO | RXWI | header | L2 pad | payload | pad | RXD | USB pad | + * |<------------ rx_pkt_len -------------->| + */ + rt2x00_desc_read(rxi, 0, &word); + rx_pkt_len = rt2x00_get_field32(word, RXINFO_W0_USB_DMA_RX_PKT_LEN); + + /* + * Remove the RXINFO structure from the sbk. + */ + skb_pull(entry->skb, RXINFO_DESC_SIZE); + + /* + * Check for rx_pkt_len validity. Return if invalid, leaving + * rxdesc->size zeroed out by the upper level. + */ + if (unlikely(rx_pkt_len == 0 || + rx_pkt_len > entry->queue->data_size)) { + rt2x00_err(entry->queue->rt2x00dev, + "Bad frame size %d, forcing to 0\n", rx_pkt_len); + return; + } + + rxd = (__le32 *)(entry->skb->data + rx_pkt_len); + + /* + * It is now safe to read the descriptor on all architectures. + */ + rt2x00_desc_read(rxd, 0, &word); + + if (rt2x00_get_field32(word, RXD_W0_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W0_CIPHER_ERROR); + + if (rt2x00_get_field32(word, RXD_W0_DECRYPTED)) { + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. Unfortunately the descriptor doesn't contain + * any fields with the EIV/IV data either, so they can't + * be restored by rt2x00lib. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + if (rt2x00_get_field32(word, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + if (rt2x00_get_field32(word, RXD_W0_L2PAD)) + rxdesc->dev_flags |= RXDONE_L2PAD; + + /* + * Remove RXD descriptor from end of buffer. + */ + skb_trim(entry->skb, rx_pkt_len); + + /* + * Process the RXWI structure. + */ + rt2800_process_rxwi(entry, rxdesc); +} + +/* + * Device probe functions. + */ +static int rt2800usb_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800usb_autorun_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) + return 1; + return rt2800_efuse_detect(rt2x00dev); +} + +static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800usb_efuse_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) + retval = rt2800_read_eeprom_efuse(rt2x00dev); + else + retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, + EEPROM_SIZE); + + return retval; +} + +static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800_probe_hw(rt2x00dev); + if (retval) + return retval; + + /* + * Set txstatus timer function. + */ + rt2x00dev->txstatus_timer.function = rt2800usb_tx_sta_fifo_timeout; + + /* + * Overwrite TX done handler + */ + INIT_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone); + + return 0; +} + +static const struct ieee80211_ops rt2800usb_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .get_tkip_seq = rt2800_get_tkip_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .sta_add = rt2x00mac_sta_add, + .sta_remove = rt2x00mac_sta_remove, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .ampdu_action = rt2800_ampdu_action, + .flush = rt2x00mac_flush, + .get_survey = rt2800_get_survey, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2800_ops rt2800usb_rt2800_ops = { + .register_read = rt2x00usb_register_read, + .register_read_lock = rt2x00usb_register_read_lock, + .register_write = rt2x00usb_register_write, + .register_write_lock = rt2x00usb_register_write_lock, + .register_multiread = rt2x00usb_register_multiread, + .register_multiwrite = rt2x00usb_register_multiwrite, + .regbusy_read = rt2x00usb_regbusy_read, + .read_eeprom = rt2800usb_read_eeprom, + .hwcrypt_disabled = rt2800usb_hwcrypt_disabled, + .drv_write_firmware = rt2800usb_write_firmware, + .drv_init_registers = rt2800usb_init_registers, + .drv_get_txwi = rt2800usb_get_txwi, +}; + +static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { + .probe_hw = rt2800usb_probe_hw, + .get_firmware_name = rt2800usb_get_firmware_name, + .check_firmware = rt2800_check_firmware, + .load_firmware = rt2800_load_firmware, + .initialize = rt2x00usb_initialize, + .uninitialize = rt2x00usb_uninitialize, + .clear_entry = rt2x00usb_clear_entry, + .set_device_state = rt2800usb_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .gain_calibration = rt2800_gain_calibration, + .vco_calibration = rt2800_vco_calibration, + .watchdog = rt2800usb_watchdog, + .start_queue = rt2800usb_start_queue, + .kick_queue = rt2x00usb_kick_queue, + .stop_queue = rt2800usb_stop_queue, + .flush_queue = rt2x00usb_flush_queue, + .tx_dma_done = rt2800usb_tx_dma_done, + .write_tx_desc = rt2800usb_write_tx_desc, + .write_tx_data = rt2800_write_tx_data, + .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, + .get_tx_data_len = rt2800usb_get_tx_data_len, + .fill_rxdone = rt2800usb_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, + .sta_add = rt2800_sta_add, + .sta_remove = rt2800_sta_remove, +}; + +static void rt2800usb_queue_init(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; + + rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); + + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXINFO_DESC_SIZE; + queue->winfo_size = rxwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 16; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXINFO_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_BEACON: + queue->limit = 8; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXINFO_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} + +static const struct rt2x00_ops rt2800usb_ops = { + .name = KBUILD_MODNAME, + .drv_data_size = sizeof(struct rt2800_drv_data), + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2800usb_queue_init, + .lib = &rt2800usb_rt2x00_ops, + .drv = &rt2800usb_rt2800_ops, + .hw = &rt2800usb_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +/* + * rt2800usb module information. + */ +static struct usb_device_id rt2800usb_device_table[] = { + /* Abocom */ + { USB_DEVICE(0x07b8, 0x2870) }, + { USB_DEVICE(0x07b8, 0x2770) }, + { USB_DEVICE(0x07b8, 0x3070) }, + { USB_DEVICE(0x07b8, 0x3071) }, + { USB_DEVICE(0x07b8, 0x3072) }, + { USB_DEVICE(0x1482, 0x3c09) }, + /* AirTies */ + { USB_DEVICE(0x1eda, 0x2012) }, + { USB_DEVICE(0x1eda, 0x2210) }, + { USB_DEVICE(0x1eda, 0x2310) }, + /* Allwin */ + { USB_DEVICE(0x8516, 0x2070) }, + { USB_DEVICE(0x8516, 0x2770) }, + { USB_DEVICE(0x8516, 0x2870) }, + { USB_DEVICE(0x8516, 0x3070) }, + { USB_DEVICE(0x8516, 0x3071) }, + { USB_DEVICE(0x8516, 0x3072) }, + /* Alpha Networks */ + { USB_DEVICE(0x14b2, 0x3c06) }, + { USB_DEVICE(0x14b2, 0x3c07) }, + { USB_DEVICE(0x14b2, 0x3c09) }, + { USB_DEVICE(0x14b2, 0x3c12) }, + { USB_DEVICE(0x14b2, 0x3c23) }, + { USB_DEVICE(0x14b2, 0x3c25) }, + { USB_DEVICE(0x14b2, 0x3c27) }, + { USB_DEVICE(0x14b2, 0x3c28) }, + { USB_DEVICE(0x14b2, 0x3c2c) }, + /* Amit */ + { USB_DEVICE(0x15c5, 0x0008) }, + /* Askey */ + { USB_DEVICE(0x1690, 0x0740) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x1731) }, + { USB_DEVICE(0x0b05, 0x1732) }, + { USB_DEVICE(0x0b05, 0x1742) }, + { USB_DEVICE(0x0b05, 0x1784) }, + { USB_DEVICE(0x1761, 0x0b05) }, + /* AzureWave */ + { USB_DEVICE(0x13d3, 0x3247) }, + { USB_DEVICE(0x13d3, 0x3273) }, + { USB_DEVICE(0x13d3, 0x3305) }, + { USB_DEVICE(0x13d3, 0x3307) }, + { USB_DEVICE(0x13d3, 0x3321) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x8053) }, + { USB_DEVICE(0x050d, 0x805c) }, + { USB_DEVICE(0x050d, 0x815c) }, + { USB_DEVICE(0x050d, 0x825a) }, + { USB_DEVICE(0x050d, 0x825b) }, + { USB_DEVICE(0x050d, 0x935a) }, + { USB_DEVICE(0x050d, 0x935b) }, + /* Buffalo */ + { USB_DEVICE(0x0411, 0x00e8) }, + { USB_DEVICE(0x0411, 0x0158) }, + { USB_DEVICE(0x0411, 0x015d) }, + { USB_DEVICE(0x0411, 0x016f) }, + { USB_DEVICE(0x0411, 0x01a2) }, + { USB_DEVICE(0x0411, 0x01ee) }, + { USB_DEVICE(0x0411, 0x01a8) }, + /* Corega */ + { USB_DEVICE(0x07aa, 0x002f) }, + { USB_DEVICE(0x07aa, 0x003c) }, + { USB_DEVICE(0x07aa, 0x003f) }, + { USB_DEVICE(0x18c5, 0x0012) }, + /* D-Link */ + { USB_DEVICE(0x07d1, 0x3c09) }, + { USB_DEVICE(0x07d1, 0x3c0a) }, + { USB_DEVICE(0x07d1, 0x3c0d) }, + { USB_DEVICE(0x07d1, 0x3c0e) }, + { USB_DEVICE(0x07d1, 0x3c0f) }, + { USB_DEVICE(0x07d1, 0x3c11) }, + { USB_DEVICE(0x07d1, 0x3c13) }, + { USB_DEVICE(0x07d1, 0x3c15) }, + { USB_DEVICE(0x07d1, 0x3c16) }, + { USB_DEVICE(0x07d1, 0x3c17) }, + { USB_DEVICE(0x2001, 0x3317) }, + { USB_DEVICE(0x2001, 0x3c1b) }, + /* Draytek */ + { USB_DEVICE(0x07fa, 0x7712) }, + /* DVICO */ + { USB_DEVICE(0x0fe9, 0xb307) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x4085) }, + { USB_DEVICE(0x7392, 0x7711) }, + { USB_DEVICE(0x7392, 0x7717) }, + { USB_DEVICE(0x7392, 0x7718) }, + { USB_DEVICE(0x7392, 0x7722) }, + /* Encore */ + { USB_DEVICE(0x203d, 0x1480) }, + { USB_DEVICE(0x203d, 0x14a9) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x9701) }, + { USB_DEVICE(0x1740, 0x9702) }, + { USB_DEVICE(0x1740, 0x9703) }, + { USB_DEVICE(0x1740, 0x9705) }, + { USB_DEVICE(0x1740, 0x9706) }, + { USB_DEVICE(0x1740, 0x9707) }, + { USB_DEVICE(0x1740, 0x9708) }, + { USB_DEVICE(0x1740, 0x9709) }, + /* Gemtek */ + { USB_DEVICE(0x15a9, 0x0012) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x800b) }, + { USB_DEVICE(0x1044, 0x800d) }, + /* Hawking */ + { USB_DEVICE(0x0e66, 0x0001) }, + { USB_DEVICE(0x0e66, 0x0003) }, + { USB_DEVICE(0x0e66, 0x0009) }, + { USB_DEVICE(0x0e66, 0x000b) }, + { USB_DEVICE(0x0e66, 0x0013) }, + { USB_DEVICE(0x0e66, 0x0017) }, + { USB_DEVICE(0x0e66, 0x0018) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x0945) }, + { USB_DEVICE(0x04bb, 0x0947) }, + { USB_DEVICE(0x04bb, 0x0948) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x0031) }, + { USB_DEVICE(0x1737, 0x0070) }, + { USB_DEVICE(0x1737, 0x0071) }, + { USB_DEVICE(0x1737, 0x0077) }, + { USB_DEVICE(0x1737, 0x0078) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0162) }, + { USB_DEVICE(0x0789, 0x0163) }, + { USB_DEVICE(0x0789, 0x0164) }, + { USB_DEVICE(0x0789, 0x0166) }, + /* Motorola */ + { USB_DEVICE(0x100d, 0x9031) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x3820) }, + { USB_DEVICE(0x0db0, 0x3821) }, + { USB_DEVICE(0x0db0, 0x3822) }, + { USB_DEVICE(0x0db0, 0x3870) }, + { USB_DEVICE(0x0db0, 0x3871) }, + { USB_DEVICE(0x0db0, 0x6899) }, + { USB_DEVICE(0x0db0, 0x821a) }, + { USB_DEVICE(0x0db0, 0x822a) }, + { USB_DEVICE(0x0db0, 0x822b) }, + { USB_DEVICE(0x0db0, 0x822c) }, + { USB_DEVICE(0x0db0, 0x870a) }, + { USB_DEVICE(0x0db0, 0x871a) }, + { USB_DEVICE(0x0db0, 0x871b) }, + { USB_DEVICE(0x0db0, 0x871c) }, + { USB_DEVICE(0x0db0, 0x899a) }, + /* Ovislink */ + { USB_DEVICE(0x1b75, 0x3071) }, + { USB_DEVICE(0x1b75, 0x3072) }, + /* Para */ + { USB_DEVICE(0x20b8, 0x8888) }, + /* Pegatron */ + { USB_DEVICE(0x1d4d, 0x0002) }, + { USB_DEVICE(0x1d4d, 0x000c) }, + { USB_DEVICE(0x1d4d, 0x000e) }, + { USB_DEVICE(0x1d4d, 0x0011) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x200f) }, + /* Planex */ + { USB_DEVICE(0x2019, 0x5201) }, + { USB_DEVICE(0x2019, 0xab25) }, + { USB_DEVICE(0x2019, 0xed06) }, + /* Quanta */ + { USB_DEVICE(0x1a32, 0x0304) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x2070) }, + { USB_DEVICE(0x148f, 0x2770) }, + { USB_DEVICE(0x148f, 0x2870) }, + { USB_DEVICE(0x148f, 0x3070) }, + { USB_DEVICE(0x148f, 0x3071) }, + { USB_DEVICE(0x148f, 0x3072) }, + /* Samsung */ + { USB_DEVICE(0x04e8, 0x2018) }, + /* Siemens */ + { USB_DEVICE(0x129b, 0x1828) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0017) }, + { USB_DEVICE(0x0df6, 0x002b) }, + { USB_DEVICE(0x0df6, 0x002c) }, + { USB_DEVICE(0x0df6, 0x002d) }, + { USB_DEVICE(0x0df6, 0x0039) }, + { USB_DEVICE(0x0df6, 0x003b) }, + { USB_DEVICE(0x0df6, 0x003d) }, + { USB_DEVICE(0x0df6, 0x003e) }, + { USB_DEVICE(0x0df6, 0x003f) }, + { USB_DEVICE(0x0df6, 0x0040) }, + { USB_DEVICE(0x0df6, 0x0042) }, + { USB_DEVICE(0x0df6, 0x0047) }, + { USB_DEVICE(0x0df6, 0x0048) }, + { USB_DEVICE(0x0df6, 0x0051) }, + { USB_DEVICE(0x0df6, 0x005f) }, + { USB_DEVICE(0x0df6, 0x0060) }, + /* SMC */ + { USB_DEVICE(0x083a, 0x6618) }, + { USB_DEVICE(0x083a, 0x7511) }, + { USB_DEVICE(0x083a, 0x7512) }, + { USB_DEVICE(0x083a, 0x7522) }, + { USB_DEVICE(0x083a, 0x8522) }, + { USB_DEVICE(0x083a, 0xa618) }, + { USB_DEVICE(0x083a, 0xa701) }, + { USB_DEVICE(0x083a, 0xa702) }, + { USB_DEVICE(0x083a, 0xa703) }, + { USB_DEVICE(0x083a, 0xb522) }, + /* Sparklan */ + { USB_DEVICE(0x15a9, 0x0006) }, + /* Sweex */ + { USB_DEVICE(0x177f, 0x0153) }, + { USB_DEVICE(0x177f, 0x0164) }, + { USB_DEVICE(0x177f, 0x0302) }, + { USB_DEVICE(0x177f, 0x0313) }, + { USB_DEVICE(0x177f, 0x0323) }, + { USB_DEVICE(0x177f, 0x0324) }, + /* U-Media */ + { USB_DEVICE(0x157e, 0x300e) }, + { USB_DEVICE(0x157e, 0x3013) }, + /* ZCOM */ + { USB_DEVICE(0x0cde, 0x0022) }, + { USB_DEVICE(0x0cde, 0x0025) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0280) }, + { USB_DEVICE(0x5a57, 0x0282) }, + { USB_DEVICE(0x5a57, 0x0283) }, + { USB_DEVICE(0x5a57, 0x5257) }, + /* Zyxel */ + { USB_DEVICE(0x0586, 0x3416) }, + { USB_DEVICE(0x0586, 0x3418) }, + { USB_DEVICE(0x0586, 0x341a) }, + { USB_DEVICE(0x0586, 0x341e) }, + { USB_DEVICE(0x0586, 0x343e) }, +#ifdef CONFIG_RT2800USB_RT33XX + /* Belkin */ + { USB_DEVICE(0x050d, 0x945b) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c17) }, + /* Panasonic */ + { USB_DEVICE(0x083a, 0xb511) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x20dd) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x3370) }, + { USB_DEVICE(0x148f, 0x8070) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0050) }, + /* Sweex */ + { USB_DEVICE(0x177f, 0x0163) }, + { USB_DEVICE(0x177f, 0x0165) }, +#endif +#ifdef CONFIG_RT2800USB_RT35XX + /* Allwin */ + { USB_DEVICE(0x8516, 0x3572) }, + /* Askey */ + { USB_DEVICE(0x1690, 0x0744) }, + { USB_DEVICE(0x1690, 0x0761) }, + { USB_DEVICE(0x1690, 0x0764) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x179d) }, + /* Cisco */ + { USB_DEVICE(0x167b, 0x4001) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x9801) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x0944) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x002f) }, + { USB_DEVICE(0x1737, 0x0079) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0170) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x3572) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0041) }, + { USB_DEVICE(0x0df6, 0x0062) }, + { USB_DEVICE(0x0df6, 0x0065) }, + { USB_DEVICE(0x0df6, 0x0066) }, + { USB_DEVICE(0x0df6, 0x0068) }, + /* Toshiba */ + { USB_DEVICE(0x0930, 0x0a07) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0284) }, +#endif +#ifdef CONFIG_RT2800USB_RT3573 + /* AirLive */ + { USB_DEVICE(0x1b75, 0x7733) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x17bc) }, + { USB_DEVICE(0x0b05, 0x17ad) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x1103) }, + /* Cameo */ + { USB_DEVICE(0x148f, 0xf301) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c1f) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x7733) }, + /* Hawking */ + { USB_DEVICE(0x0e66, 0x0020) }, + { USB_DEVICE(0x0e66, 0x0021) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x094e) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x003b) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x016b) }, + /* NETGEAR */ + { USB_DEVICE(0x0846, 0x9012) }, + { USB_DEVICE(0x0846, 0x9013) }, + { USB_DEVICE(0x0846, 0x9019) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xed19) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x3573) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x0067) }, + { USB_DEVICE(0x0df6, 0x006a) }, + { USB_DEVICE(0x0df6, 0x006e) }, + /* ZyXEL */ + { USB_DEVICE(0x0586, 0x3421) }, +#endif +#ifdef CONFIG_RT2800USB_RT53XX + /* Arcadyan */ + { USB_DEVICE(0x043e, 0x7a12) }, + { USB_DEVICE(0x043e, 0x7a32) }, + /* Azurewave */ + { USB_DEVICE(0x13d3, 0x3329) }, + { USB_DEVICE(0x13d3, 0x3365) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c15) }, + { USB_DEVICE(0x2001, 0x3c19) }, + { USB_DEVICE(0x2001, 0x3c1c) }, + { USB_DEVICE(0x2001, 0x3c1d) }, + { USB_DEVICE(0x2001, 0x3c1e) }, + { USB_DEVICE(0x2001, 0x3c20) }, + { USB_DEVICE(0x2001, 0x3c22) }, + { USB_DEVICE(0x2001, 0x3c23) }, + /* LG innotek */ + { USB_DEVICE(0x043e, 0x7a22) }, + { USB_DEVICE(0x043e, 0x7a42) }, + /* Panasonic */ + { USB_DEVICE(0x04da, 0x1801) }, + { USB_DEVICE(0x04da, 0x1800) }, + { USB_DEVICE(0x04da, 0x23f6) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x2104) }, + { USB_DEVICE(0x0471, 0x2126) }, + { USB_DEVICE(0x0471, 0x2180) }, + { USB_DEVICE(0x0471, 0x2181) }, + { USB_DEVICE(0x0471, 0x2182) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x5370) }, + { USB_DEVICE(0x148f, 0x5372) }, +#endif +#ifdef CONFIG_RT2800USB_RT55XX + /* Arcadyan */ + { USB_DEVICE(0x043e, 0x7a32) }, + /* AVM GmbH */ + { USB_DEVICE(0x057c, 0x8501) }, + /* Buffalo */ + { USB_DEVICE(0x0411, 0x0241) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c1a) }, + { USB_DEVICE(0x2001, 0x3c21) }, + /* Proware */ + { USB_DEVICE(0x043e, 0x7a13) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x5572) }, + /* TRENDnet */ + { USB_DEVICE(0x20f4, 0x724a) }, +#endif +#ifdef CONFIG_RT2800USB_UNKNOWN + /* + * Unclear what kind of devices these are (they aren't supported by the + * vendor linux driver). + */ + /* Abocom */ + { USB_DEVICE(0x07b8, 0x3073) }, + { USB_DEVICE(0x07b8, 0x3074) }, + /* Alpha Networks */ + { USB_DEVICE(0x14b2, 0x3c08) }, + { USB_DEVICE(0x14b2, 0x3c11) }, + /* Amigo */ + { USB_DEVICE(0x0e0b, 0x9031) }, + { USB_DEVICE(0x0e0b, 0x9041) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x166a) }, + { USB_DEVICE(0x0b05, 0x1760) }, + { USB_DEVICE(0x0b05, 0x1761) }, + { USB_DEVICE(0x0b05, 0x1790) }, + { USB_DEVICE(0x0b05, 0x17a7) }, + /* AzureWave */ + { USB_DEVICE(0x13d3, 0x3262) }, + { USB_DEVICE(0x13d3, 0x3284) }, + { USB_DEVICE(0x13d3, 0x3322) }, + { USB_DEVICE(0x13d3, 0x3340) }, + { USB_DEVICE(0x13d3, 0x3399) }, + { USB_DEVICE(0x13d3, 0x3400) }, + { USB_DEVICE(0x13d3, 0x3401) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x1003) }, + /* Buffalo */ + { USB_DEVICE(0x0411, 0x012e) }, + { USB_DEVICE(0x0411, 0x0148) }, + { USB_DEVICE(0x0411, 0x0150) }, + /* Corega */ + { USB_DEVICE(0x07aa, 0x0041) }, + { USB_DEVICE(0x07aa, 0x0042) }, + { USB_DEVICE(0x18c5, 0x0008) }, + /* D-Link */ + { USB_DEVICE(0x07d1, 0x3c0b) }, + /* Encore */ + { USB_DEVICE(0x203d, 0x14a1) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x0600) }, + { USB_DEVICE(0x1740, 0x0602) }, + /* Gemtek */ + { USB_DEVICE(0x15a9, 0x0010) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x800c) }, + /* Hercules */ + { USB_DEVICE(0x06f8, 0xe036) }, + /* Huawei */ + { USB_DEVICE(0x148f, 0xf101) }, + /* I-O DATA */ + { USB_DEVICE(0x04bb, 0x094b) }, + /* LevelOne */ + { USB_DEVICE(0x1740, 0x0605) }, + { USB_DEVICE(0x1740, 0x0615) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0168) }, + { USB_DEVICE(0x0789, 0x0169) }, + /* Motorola */ + { USB_DEVICE(0x100d, 0x9032) }, + /* Pegatron */ + { USB_DEVICE(0x05a6, 0x0101) }, + { USB_DEVICE(0x1d4d, 0x0010) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xab24) }, + { USB_DEVICE(0x2019, 0xab29) }, + /* Qcom */ + { USB_DEVICE(0x18e8, 0x6259) }, + /* RadioShack */ + { USB_DEVICE(0x08b9, 0x1197) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x003c) }, + { USB_DEVICE(0x0df6, 0x004a) }, + { USB_DEVICE(0x0df6, 0x004d) }, + { USB_DEVICE(0x0df6, 0x0053) }, + { USB_DEVICE(0x0df6, 0x0069) }, + { USB_DEVICE(0x0df6, 0x006f) }, + /* SMC */ + { USB_DEVICE(0x083a, 0xa512) }, + { USB_DEVICE(0x083a, 0xc522) }, + { USB_DEVICE(0x083a, 0xd522) }, + { USB_DEVICE(0x083a, 0xf511) }, + /* Sweex */ + { USB_DEVICE(0x177f, 0x0254) }, + /* TP-LINK */ + { USB_DEVICE(0xf201, 0x5370) }, +#endif + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2800 USB Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2870 USB chipset based cards"); +MODULE_DEVICE_TABLE(usb, rt2800usb_device_table); +MODULE_FIRMWARE(FIRMWARE_RT2870); +MODULE_LICENSE("GPL"); + +static int rt2800usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + return rt2x00usb_probe(usb_intf, &rt2800usb_ops); +} + +static struct usb_driver rt2800usb_driver = { + .name = KBUILD_MODNAME, + .id_table = rt2800usb_device_table, + .probe = rt2800usb_probe, + .disconnect = rt2x00usb_disconnect, + .suspend = rt2x00usb_suspend, + .resume = rt2x00usb_resume, + .reset_resume = rt2x00usb_resume, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(rt2800usb_driver); diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/rt2x00/rt2800usb.h new file mode 100644 index 00000000000..ea7cac09599 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800usb.h @@ -0,0 +1,110 @@ +/* + Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + Copyright (C) 2009 Axel Kollhofer <rain_maker@root-forum.org> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2800usb + Abstract: Data structures and registers for the rt2800usb module. + Supported chipsets: RT2800U. + */ + +#ifndef RT2800USB_H +#define RT2800USB_H + +/* + * 8051 firmware image. + */ +#define FIRMWARE_RT2870 "rt2870.bin" +#define FIRMWARE_IMAGE_BASE 0x3000 + +/* + * DMA descriptor defines. + */ +#define TXINFO_DESC_SIZE (1 * sizeof(__le32)) +#define RXINFO_DESC_SIZE (1 * sizeof(__le32)) + +/* + * TX Info structure + */ + +/* + * Word0 + * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI + * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. + * 0:MGMT, 1:HCCA 2:EDCA + * USB_DMA_NEXT_VALID: Used ONLY in USB bulk Aggregation, NextValid + * DMA_TX_BURST: used ONLY in USB bulk Aggregation. + * Force USB DMA transmit frame from current selected endpoint + */ +#define TXINFO_W0_USB_DMA_TX_PKT_LEN FIELD32(0x0000ffff) +#define TXINFO_W0_WIV FIELD32(0x01000000) +#define TXINFO_W0_QSEL FIELD32(0x06000000) +#define TXINFO_W0_SW_USE_LAST_ROUND FIELD32(0x08000000) +#define TXINFO_W0_USB_DMA_NEXT_VALID FIELD32(0x40000000) +#define TXINFO_W0_USB_DMA_TX_BURST FIELD32(0x80000000) + +/* + * RX Info structure + */ + +/* + * Word 0 + */ + +#define RXINFO_W0_USB_DMA_RX_PKT_LEN FIELD32(0x0000ffff) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + * UNICAST_TO_ME: This RX frame is unicast to me. + * MULTICAST: This is a multicast frame. + * BROADCAST: This is a broadcast frame. + * MY_BSS: this frame belongs to the same BSSID. + * CRC_ERROR: CRC error. + * CIPHER_ERROR: 0: decryption okay, 1:ICV error, 2:MIC error, 3:KEY not valid. + * AMSDU: rx with 802.3 header, not 802.11 header. + */ + +#define RXD_W0_BA FIELD32(0x00000001) +#define RXD_W0_DATA FIELD32(0x00000002) +#define RXD_W0_NULLDATA FIELD32(0x00000004) +#define RXD_W0_FRAG FIELD32(0x00000008) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000010) +#define RXD_W0_MULTICAST FIELD32(0x00000020) +#define RXD_W0_BROADCAST FIELD32(0x00000040) +#define RXD_W0_MY_BSS FIELD32(0x00000080) +#define RXD_W0_CRC_ERROR FIELD32(0x00000100) +#define RXD_W0_CIPHER_ERROR FIELD32(0x00000600) +#define RXD_W0_AMSDU FIELD32(0x00000800) +#define RXD_W0_HTC FIELD32(0x00001000) +#define RXD_W0_RSSI FIELD32(0x00002000) +#define RXD_W0_L2PAD FIELD32(0x00004000) +#define RXD_W0_AMPDU FIELD32(0x00008000) +#define RXD_W0_DECRYPTED FIELD32(0x00010000) +#define RXD_W0_PLCP_RSSI FIELD32(0x00020000) +#define RXD_W0_CIPHER_ALG FIELD32(0x00040000) +#define RXD_W0_LAST_AMSDU FIELD32(0x00080000) +#define RXD_W0_PLCP_SIGNAL FIELD32(0xfff00000) + +#endif /* RT2800USB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 6c725422af5..d13f25cd70d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1,5 +1,7 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> + Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +15,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -27,88 +27,93 @@ #define RT2X00_H #include <linux/bitops.h> -#include <linux/prefetch.h> +#include <linux/interrupt.h> #include <linux/skbuff.h> #include <linux/workqueue.h> #include <linux/firmware.h> +#include <linux/leds.h> #include <linux/mutex.h> #include <linux/etherdevice.h> +#include <linux/input-polldev.h> +#include <linux/kfifo.h> +#include <linux/hrtimer.h> +#include <linux/average.h> #include <net/mac80211.h> #include "rt2x00debug.h" +#include "rt2x00dump.h" +#include "rt2x00leds.h" #include "rt2x00reg.h" -#include "rt2x00ring.h" +#include "rt2x00queue.h" /* * Module information. */ -#define DRV_VERSION "2.0.14" +#define DRV_VERSION "2.3.0" #define DRV_PROJECT "http://rt2x00.serialmonkey.com" -/* - * Debug definitions. +/* Debug definitions. * Debug output has to be enabled during compile time. */ -#define DEBUG_PRINTK_MSG(__dev, __kernlvl, __lvl, __msg, __args...) \ - printk(__kernlvl "%s -> %s: %s - " __msg, \ - wiphy_name((__dev)->hw->wiphy), __FUNCTION__, __lvl, ##__args) - -#define DEBUG_PRINTK_PROBE(__kernlvl, __lvl, __msg, __args...) \ - printk(__kernlvl "%s -> %s: %s - " __msg, \ - KBUILD_MODNAME, __FUNCTION__, __lvl, ##__args) - #ifdef CONFIG_RT2X00_DEBUG -#define DEBUG_PRINTK(__dev, __kernlvl, __lvl, __msg, __args...) \ - DEBUG_PRINTK_MSG(__dev, __kernlvl, __lvl, __msg, ##__args); -#else -#define DEBUG_PRINTK(__dev, __kernlvl, __lvl, __msg, __args...) \ - do { } while (0) +#define DEBUG #endif /* CONFIG_RT2X00_DEBUG */ +/* Utility printing macros + * rt2x00_probe_err is for messages when rt2x00_dev is uninitialized + */ +#define rt2x00_probe_err(fmt, ...) \ + printk(KERN_ERR KBUILD_MODNAME ": %s: Error - " fmt, \ + __func__, ##__VA_ARGS__) +#define rt2x00_err(dev, fmt, ...) \ + wiphy_err((dev)->hw->wiphy, "%s: Error - " fmt, \ + __func__, ##__VA_ARGS__) +#define rt2x00_warn(dev, fmt, ...) \ + wiphy_warn((dev)->hw->wiphy, "%s: Warning - " fmt, \ + __func__, ##__VA_ARGS__) +#define rt2x00_info(dev, fmt, ...) \ + wiphy_info((dev)->hw->wiphy, "%s: Info - " fmt, \ + __func__, ##__VA_ARGS__) + +/* Various debug levels */ +#define rt2x00_dbg(dev, fmt, ...) \ + wiphy_dbg((dev)->hw->wiphy, "%s: Debug - " fmt, \ + __func__, ##__VA_ARGS__) +#define rt2x00_eeprom_dbg(dev, fmt, ...) \ + wiphy_dbg((dev)->hw->wiphy, "%s: EEPROM recovery - " fmt, \ + __func__, ##__VA_ARGS__) + +/* + * Duration calculations + * The rate variable passed is: 100kbs. + * To convert from bytes to bits we multiply size with 8, + * then the size is multiplied with 10 to make the + * real rate -> rate argument correction. + */ +#define GET_DURATION(__size, __rate) (((__size) * 8 * 10) / (__rate)) +#define GET_DURATION_RES(__size, __rate)(((__size) * 8 * 10) % (__rate)) + /* - * Various debug levels. - * The debug levels PANIC and ERROR both indicate serious problems, - * for this reason they should never be ignored. - * The special ERROR_PROBE message is for messages that are generated - * when the rt2x00_dev is not yet initialized. + * Determine the number of L2 padding bytes required between the header and + * the payload. */ -#define PANIC(__dev, __msg, __args...) \ - DEBUG_PRINTK_MSG(__dev, KERN_CRIT, "Panic", __msg, ##__args) -#define ERROR(__dev, __msg, __args...) \ - DEBUG_PRINTK_MSG(__dev, KERN_ERR, "Error", __msg, ##__args) -#define ERROR_PROBE(__msg, __args...) \ - DEBUG_PRINTK_PROBE(KERN_ERR, "Error", __msg, ##__args) -#define WARNING(__dev, __msg, __args...) \ - DEBUG_PRINTK(__dev, KERN_WARNING, "Warning", __msg, ##__args) -#define NOTICE(__dev, __msg, __args...) \ - DEBUG_PRINTK(__dev, KERN_NOTICE, "Notice", __msg, ##__args) -#define INFO(__dev, __msg, __args...) \ - DEBUG_PRINTK(__dev, KERN_INFO, "Info", __msg, ##__args) -#define DEBUG(__dev, __msg, __args...) \ - DEBUG_PRINTK(__dev, KERN_DEBUG, "Debug", __msg, ##__args) -#define EEPROM(__dev, __msg, __args...) \ - DEBUG_PRINTK(__dev, KERN_DEBUG, "EEPROM recovery", __msg, ##__args) +#define L2PAD_SIZE(__hdrlen) (-(__hdrlen) & 3) /* - * Ring sizes. - * Ralink PCI devices demand the Frame size to be a multiple of 128 bytes. - * DATA_FRAME_SIZE is used for TX, RX, ATIM and PRIO rings. - * MGMT_FRAME_SIZE is used for the BEACON ring. + * Determine the alignment requirement, + * to make sure the 802.11 payload is padded to a 4-byte boundrary + * we must determine the address of the payload and calculate the + * amount of bytes needed to move the data. */ -#define DATA_FRAME_SIZE 2432 -#define MGMT_FRAME_SIZE 256 +#define ALIGN_SIZE(__skb, __header) \ + ( ((unsigned long)((__skb)->data + (__header))) & 3 ) /* - * Number of entries in a packet ring. - * PCI devices only need 1 Beacon entry, - * but USB devices require a second because they - * have to send a Guardian byte first. + * Constants for extra TX headroom for alignment purposes. */ -#define RX_ENTRIES 12 -#define TX_ENTRIES 12 -#define ATIM_ENTRIES 1 -#define BEACON_ENTRIES 2 +#define RT2X00_ALIGN_SIZE 4 /* Only whole frame needs alignment */ +#define RT2X00_L2PAD_SIZE 8 /* Both header & payload need alignment */ /* * Standard timing and size defines. @@ -127,34 +132,17 @@ #define SHORT_PIFS ( SIFS + SHORT_SLOT_TIME ) #define DIFS ( PIFS + SLOT_TIME ) #define SHORT_DIFS ( SHORT_PIFS + SHORT_SLOT_TIME ) -#define EIFS ( SIFS + (8 * (IEEE80211_HEADER + ACK_SIZE)) ) - -/* - * IEEE802.11 header defines - */ -static inline int is_rts_frame(u16 fc) -{ - return (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_RTS)); -} - -static inline int is_cts_frame(u16 fc) -{ - return (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_CTS)); -} - -static inline int is_probe_resp(u16 fc) -{ - return (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)); -} - -static inline int is_beacon(u16 fc) -{ - return (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)); -} +#define EIFS ( SIFS + DIFS + \ + GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) +#define SHORT_EIFS ( SIFS + SHORT_DIFS + \ + GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) + +enum rt2x00_chip_intf { + RT2X00_CHIP_INTF_PCI, + RT2X00_CHIP_INTF_PCIE, + RT2X00_CHIP_INTF_USB, + RT2X00_CHIP_INTF_SOC, +}; /* * Chipset identification @@ -163,16 +151,31 @@ static inline int is_beacon(u16 fc) */ struct rt2x00_chip { u16 rt; -#define RT2460 0x0101 -#define RT2560 0x0201 -#define RT2570 0x1201 -#define RT2561s 0x0301 /* Turbo */ -#define RT2561 0x0302 -#define RT2661 0x0401 -#define RT2571 0x1300 +#define RT2460 0x2460 +#define RT2560 0x2560 +#define RT2570 0x2570 +#define RT2661 0x2661 +#define RT2573 0x2573 +#define RT2860 0x2860 /* 2.4GHz */ +#define RT2872 0x2872 /* WSOC */ +#define RT2883 0x2883 /* WSOC */ +#define RT3070 0x3070 +#define RT3071 0x3071 +#define RT3090 0x3090 /* 2.4GHz PCIe */ +#define RT3290 0x3290 +#define RT3352 0x3352 /* WSOC */ +#define RT3390 0x3390 +#define RT3572 0x3572 +#define RT3593 0x3593 +#define RT3883 0x3883 /* WSOC */ +#define RT5390 0x5390 /* 2.4GHz */ +#define RT5392 0x5392 /* 2.4GHz */ +#define RT5592 0x5592 u16 rf; - u32 rev; + u16 rev; + + enum rt2x00_chip_intf intf; }; /* @@ -187,11 +190,26 @@ struct rf_channel { }; /* + * Channel information structure + */ +struct channel_info { + unsigned int flags; +#define GEOGRAPHY_ALLOWED 0x00000001 + + short max_power; + short default_power1; + short default_power2; + short default_power3; +}; + +/* * Antenna setup values. */ struct antenna_setup { enum antenna rx; enum antenna tx; + u8 rx_chain_num; + u8 tx_chain_num; }; /* @@ -199,52 +217,41 @@ struct antenna_setup { */ struct link_qual { /* - * Statistics required for Link tuning. - * For the average RSSI value we use the "Walking average" approach. - * When adding RSSI to the average value the following calculation - * is needed: - * - * avg_rssi = ((avg_rssi * 7) + rssi) / 8; - * - * The advantage of this approach is that we only need 1 variable - * to store the average in (No need for a count and a total). - * But more importantly, normal average values will over time - * move less and less towards newly added values this results - * that with link tuning, the device can have a very good RSSI - * for a few minutes but when the device is moved away from the AP - * the average will not decrease fast enough to compensate. - * The walking average compensates this and will move towards - * the new values correctly allowing a effective link tuning. - */ - int avg_rssi; + * Statistics required for Link tuning by driver + * The rssi value is provided by rt2x00lib during the + * link_tuner() callback function. + * The false_cca field is filled during the link_stats() + * callback function and could be used during the + * link_tuner() callback function. + */ + int rssi; int false_cca; /* - * Statistics required for Signal quality calculation. - * For calculating the Signal quality we have to determine - * the total number of success and failed RX and TX frames. - * After that we also use the average RSSI value to help - * determining the signal quality. - * For the calculation we will use the following algorithm: - * - * rssi_percentage = (avg_rssi * 100) / rssi_offset - * rx_percentage = (rx_success * 100) / rx_total - * tx_percentage = (tx_success * 100) / tx_total - * avg_signal = ((WEIGHT_RSSI * avg_rssi) + - * (WEIGHT_TX * tx_percentage) + - * (WEIGHT_RX * rx_percentage)) / 100 + * VGC levels + * Hardware driver will tune the VGC level during each call + * to the link_tuner() callback function. This vgc_level is + * is determined based on the link quality statistics like + * average RSSI and the false CCA count. * - * This value should then be checked to not be greated then 100. + * In some cases the drivers need to differentiate between + * the currently "desired" VGC level and the level configured + * in the hardware. The latter is important to reduce the + * number of BBP register reads to reduce register access + * overhead. For this reason we store both values here. + */ + u8 vgc_level; + u8 vgc_level_reg; + + /* + * Statistics required for Signal quality calculation. + * These fields might be changed during the link_stats() + * callback function. */ - int rx_percentage; int rx_success; int rx_failed; - int tx_percentage; int tx_success; int tx_failed; -#define WEIGHT_RSSI 20 -#define WEIGHT_RX 40 -#define WEIGHT_TX 40 }; /* @@ -267,21 +274,18 @@ struct link_ant { struct antenna_setup active; /* - * RSSI information for the different antenna's. - * These statistics are used to determine when - * to switch antenna when using software diversity. - * - * rssi[0] -> Antenna A RSSI - * rssi[1] -> Antenna B RSSI + * RSSI history information for the antenna. + * Used to determine when to switch antenna + * when using software diversity. */ - int rssi_history[2]; + int rssi_history; /* * Current RSSI average of the currently active antenna. * Similar to the avg_rssi in the link_qual structure * this value is updated by using the walking average. */ - int rssi_ant; + struct ewma rssi_ant; }; /* @@ -308,122 +312,106 @@ struct link { struct link_ant ant; /* - * Active VGC level + * Currently active average RSSI value */ - int vgc_level; + struct ewma avg_rssi; /* * Work structure for scheduling periodic link tuning. */ struct delayed_work work; -}; - -/* - * Small helper macro to work with moving/walking averages. - */ -#define MOVING_AVERAGE(__avg, __val, __samples) \ - ( (((__avg) * ((__samples) - 1)) + (__val)) / (__samples) ) -/* - * When we lack RSSI information return something less then -80 to - * tell the driver to tune the device to maximum sensitivity. - */ -#define DEFAULT_RSSI ( -128 ) - -/* - * Link quality access functions. - */ -static inline int rt2x00_get_link_rssi(struct link *link) -{ - if (link->qual.avg_rssi && link->qual.rx_success) - return link->qual.avg_rssi; - return DEFAULT_RSSI; -} + /* + * Work structure for scheduling periodic watchdog monitoring. + * This work must be scheduled on the kernel workqueue, while + * all other work structures must be queued on the mac80211 + * workqueue. This guarantees that the watchdog can schedule + * other work structures and wait for their completion in order + * to bring the device/driver back into the desired state. + */ + struct delayed_work watchdog_work; -static inline int rt2x00_get_link_ant_rssi(struct link *link) -{ - if (link->ant.rssi_ant && link->qual.rx_success) - return link->ant.rssi_ant; - return DEFAULT_RSSI; -} + /* + * Work structure for scheduling periodic AGC adjustments. + */ + struct delayed_work agc_work; -static inline int rt2x00_get_link_ant_rssi_history(struct link *link, - enum antenna ant) -{ - if (link->ant.rssi_history[ant - ANTENNA_A]) - return link->ant.rssi_history[ant - ANTENNA_A]; - return DEFAULT_RSSI; -} + /* + * Work structure for scheduling periodic VCO calibration. + */ + struct delayed_work vco_work; +}; -static inline int rt2x00_update_ant_rssi(struct link *link, int rssi) -{ - int old_rssi = link->ant.rssi_history[link->ant.active.rx - ANTENNA_A]; - link->ant.rssi_history[link->ant.active.rx - ANTENNA_A] = rssi; - return old_rssi; -} +enum rt2x00_delayed_flags { + DELAYED_UPDATE_BEACON, +}; /* * Interface structure - * Configuration details about the current interface. + * Per interface configuration details, this structure + * is allocated as the private data for ieee80211_vif. */ -struct interface { +struct rt2x00_intf { /* - * Interface identification. The value is assigned - * to us by the 80211 stack, and is used to request - * new beacons. + * beacon->skb must be protected with the mutex. */ - struct ieee80211_vif *id; + struct mutex beacon_skb_mutex; /* - * Current working type (IEEE80211_IF_TYPE_*). + * Entry in the beacon queue which belongs to + * this interface. Each interface has its own + * dedicated beacon entry. */ - int type; + struct queue_entry *beacon; + bool enable_beacon; /* - * MAC of the device. + * Actions that needed rescheduling. */ - u8 mac[ETH_ALEN]; + unsigned long delayed_flags; /* - * BBSID of the AP to associate with. + * Software sequence counter, this is only required + * for hardware which doesn't support hardware + * sequence counting. */ - u8 bssid[ETH_ALEN]; + atomic_t seqno; }; -static inline int is_interface_present(struct interface *intf) -{ - return !!intf->id; -} - -static inline int is_interface_type(struct interface *intf, int type) +static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif) { - return intf->type == type; + return (struct rt2x00_intf *)vif->drv_priv; } -/* +/** + * struct hw_mode_spec: Hardware specifications structure + * * Details about the supported modes, rates and channels * of a particular chipset. This is used by rt2x00lib * to build the ieee80211_hw_mode array for mac80211. + * + * @supported_bands: Bitmask contained the supported bands (2.4GHz, 5.2GHz). + * @supported_rates: Rate types which are supported (CCK, OFDM). + * @num_channels: Number of supported channels. This is used as array size + * for @tx_power_a, @tx_power_bg and @channels. + * @channels: Device/chipset specific channel values (See &struct rf_channel). + * @channels_info: Additional information for channels (See &struct channel_info). + * @ht: Driver HT Capabilities (See &ieee80211_sta_ht_cap). */ struct hw_mode_spec { - /* - * Number of modes, rates and channels. - */ - int num_modes; - int num_rates; - int num_channels; + unsigned int supported_bands; +#define SUPPORT_BAND_2GHZ 0x00000001 +#define SUPPORT_BAND_5GHZ 0x00000002 - /* - * txpower values. - */ - const u8 *tx_power_a; - const u8 *tx_power_bg; - u8 tx_power_default; + unsigned int supported_rates; +#define SUPPORT_RATE_CCK 0x00000001 +#define SUPPORT_RATE_OFDM 0x00000002 - /* - * Device/chipset specific value. - */ + unsigned int num_channels; const struct rf_channel *channels; + const struct channel_info *channels_info; + + struct ieee80211_sta_ht_cap ht; }; /* @@ -435,22 +423,94 @@ struct hw_mode_spec { */ struct rt2x00lib_conf { struct ieee80211_conf *conf; + struct rf_channel rf; + struct channel_info channel; +}; - struct antenna_setup ant; +/* + * Configuration structure for erp settings. + */ +struct rt2x00lib_erp { + int short_preamble; + int cts_protection; - int phymode; + u32 basic_rates; - int basic_rates; int slot_time; short sifs; short pifs; short difs; short eifs; + + u16 beacon_int; + u16 ht_opmode; +}; + +/* + * Configuration structure for hardware encryption. + */ +struct rt2x00lib_crypto { + enum cipher cipher; + + enum set_key_cmd cmd; + const u8 *address; + + u32 bssidx; + + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; + + int wcid; }; /* + * Configuration structure wrapper around the + * rt2x00 interface configuration handler. + */ +struct rt2x00intf_conf { + /* + * Interface type + */ + enum nl80211_iftype type; + + /* + * TSF sync value, this is dependent on the operation type. + */ + enum tsf_sync sync; + + /* + * The MAC and BSSID addresses are simple array of bytes, + * these arrays are little endian, so when sending the addresses + * to the drivers, copy the it into a endian-signed variable. + * + * Note that all devices (except rt2500usb) have 32 bits + * register word sizes. This means that whatever variable we + * pass _must_ be a multiple of 32 bits. Otherwise the device + * might not accept what we are sending to it. + * This will also make it easier for the driver to write + * the data to the device. + */ + __le32 mac[2]; + __le32 bssid[2]; +}; + +/* + * Private structure for storing STA details + * wcid: Wireless Client ID + */ +struct rt2x00_sta { + int wcid; +}; + +static inline struct rt2x00_sta* sta_to_rt2x00_sta(struct ieee80211_sta *sta) +{ + return (struct rt2x00_sta *)sta->drv_priv; +} + +/* * rt2x00lib callback functions. */ struct rt2x00lib_ops { @@ -460,12 +520,23 @@ struct rt2x00lib_ops { irq_handler_t irq_handler; /* + * TX status tasklet handler. + */ + void (*txstatus_tasklet) (unsigned long data); + void (*pretbtt_tasklet) (unsigned long data); + void (*tbtt_tasklet) (unsigned long data); + void (*rxdone_tasklet) (unsigned long data); + void (*autowake_tasklet) (unsigned long data); + + /* * Device init handlers. */ int (*probe_hw) (struct rt2x00_dev *rt2x00dev); char *(*get_firmware_name) (struct rt2x00_dev *rt2x00dev); - int (*load_firmware) (struct rt2x00_dev *rt2x00dev, void *data, - const size_t len); + int (*check_firmware) (struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); + int (*load_firmware) (struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len); /* * Device initialization/deinitialization handlers. @@ -474,12 +545,10 @@ struct rt2x00lib_ops { void (*uninitialize) (struct rt2x00_dev *rt2x00dev); /* - * Ring initialization handlers + * queue initialization handlers */ - void (*init_rxentry) (struct rt2x00_dev *rt2x00dev, - struct data_entry *entry); - void (*init_txentry) (struct rt2x00_dev *rt2x00dev, - struct data_entry *entry); + bool (*get_entry_state) (struct queue_entry *entry); + void (*clear_entry) (struct queue_entry *entry); /* * Radio control handlers. @@ -489,50 +558,73 @@ struct rt2x00lib_ops { int (*rfkill_poll) (struct rt2x00_dev *rt2x00dev); void (*link_stats) (struct rt2x00_dev *rt2x00dev, struct link_qual *qual); - void (*reset_tuner) (struct rt2x00_dev *rt2x00dev); - void (*link_tuner) (struct rt2x00_dev *rt2x00dev); + void (*reset_tuner) (struct rt2x00_dev *rt2x00dev, + struct link_qual *qual); + void (*link_tuner) (struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count); + void (*gain_calibration) (struct rt2x00_dev *rt2x00dev); + void (*vco_calibration) (struct rt2x00_dev *rt2x00dev); + + /* + * Data queue handlers. + */ + void (*watchdog) (struct rt2x00_dev *rt2x00dev); + void (*start_queue) (struct data_queue *queue); + void (*kick_queue) (struct data_queue *queue); + void (*stop_queue) (struct data_queue *queue); + void (*flush_queue) (struct data_queue *queue, bool drop); + void (*tx_dma_done) (struct queue_entry *entry); /* * TX control handlers */ - void (*write_tx_desc) (struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txdata_entry_desc *desc, - struct ieee80211_tx_control *control); - int (*write_tx_data) (struct rt2x00_dev *rt2x00dev, - struct data_ring *ring, struct sk_buff *skb, - struct ieee80211_tx_control *control); - int (*get_tx_data_len) (struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb); - void (*kick_tx_queue) (struct rt2x00_dev *rt2x00dev, - unsigned int queue); + void (*write_tx_desc) (struct queue_entry *entry, + struct txentry_desc *txdesc); + void (*write_tx_data) (struct queue_entry *entry, + struct txentry_desc *txdesc); + void (*write_beacon) (struct queue_entry *entry, + struct txentry_desc *txdesc); + void (*clear_beacon) (struct queue_entry *entry); + int (*get_tx_data_len) (struct queue_entry *entry); /* * RX control handlers */ - void (*fill_rxdone) (struct data_entry *entry, - struct rxdata_entry_desc *desc); + void (*fill_rxdone) (struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc); /* * Configuration handlers. */ - void (*config_mac_addr) (struct rt2x00_dev *rt2x00dev, __le32 *mac); - void (*config_bssid) (struct rt2x00_dev *rt2x00dev, __le32 *bssid); - void (*config_type) (struct rt2x00_dev *rt2x00dev, const int type, - const int tsf_sync); - void (*config_preamble) (struct rt2x00_dev *rt2x00dev, - const int short_preamble, - const int ack_timeout, - const int ack_consume_time); - void (*config) (struct rt2x00_dev *rt2x00dev, const unsigned int flags, - struct rt2x00lib_conf *libconf); -#define CONFIG_UPDATE_PHYMODE ( 1 << 1 ) -#define CONFIG_UPDATE_CHANNEL ( 1 << 2 ) -#define CONFIG_UPDATE_TXPOWER ( 1 << 3 ) -#define CONFIG_UPDATE_ANTENNA ( 1 << 4 ) -#define CONFIG_UPDATE_SLOT_TIME ( 1 << 5 ) -#define CONFIG_UPDATE_BEACON_INT ( 1 << 6 ) -#define CONFIG_UPDATE_ALL 0xffff + int (*config_shared_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); + int (*config_pairwise_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); + void (*config_filter) (struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags); + void (*config_intf) (struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags); +#define CONFIG_UPDATE_TYPE ( 1 << 1 ) +#define CONFIG_UPDATE_MAC ( 1 << 2 ) +#define CONFIG_UPDATE_BSSID ( 1 << 3 ) + + void (*config_erp) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed); + void (*config_ant) (struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant); + void (*config) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int changed_flags); + int (*sta_add) (struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + int (*sta_remove) (struct rt2x00_dev *rt2x00dev, + int wcid); }; /* @@ -540,11 +632,14 @@ struct rt2x00lib_ops { */ struct rt2x00_ops { const char *name; - const unsigned int rxd_size; - const unsigned int txd_size; + const unsigned int drv_data_size; + const unsigned int max_ap_intf; const unsigned int eeprom_size; const unsigned int rf_size; + const unsigned int tx_queues; + void (*queue_init)(struct data_queue *queue); const struct rt2x00lib_ops *lib; + const void *drv; const struct ieee80211_ops *hw; #ifdef CONFIG_RT2X00_LIB_DEBUGFS const struct rt2x00debug *debugfs; @@ -552,37 +647,79 @@ struct rt2x00_ops { }; /* - * rt2x00 device flags + * rt2x00 state flags */ -enum rt2x00_flags { +enum rt2x00_state_flags { /* - * Device state flags + * Device flags */ - DEVICE_PRESENT, - DEVICE_REGISTERED_HW, - DEVICE_INITIALIZED, - DEVICE_STARTED, - DEVICE_STARTED_SUSPEND, - DEVICE_ENABLED_RADIO, - DEVICE_DISABLED_RADIO_HW, + DEVICE_STATE_PRESENT, + DEVICE_STATE_REGISTERED_HW, + DEVICE_STATE_INITIALIZED, + DEVICE_STATE_STARTED, + DEVICE_STATE_ENABLED_RADIO, + DEVICE_STATE_SCANNING, /* - * Driver features + * Driver configuration */ - DRIVER_REQUIRE_FIRMWARE, - DRIVER_REQUIRE_BEACON_RING, + CONFIG_CHANNEL_HT40, + CONFIG_POWERSAVING, + CONFIG_HT_DISABLED, + CONFIG_QOS_DISABLED, /* - * Driver configuration + * Mark we currently are sequentially reading TX_STA_FIFO register + * FIXME: this is for only rt2800usb, should go to private data + */ + TX_STATUS_READING, +}; + +/* + * rt2x00 capability flags + */ +enum rt2x00_capability_flags { + /* + * Requirements + */ + REQUIRE_FIRMWARE, + REQUIRE_BEACON_GUARD, + REQUIRE_ATIM_QUEUE, + REQUIRE_DMA, + REQUIRE_COPY_IV, + REQUIRE_L2PAD, + REQUIRE_TXSTATUS_FIFO, + REQUIRE_TASKLET_CONTEXT, + REQUIRE_SW_SEQNO, + REQUIRE_HT_TX_DESC, + REQUIRE_PS_AUTOWAKE, + REQUIRE_DELAYED_RFKILL, + + /* + * Capabilities */ - CONFIG_SUPPORT_HW_BUTTON, - CONFIG_FRAME_TYPE, - CONFIG_RF_SEQUENCE, - CONFIG_EXTERNAL_LNA_A, - CONFIG_EXTERNAL_LNA_BG, - CONFIG_DOUBLE_ANTENNA, - CONFIG_DISABLE_LINK_TUNING, - CONFIG_SHORT_PREAMBLE, + CAPABILITY_HW_BUTTON, + CAPABILITY_HW_CRYPTO, + CAPABILITY_POWER_LIMIT, + CAPABILITY_CONTROL_FILTERS, + CAPABILITY_CONTROL_FILTER_PSPOLL, + CAPABILITY_PRE_TBTT_INTERRUPT, + CAPABILITY_LINK_TUNING, + CAPABILITY_FRAME_TYPE, + CAPABILITY_RF_SEQUENCE, + CAPABILITY_EXTERNAL_LNA_A, + CAPABILITY_EXTERNAL_LNA_BG, + CAPABILITY_DOUBLE_ANTENNA, + CAPABILITY_BT_COEXIST, + CAPABILITY_VCO_RECALIBRATION, +}; + +/* + * Interface combinations + */ +enum { + IF_COMB_AP = 0, + NUM_IF_COMB, }; /* @@ -594,11 +731,9 @@ struct rt2x00_dev { * The structure stored in here depends on the * system bus (PCI or USB). * When accessing this variable, the rt2x00dev_{pci,usb} - * macro's should be used for correct typecasting. + * macros should be used for correct typecasting. */ - void *dev; -#define rt2x00dev_pci(__dev) ( (struct pci_dev*)(__dev)->dev ) -#define rt2x00dev_usb(__dev) ( (struct usb_interface*)(__dev)->dev ) + struct device *dev; /* * Callback functions. @@ -606,26 +741,17 @@ struct rt2x00_dev { const struct rt2x00_ops *ops; /* - * IEEE80211 control structure. + * Driver data. */ - struct ieee80211_hw *hw; - struct ieee80211_hw_mode *hwmodes; - unsigned int curr_hwmode; -#define HWMODE_B 0 -#define HWMODE_G 1 -#define HWMODE_A 2 + void *drv_data; /* - * rfkill structure for RF state switching support. - * This will only be compiled in when required. + * IEEE80211 control structure. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL -unsigned long rfkill_state; -#define RFKILL_STATE_ALLOCATED 1 -#define RFKILL_STATE_REGISTERED 2 - struct rfkill *rfkill; - struct input_polled_dev *poll_dev; -#endif /* CONFIG_RT2X00_LIB_RFKILL */ + struct ieee80211_hw *hw; + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; + enum ieee80211_band curr_band; + int curr_freq; /* * If enabled, the debugfs interface structures @@ -636,13 +762,37 @@ unsigned long rfkill_state; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ /* - * Device flags. - * In these flags the current status and some - * of the device capabilities are stored. + * LED structure for changing the LED status + * by mac8011 or the kernel. + */ +#ifdef CONFIG_RT2X00_LIB_LEDS + struct rt2x00_led led_radio; + struct rt2x00_led led_assoc; + struct rt2x00_led led_qual; + u16 led_mcu_reg; +#endif /* CONFIG_RT2X00_LIB_LEDS */ + + /* + * Device state flags. + * In these flags the current status is stored. + * Access to these flags should occur atomically. */ unsigned long flags; /* + * Device capabiltiy flags. + * In these flags the device/driver capabilities are stored. + * Access to these flags should occur non-atomically. + */ + unsigned long cap_flags; + + /* + * Device information, Bus IRQ and name (PCI, SoC) + */ + int irq; + const char *name; + + /* * Chipset identification. */ struct rt2x00_chip chip; @@ -654,30 +804,30 @@ unsigned long rfkill_state; /* * This is the default TX/RX antenna setup as indicated - * by the device's EEPROM. When mac80211 sets its - * antenna value to 0 we should be using these values. + * by the device's EEPROM. */ struct antenna_setup default_ant; /* * Register pointers - * csr_addr: Base register address. (PCI) - * csr_cache: CSR cache for usb_control_msg. (USB) + * csr.base: CSR base register address. (PCI) + * csr.cache: CSR cache for usb_control_msg. (USB) */ - void __iomem *csr_addr; - void *csr_cache; + union csr { + void __iomem *base; + void *cache; + } csr; /* - * Mutex to protect register accesses on USB devices. - * There are 2 reasons this is needed, one is to ensure - * use of the csr_cache (for USB devices) by one thread - * isn't corrupted by another thread trying to access it. - * The other is that access to BBP and RF registers - * require multiple BUS transactions and if another thread - * attempted to access one of those registers at the same - * time one of the writes could silently fail. + * Mutex to protect register accesses. + * For PCI and USB devices it protects against concurrent indirect + * register access (BBP, RF, MCU) since accessing those + * registers require multiple calls to the CSR registers. + * For USB devices it also protects the csr_cache since that + * field is used for normal CSR access and it cannot support + * multiple callers simultaneously. */ - struct mutex usb_cache_mutex; + struct mutex csr_mutex; /* * Current packet filter configuration for the device. @@ -687,9 +837,22 @@ unsigned long rfkill_state; unsigned int packet_filter; /* - * Interface configuration. + * Interface details: + * - Open ap interface count. + * - Open sta interface count. + * - Association count. + * - Beaconing enabled count. */ - struct interface interface; + unsigned int intf_ap_count; + unsigned int intf_sta_count; + unsigned int intf_associated; + unsigned int intf_beaconing; + + /* + * Interface combinations + */ + struct ieee80211_iface_limit if_limits_ap; + struct ieee80211_iface_combination if_combinations[NUM_IF_COMB]; /* * Link quality @@ -712,9 +875,9 @@ unsigned long rfkill_state; u32 *rf; /* - * USB Max frame size (for rt2500usb & rt73usb). + * LNA gain */ - u16 usb_maxpacket; + short lna_gain; /* * Current TX power value. @@ -722,14 +885,10 @@ unsigned long rfkill_state; u16 tx_power; /* - * LED register (for rt61pci & rt73usb). - */ - u16 led_reg; - - /* - * Led mode (LED_MODE_*) + * Current retry values. */ - u8 led_mode; + u8 short_retry; + u8 long_retry; /* * Rssi <-> Dbm offset @@ -737,74 +896,133 @@ unsigned long rfkill_state; u8 rssi_offset; /* - * Frequency offset (for rt61pci & rt73usb). + * Frequency offset. */ u8 freq_offset; /* + * Association id. + */ + u16 aid; + + /* + * Beacon interval. + */ + u16 beacon_int; + + /** + * Timestamp of last received beacon + */ + unsigned long last_beacon; + + /* * Low level statistics which will have * to be kept up to date while device is running. */ struct ieee80211_low_level_stats low_level_stats; - /* - * RX configuration information. + /** + * Work queue for all work which should not be placed + * on the mac80211 workqueue (because of dependencies + * between various work structures). */ - struct ieee80211_rx_status rx_status; + struct workqueue_struct *workqueue; /* * Scheduled work. + * NOTE: intf_work will use ieee80211_iterate_active_interfaces() + * which means it cannot be placed on the hw->workqueue + * due to RTNL locking requirements. */ - struct work_struct beacon_work; - struct work_struct filter_work; - struct work_struct config_work; + struct work_struct intf_work; + + /** + * Scheduled work for TX/RX done handling (USB devices) + */ + struct work_struct rxdone_work; + struct work_struct txdone_work; + + /* + * Powersaving work + */ + struct delayed_work autowakeup_work; + struct work_struct sleep_work; /* - * Data ring arrays for RX, TX and Beacon. - * The Beacon array also contains the Atim ring - * if that is supported by the device. + * Data queue arrays for RX, TX, Beacon and ATIM. */ - int data_rings; - struct data_ring *rx; - struct data_ring *tx; - struct data_ring *bcn; + unsigned int data_queues; + struct data_queue *rx; + struct data_queue *tx; + struct data_queue *bcn; + struct data_queue *atim; /* * Firmware image. */ const struct firmware *fw; -}; -/* - * For-each loop for the ring array. - * All rings have been allocated as a single array, - * this means we can create a very simply loop macro - * that is capable of looping through all rings. - * ring_end(), txring_end() and ring_loop() are helper macro's which - * should not be used directly. Instead the following should be used: - * ring_for_each() - Loops through all rings (RX, TX, Beacon & Atim) - * txring_for_each() - Loops through TX data rings (TX only) - * txringall_for_each() - Loops through all TX rings (TX, Beacon & Atim) - */ -#define ring_end(__dev) \ - &(__dev)->rx[(__dev)->data_rings] + /* + * FIFO for storing tx status reports between isr and tasklet. + */ + DECLARE_KFIFO_PTR(txstatus_fifo, u32); -#define txring_end(__dev) \ - &(__dev)->tx[(__dev)->hw->queues] + /* + * Timer to ensure tx status reports are read (rt2800usb). + */ + struct hrtimer txstatus_timer; -#define ring_loop(__entry, __start, __end) \ - for ((__entry) = (__start); \ - prefetch(&(__entry)[1]), (__entry) != (__end); \ - (__entry) = &(__entry)[1]) + /* + * Tasklet for processing tx status reports (rt2800pci). + */ + struct tasklet_struct txstatus_tasklet; + struct tasklet_struct pretbtt_tasklet; + struct tasklet_struct tbtt_tasklet; + struct tasklet_struct rxdone_tasklet; + struct tasklet_struct autowake_tasklet; -#define ring_for_each(__dev, __entry) \ - ring_loop(__entry, (__dev)->rx, ring_end(__dev)) + /* + * Used for VCO periodic calibration. + */ + int rf_channel; -#define txring_for_each(__dev, __entry) \ - ring_loop(__entry, (__dev)->tx, txring_end(__dev)) + /* + * Protect the interrupt mask register. + */ + spinlock_t irqmask_lock; -#define txringall_for_each(__dev, __entry) \ - ring_loop(__entry, (__dev)->tx, ring_end(__dev)) + /* + * List of BlockAckReq TX entries that need driver BlockAck processing. + */ + struct list_head bar_list; + spinlock_t bar_list_lock; + + /* Extra TX headroom required for alignment purposes. */ + unsigned int extra_tx_headroom; +}; + +struct rt2x00_bar_list_entry { + struct list_head list; + struct rcu_head head; + + struct queue_entry *entry; + int block_acked; + + /* Relevant parts of the IEEE80211 BAR header */ + __u8 ra[6]; + __u8 ta[6]; + __le16 control; + __le16 start_seq_num; +}; + +/* + * Register defines. + * Some registers require multiple attempts before success, + * in those cases REGISTER_BUSY_COUNT attempts should be + * taken with a REGISTER_BUSY_DELAY interval. + */ +#define REGISTER_BUSY_COUNT 100 +#define REGISTER_BUSY_DELAY 100 /* * Generic RF access. @@ -813,18 +1031,19 @@ unsigned long rfkill_state; static inline void rt2x00_rf_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u32 *data) { - *data = rt2x00dev->rf[word]; + BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); + *data = rt2x00dev->rf[word - 1]; } static inline void rt2x00_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, u32 data) { - rt2x00dev->rf[word] = data; + BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); + rt2x00dev->rf[word - 1] = data; } /* - * Generic EEPROM access. - * The EEPROM is being accessed by word index. + * Generic EEPROM access. The EEPROM is being accessed by word or byte index. */ static inline void *rt2x00_eeprom_addr(struct rt2x00_dev *rt2x00dev, const unsigned int word) @@ -844,107 +1063,399 @@ static inline void rt2x00_eeprom_write(struct rt2x00_dev *rt2x00dev, rt2x00dev->eeprom[word] = cpu_to_le16(data); } +static inline u8 rt2x00_eeprom_byte(struct rt2x00_dev *rt2x00dev, + const unsigned int byte) +{ + return *(((u8 *)rt2x00dev->eeprom) + byte); +} + /* * Chipset handlers */ static inline void rt2x00_set_chip(struct rt2x00_dev *rt2x00dev, - const u16 rt, const u16 rf, const u32 rev) + const u16 rt, const u16 rf, const u16 rev) { - INFO(rt2x00dev, - "Chipset detected - rt: %04x, rf: %04x, rev: %08x.\n", - rt, rf, rev); - rt2x00dev->chip.rt = rt; rt2x00dev->chip.rf = rf; rt2x00dev->chip.rev = rev; + + rt2x00_info(rt2x00dev, "Chipset detected - rt: %04x, rf: %04x, rev: %04x\n", + rt2x00dev->chip.rt, rt2x00dev->chip.rf, + rt2x00dev->chip.rev); +} + +static inline void rt2x00_set_rt(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rev) +{ + rt2x00dev->chip.rt = rt; + rt2x00dev->chip.rev = rev; + + rt2x00_info(rt2x00dev, "RT chipset %04x, rev %04x detected\n", + rt2x00dev->chip.rt, rt2x00dev->chip.rev); } -static inline char rt2x00_rt(const struct rt2x00_chip *chipset, const u16 chip) +static inline void rt2x00_set_rf(struct rt2x00_dev *rt2x00dev, const u16 rf) { - return (chipset->rt == chip); + rt2x00dev->chip.rf = rf; + + rt2x00_info(rt2x00dev, "RF chipset %04x detected\n", + rt2x00dev->chip.rf); } -static inline char rt2x00_rf(const struct rt2x00_chip *chipset, const u16 chip) +static inline bool rt2x00_rt(struct rt2x00_dev *rt2x00dev, const u16 rt) { - return (chipset->rf == chip); + return (rt2x00dev->chip.rt == rt); } -static inline u16 rt2x00_rev(const struct rt2x00_chip *chipset) +static inline bool rt2x00_rf(struct rt2x00_dev *rt2x00dev, const u16 rf) { - return chipset->rev; + return (rt2x00dev->chip.rf == rf); } -static inline u16 rt2x00_check_rev(const struct rt2x00_chip *chipset, - const u32 rev) +static inline u16 rt2x00_rev(struct rt2x00_dev *rt2x00dev) { - return (((chipset->rev & 0xffff0) == rev) && - !!(chipset->rev & 0x0000f)); + return rt2x00dev->chip.rev; } -/* - * Duration calculations - * The rate variable passed is: 100kbs. - * To convert from bytes to bits we multiply size with 8, - * then the size is multiplied with 10 to make the - * real rate -> rate argument correction. - */ -static inline u16 get_duration(const unsigned int size, const u8 rate) +static inline bool rt2x00_rt_rev(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rev) +{ + return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) == rev); +} + +static inline bool rt2x00_rt_rev_lt(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rev) +{ + return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) < rev); +} + +static inline bool rt2x00_rt_rev_gte(struct rt2x00_dev *rt2x00dev, + const u16 rt, const u16 rev) +{ + return (rt2x00_rt(rt2x00dev, rt) && rt2x00_rev(rt2x00dev) >= rev); +} + +static inline void rt2x00_set_chip_intf(struct rt2x00_dev *rt2x00dev, + enum rt2x00_chip_intf intf) +{ + rt2x00dev->chip.intf = intf; +} + +static inline bool rt2x00_intf(struct rt2x00_dev *rt2x00dev, + enum rt2x00_chip_intf intf) +{ + return (rt2x00dev->chip.intf == intf); +} + +static inline bool rt2x00_is_pci(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI) || + rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); +} + +static inline bool rt2x00_is_pcie(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); +} + +static inline bool rt2x00_is_usb(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); +} + +static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); +} + +/* Helpers for capability flags */ + +static inline bool +rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev, + enum rt2x00_capability_flags cap_flag) +{ + return test_bit(cap_flag, &rt2x00dev->cap_flags); +} + +static inline bool +rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO); +} + +static inline bool +rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT); +} + +static inline bool +rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS); +} + +static inline bool +rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL); +} + +static inline bool +rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT); +} + +static inline bool +rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING); +} + +static inline bool +rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE); +} + +static inline bool +rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE); +} + +static inline bool +rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A); +} + +static inline bool +rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev) { - return ((size * 8 * 10) / rate); + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG); } -static inline u16 get_duration_res(const unsigned int size, const u8 rate) +static inline bool +rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev) { - return ((size * 8 * 10) % rate); + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA); } +static inline bool +rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST); +} + +static inline bool +rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION); +} + +/** + * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes. + * @entry: Pointer to &struct queue_entry + * + * Returns -ENOMEM if mapping fail, 0 otherwise. + */ +int rt2x00queue_map_txskb(struct queue_entry *entry); + +/** + * rt2x00queue_unmap_skb - Unmap a skb from DMA. + * @entry: Pointer to &struct queue_entry + */ +void rt2x00queue_unmap_skb(struct queue_entry *entry); + +/** + * rt2x00queue_get_tx_queue - Convert tx queue index to queue pointer + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @queue: rt2x00 queue index (see &enum data_queue_qid). + * + * Returns NULL for non tx queues. + */ +static inline struct data_queue * +rt2x00queue_get_tx_queue(struct rt2x00_dev *rt2x00dev, + const enum data_queue_qid queue) +{ + if (queue < rt2x00dev->ops->tx_queues && rt2x00dev->tx) + return &rt2x00dev->tx[queue]; + + if (queue == QID_ATIM) + return rt2x00dev->atim; + + return NULL; +} + +/** + * rt2x00queue_get_entry - Get queue entry where the given index points to. + * @queue: Pointer to &struct data_queue from where we obtain the entry. + * @index: Index identifier for obtaining the correct index. + */ +struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue, + enum queue_index index); + +/** + * rt2x00queue_pause_queue - Pause a data queue + * @queue: Pointer to &struct data_queue. + * + * This function will pause the data queue locally, preventing + * new frames to be added to the queue (while the hardware is + * still allowed to run). + */ +void rt2x00queue_pause_queue(struct data_queue *queue); + +/** + * rt2x00queue_unpause_queue - unpause a data queue + * @queue: Pointer to &struct data_queue. + * + * This function will unpause the data queue locally, allowing + * new frames to be added to the queue again. + */ +void rt2x00queue_unpause_queue(struct data_queue *queue); + +/** + * rt2x00queue_start_queue - Start a data queue + * @queue: Pointer to &struct data_queue. + * + * This function will start handling all pending frames in the queue. + */ +void rt2x00queue_start_queue(struct data_queue *queue); + +/** + * rt2x00queue_stop_queue - Halt a data queue + * @queue: Pointer to &struct data_queue. + * + * This function will stop all pending frames in the queue. + */ +void rt2x00queue_stop_queue(struct data_queue *queue); + +/** + * rt2x00queue_flush_queue - Flush a data queue + * @queue: Pointer to &struct data_queue. + * @drop: True to drop all pending frames. + * + * This function will flush the queue. After this call + * the queue is guaranteed to be empty. + */ +void rt2x00queue_flush_queue(struct data_queue *queue, bool drop); + +/** + * rt2x00queue_start_queues - Start all data queues + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This function will loop through all available queues to start them + */ +void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00queue_stop_queues - Halt all data queues + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This function will loop through all available queues to stop + * any pending frames. + */ +void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00queue_flush_queues - Flush all data queues + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @drop: True to drop all pending frames. + * + * This function will loop through all available queues to flush + * any pending frames. + */ +void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop); + /* - * Library functions. + * Debugfs handlers. */ -struct data_ring *rt2x00lib_get_ring(struct rt2x00_dev *rt2x00dev, - const unsigned int queue); +/** + * rt2x00debug_dump_frame - Dump a frame to userspace through debugfs. + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @type: The type of frame that is being dumped. + * @skb: The skb containing the frame to be dumped. + */ +#ifdef CONFIG_RT2X00_LIB_DEBUGFS +void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, + enum rt2x00_dump_type type, struct sk_buff *skb); +#else +static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, + enum rt2x00_dump_type type, + struct sk_buff *skb) +{ +} +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ /* - * Interrupt context handlers. + * Utility functions. */ -void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev); -void rt2x00lib_txdone(struct data_entry *entry, - const int status, const int retry); -void rt2x00lib_rxdone(struct data_entry *entry, struct sk_buff *skb, - struct rxdata_entry_desc *desc); +u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif); /* - * TX descriptor initializer + * Interrupt context handlers. */ -void rt2x00lib_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct ieee80211_tx_control *control); +void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_dmastart(struct queue_entry *entry); +void rt2x00lib_dmadone(struct queue_entry *entry); +void rt2x00lib_txdone(struct queue_entry *entry, + struct txdone_entry_desc *txdesc); +void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status); +void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp); /* * mac80211 handlers. */ -int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_tx_control *control); +void rt2x00mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb); int rt2x00mac_start(struct ieee80211_hw *hw); void rt2x00mac_stop(struct ieee80211_hw *hw); int rt2x00mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf); + struct ieee80211_vif *vif); void rt2x00mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf); -int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf); -int rt2x00mac_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf); + struct ieee80211_vif *vif); +int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); +void rt2x00mac_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set); +#ifdef CONFIG_RT2X00_LIB_CRYPTO +int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +#else +#define rt2x00mac_set_key NULL +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ +int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw); +void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw); int rt2x00mac_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); -int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats); void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes); -int rt2x00mac_conf_tx(struct ieee80211_hw *hw, int queue, +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); +void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); +void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); +int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); +int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); +void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, + u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max); +bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw); /* * Driver allocation handlers. diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 07adc576db4..1122dc44c9f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -29,263 +27,259 @@ #include "rt2x00.h" #include "rt2x00lib.h" - -/* - * The MAC and BSSID addressess are simple array of bytes, - * these arrays are little endian, so when sending the addressess - * to the drivers, copy the it into a endian-signed variable. - * - * Note that all devices (except rt2500usb) have 32 bits - * register word sizes. This means that whatever variable we - * pass _must_ be a multiple of 32 bits. Otherwise the device - * might not accept what we are sending to it. - * This will also make it easier for the driver to write - * the data to the device. - * - * Also note that when NULL is passed as address the - * we will send 00:00:00:00:00 to the device to clear the address. - * This will prevent the device being confused when it wants - * to ACK frames or consideres itself associated. - */ -void rt2x00lib_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *mac) +void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + enum nl80211_iftype type, + const u8 *mac, const u8 *bssid) { - __le32 reg[2]; + struct rt2x00intf_conf conf; + unsigned int flags = 0; - memset(®, 0, sizeof(reg)); - if (mac) - memcpy(®, mac, ETH_ALEN); + conf.type = type; - rt2x00dev->ops->lib->config_mac_addr(rt2x00dev, ®[0]); -} + switch (type) { + case NL80211_IFTYPE_ADHOC: + conf.sync = TSF_SYNC_ADHOC; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_WDS: + conf.sync = TSF_SYNC_AP_NONE; + break; + case NL80211_IFTYPE_STATION: + conf.sync = TSF_SYNC_INFRA; + break; + default: + conf.sync = TSF_SYNC_NONE; + break; + } -void rt2x00lib_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) -{ - __le32 reg[2]; + /* + * Note that when NULL is passed as address we will send + * 00:00:00:00:00 to the device to clear the address. + * This will prevent the device being confused when it wants + * to ACK frames or considers itself associated. + */ + memset(conf.mac, 0, sizeof(conf.mac)); + if (mac) + memcpy(conf.mac, mac, ETH_ALEN); - memset(®, 0, sizeof(reg)); + memset(conf.bssid, 0, sizeof(conf.bssid)); if (bssid) - memcpy(®, bssid, ETH_ALEN); + memcpy(conf.bssid, bssid, ETH_ALEN); - rt2x00dev->ops->lib->config_bssid(rt2x00dev, ®[0]); + flags |= CONFIG_UPDATE_TYPE; + if (mac || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) + flags |= CONFIG_UPDATE_MAC; + if (bssid || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) + flags |= CONFIG_UPDATE_BSSID; + + rt2x00dev->ops->lib->config_intf(rt2x00dev, intf, &conf, flags); } -void rt2x00lib_config_type(struct rt2x00_dev *rt2x00dev, const int type) +void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct ieee80211_bss_conf *bss_conf, + u32 changed) { - int tsf_sync; + struct rt2x00lib_erp erp; - switch (type) { - case IEEE80211_IF_TYPE_IBSS: - case IEEE80211_IF_TYPE_AP: - tsf_sync = TSF_SYNC_BEACON; - break; - case IEEE80211_IF_TYPE_STA: - tsf_sync = TSF_SYNC_INFRA; - break; - default: - tsf_sync = TSF_SYNC_NONE; - break; - } + memset(&erp, 0, sizeof(erp)); + + erp.short_preamble = bss_conf->use_short_preamble; + erp.cts_protection = bss_conf->use_cts_prot; - rt2x00dev->ops->lib->config_type(rt2x00dev, type, tsf_sync); + erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME; + erp.sifs = SIFS; + erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS; + erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS; + erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS; + + erp.basic_rates = bss_conf->basic_rates; + erp.beacon_int = bss_conf->beacon_int; + + /* Update the AID, this is needed for dynamic PS support */ + rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0; + rt2x00dev->last_beacon = bss_conf->sync_tsf; + + /* Update global beacon interval time, this is needed for PS support */ + rt2x00dev->beacon_int = bss_conf->beacon_int; + + if (changed & BSS_CHANGED_HT) + erp.ht_opmode = bss_conf->ht_operation_mode; + + rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed); } void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, - enum antenna rx, enum antenna tx) + struct antenna_setup config) { - struct rt2x00lib_conf libconf; + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup *def = &rt2x00dev->default_ant; + struct antenna_setup *active = &rt2x00dev->link.ant.active; - libconf.ant.rx = rx; - libconf.ant.tx = tx; + /* + * When the caller tries to send the SW diversity, + * we must update the ANTENNA_RX_DIVERSITY flag to + * enable the antenna diversity in the link tuner. + * + * Secondly, we must guarentee we never send the + * software antenna diversity command to the driver. + */ + if (!(ant->flags & ANTENNA_RX_DIVERSITY)) { + if (config.rx == ANTENNA_SW_DIVERSITY) { + ant->flags |= ANTENNA_RX_DIVERSITY; + + if (def->rx == ANTENNA_SW_DIVERSITY) + config.rx = ANTENNA_B; + else + config.rx = def->rx; + } + } else if (config.rx == ANTENNA_SW_DIVERSITY) + config.rx = active->rx; + + if (!(ant->flags & ANTENNA_TX_DIVERSITY)) { + if (config.tx == ANTENNA_SW_DIVERSITY) { + ant->flags |= ANTENNA_TX_DIVERSITY; - if (rx == rt2x00dev->link.ant.active.rx && - tx == rt2x00dev->link.ant.active.tx) - return; + if (def->tx == ANTENNA_SW_DIVERSITY) + config.tx = ANTENNA_B; + else + config.tx = def->tx; + } + } else if (config.tx == ANTENNA_SW_DIVERSITY) + config.tx = active->tx; /* * Antenna setup changes require the RX to be disabled, * else the changes will be ignored by the device. */ - if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF_LINK); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2x00queue_stop_queue(rt2x00dev->rx); /* * Write new antenna setup to device and reset the link tuner. * The latter is required since we need to recalibrate the * noise-sensitivity ratio for the new setup. */ - rt2x00dev->ops->lib->config(rt2x00dev, CONFIG_UPDATE_ANTENNA, &libconf); - rt2x00lib_reset_link_tuner(rt2x00dev); + rt2x00dev->ops->lib->config_ant(rt2x00dev, &config); + + rt2x00link_reset_tuner(rt2x00dev, true); - rt2x00dev->link.ant.active.rx = libconf.ant.rx; - rt2x00dev->link.ant.active.tx = libconf.ant.tx; + memcpy(active, &config, sizeof(config)); - if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON_LINK); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2x00queue_start_queue(rt2x00dev->rx); } -void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, const int force_config) +static u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf) { - struct rt2x00lib_conf libconf; - struct ieee80211_hw_mode *mode; - struct ieee80211_rate *rate; - struct antenna_setup *default_ant = &rt2x00dev->default_ant; - struct antenna_setup *active_ant = &rt2x00dev->link.ant.active; - int flags = 0; - int short_slot_time; + struct hw_mode_spec *spec = &rt2x00dev->spec; + int center_channel; + u16 i; /* - * In some situations we want to force all configurations - * to be reloaded (When resuming for instance). + * Initialize center channel to current channel. */ - if (force_config) { - flags = CONFIG_UPDATE_ALL; - goto config; - } + center_channel = spec->channels[conf->chandef.chan->hw_value].channel; /* - * Check which configuration options have been - * updated and should be send to the device. + * Adjust center channel to HT40+ and HT40- operation. */ - if (rt2x00dev->rx_status.phymode != conf->phymode) - flags |= CONFIG_UPDATE_PHYMODE; - if (rt2x00dev->rx_status.channel != conf->channel) - flags |= CONFIG_UPDATE_CHANNEL; - if (rt2x00dev->tx_power != conf->power_level) - flags |= CONFIG_UPDATE_TXPOWER; + if (conf_is_ht40_plus(conf)) + center_channel += 2; + else if (conf_is_ht40_minus(conf)) + center_channel -= (center_channel == 14) ? 1 : 2; - /* - * Determining changes in the antenna setups request several checks: - * antenna_sel_{r,t}x = 0 - * -> Does active_{r,t}x match default_{r,t}x - * -> Is default_{r,t}x SW_DIVERSITY - * antenna_sel_{r,t}x = 1/2 - * -> Does active_{r,t}x match antenna_sel_{r,t}x - * The reason for not updating the antenna while SW diversity - * should be used is simple: Software diversity means that - * we should switch between the antenna's based on the - * quality. This means that the current antenna is good enough - * to work with untill the link tuner decides that an antenna - * switch should be performed. - */ - if (!conf->antenna_sel_rx && - default_ant->rx != ANTENNA_SW_DIVERSITY && - default_ant->rx != active_ant->rx) - flags |= CONFIG_UPDATE_ANTENNA; - else if (conf->antenna_sel_rx && - conf->antenna_sel_rx != active_ant->rx) - flags |= CONFIG_UPDATE_ANTENNA; - else if (active_ant->rx == ANTENNA_SW_DIVERSITY) - flags |= CONFIG_UPDATE_ANTENNA; - - if (!conf->antenna_sel_tx && - default_ant->tx != ANTENNA_SW_DIVERSITY && - default_ant->tx != active_ant->tx) - flags |= CONFIG_UPDATE_ANTENNA; - else if (conf->antenna_sel_tx && - conf->antenna_sel_tx != active_ant->tx) - flags |= CONFIG_UPDATE_ANTENNA; - else if (active_ant->tx == ANTENNA_SW_DIVERSITY) - flags |= CONFIG_UPDATE_ANTENNA; + for (i = 0; i < spec->num_channels; i++) + if (spec->channels[i].channel == center_channel) + return i; - /* - * The following configuration options are never - * stored anywhere and will always be updated. - */ - flags |= CONFIG_UPDATE_SLOT_TIME; - flags |= CONFIG_UPDATE_BEACON_INT; + WARN_ON(1); + return conf->chandef.chan->hw_value; +} - /* - * We have determined what options should be updated, - * now precalculate device configuration values depending - * on what configuration options need to be updated. - */ -config: - memset(&libconf, 0, sizeof(libconf)); +void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + unsigned int ieee80211_flags) +{ + struct rt2x00lib_conf libconf; + u16 hw_value; + u16 autowake_timeout; + u16 beacon_int; + u16 beacon_diff; - if (flags & CONFIG_UPDATE_PHYMODE) { - switch (conf->phymode) { - case MODE_IEEE80211A: - libconf.phymode = HWMODE_A; - break; - case MODE_IEEE80211B: - libconf.phymode = HWMODE_B; - break; - case MODE_IEEE80211G: - libconf.phymode = HWMODE_G; - break; - default: - ERROR(rt2x00dev, - "Attempt to configure unsupported mode (%d)" - "Defaulting to 802.11b", conf->phymode); - libconf.phymode = HWMODE_B; - } + memset(&libconf, 0, sizeof(libconf)); - mode = &rt2x00dev->hwmodes[libconf.phymode]; - rate = &mode->rates[mode->num_rates - 1]; + libconf.conf = conf; - libconf.basic_rates = - DEVICE_GET_RATE_FIELD(rate->val, RATEMASK) & DEV_BASIC_RATEMASK; - } + if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) { + if (!conf_is_ht(conf)) + set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); + else + clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); + + if (conf_is_ht40(conf)) { + set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); + hw_value = rt2x00ht_center_channel(rt2x00dev, conf); + } else { + clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); + hw_value = conf->chandef.chan->hw_value; + } - if (flags & CONFIG_UPDATE_CHANNEL) { memcpy(&libconf.rf, - &rt2x00dev->spec.channels[conf->channel_val], + &rt2x00dev->spec.channels[hw_value], sizeof(libconf.rf)); - } - if (flags & CONFIG_UPDATE_ANTENNA) { - if (conf->antenna_sel_rx) - libconf.ant.rx = conf->antenna_sel_rx; - else if (default_ant->rx != ANTENNA_SW_DIVERSITY) - libconf.ant.rx = default_ant->rx; - else if (active_ant->rx == ANTENNA_SW_DIVERSITY) - libconf.ant.rx = ANTENNA_B; - - if (conf->antenna_sel_tx) - libconf.ant.tx = conf->antenna_sel_tx; - else if (default_ant->tx != ANTENNA_SW_DIVERSITY) - libconf.ant.tx = default_ant->tx; - else if (active_ant->tx == ANTENNA_SW_DIVERSITY) - libconf.ant.tx = ANTENNA_B; - } - - if (flags & CONFIG_UPDATE_SLOT_TIME) { - short_slot_time = conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME; + memcpy(&libconf.channel, + &rt2x00dev->spec.channels_info[hw_value], + sizeof(libconf.channel)); - libconf.slot_time = - short_slot_time ? SHORT_SLOT_TIME : SLOT_TIME; - libconf.sifs = SIFS; - libconf.pifs = short_slot_time ? SHORT_PIFS : PIFS; - libconf.difs = short_slot_time ? SHORT_DIFS : DIFS; - libconf.eifs = EIFS; + /* Used for VCO periodic calibration */ + rt2x00dev->rf_channel = libconf.rf.channel; } - libconf.conf = conf; + if (test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && + (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) + cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); /* * Start configuration. */ - rt2x00dev->ops->lib->config(rt2x00dev, flags, &libconf); + rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags); /* * Some configuration changes affect the link quality * which means we need to reset the link tuner. */ - if (flags & (CONFIG_UPDATE_CHANNEL | CONFIG_UPDATE_ANTENNA)) - rt2x00lib_reset_link_tuner(rt2x00dev); - - if (flags & CONFIG_UPDATE_PHYMODE) { - rt2x00dev->curr_hwmode = libconf.phymode; - rt2x00dev->rx_status.phymode = conf->phymode; + if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt2x00link_reset_tuner(rt2x00dev, false); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && + (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) && + (conf->flags & IEEE80211_CONF_PS)) { + beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon; + beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int); + + if (beacon_diff > beacon_int) + beacon_diff = 0; + + autowake_timeout = (conf->max_sleep_period * beacon_int) - beacon_diff; + queue_delayed_work(rt2x00dev->workqueue, + &rt2x00dev->autowakeup_work, + autowake_timeout - 15); } - rt2x00dev->rx_status.freq = conf->freq; - rt2x00dev->rx_status.channel = conf->channel; - rt2x00dev->tx_power = conf->power_level; + if (conf->flags & IEEE80211_CONF_PS) + set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); + else + clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); - if (flags & CONFIG_UPDATE_ANTENNA) { - rt2x00dev->link.ant.active.rx = libconf.ant.rx; - rt2x00dev->link.ant.active.tx = libconf.ant.tx; - } + rt2x00dev->curr_band = conf->chandef.chan->band; + rt2x00dev->curr_freq = conf->chandef.chan->center_freq; + rt2x00dev->tx_power = conf->power_level; + rt2x00dev->short_retry = conf->short_frame_max_tx_count; + rt2x00dev->long_retry = conf->long_frame_max_tx_count; } diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c new file mode 100644 index 00000000000..a2fd05ba25c --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c @@ -0,0 +1,256 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 crypto specific routines. + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "rt2x00.h" +#include "rt2x00lib.h" + +enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) +{ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return CIPHER_WEP64; + case WLAN_CIPHER_SUITE_WEP104: + return CIPHER_WEP128; + case WLAN_CIPHER_SUITE_TKIP: + return CIPHER_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return CIPHER_AES; + default: + return CIPHER_NONE; + } +} + +void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; + + if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key) + return; + + __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); + + txdesc->cipher = rt2x00crypto_key_to_cipher(hw_key); + + if (hw_key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + __set_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags); + + txdesc->key_idx = hw_key->hw_key_idx; + txdesc->iv_offset = txdesc->header_length; + txdesc->iv_len = hw_key->iv_len; + + if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) + __set_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags); + + if (!(hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) + __set_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags); +} + +unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *key = tx_info->control.hw_key; + unsigned int overhead = 0; + + if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key) + return overhead; + + /* + * Extend frame length to include IV/EIV/ICV/MMIC, + * note that these lengths should only be added when + * mac80211 does not generate it. + */ + overhead += key->icv_len; + + if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) + overhead += key->iv_len; + + if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) { + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + overhead += 8; + } + + return overhead; +} + +void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + + if (unlikely(!txdesc->iv_len)) + return; + + /* Copy IV/EIV data */ + memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); +} + +void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + + if (unlikely(!txdesc->iv_len)) + return; + + /* Copy IV/EIV data */ + memcpy(skbdesc->iv, skb->data + txdesc->iv_offset, txdesc->iv_len); + + /* Move ieee80211 header */ + memmove(skb->data + txdesc->iv_len, skb->data, txdesc->iv_offset); + + /* Pull buffer to correct size */ + skb_pull(skb, txdesc->iv_len); + txdesc->length -= txdesc->iv_len; + + /* IV/EIV data has officially been stripped */ + skbdesc->flags |= SKBDESC_IV_STRIPPED; +} + +void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); + const unsigned int iv_len = + ((!!(skbdesc->iv[0])) * 4) + ((!!(skbdesc->iv[1])) * 4); + + if (!(skbdesc->flags & SKBDESC_IV_STRIPPED)) + return; + + skb_push(skb, iv_len); + + /* Move ieee80211 header */ + memmove(skb->data, skb->data + iv_len, header_length); + + /* Copy IV/EIV data */ + memcpy(skb->data + header_length, skbdesc->iv, iv_len); + + /* IV/EIV data has returned into the frame */ + skbdesc->flags &= ~SKBDESC_IV_STRIPPED; +} + +void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc) +{ + unsigned int payload_len = rxdesc->size - header_length; + unsigned int align = ALIGN_SIZE(skb, header_length); + unsigned int iv_len; + unsigned int icv_len; + unsigned int transfer = 0; + + /* + * WEP64/WEP128: Provides IV & ICV + * TKIP: Provides IV/EIV & ICV + * AES: Provies IV/EIV & ICV + */ + switch (rxdesc->cipher) { + case CIPHER_WEP64: + case CIPHER_WEP128: + iv_len = 4; + icv_len = 4; + break; + case CIPHER_TKIP: + iv_len = 8; + icv_len = 4; + break; + case CIPHER_AES: + iv_len = 8; + icv_len = 8; + break; + default: + /* Unsupport type */ + return; + } + + /* + * Make room for new data. There are 2 possibilities + * either the alignment is already present between + * the 802.11 header and payload. In that case we + * we have to move the header less then the iv_len + * since we can use the already available l2pad bytes + * for the iv data. + * When the alignment must be added manually we must + * move the header more then iv_len since we must + * make room for the payload move as well. + */ + if (rxdesc->dev_flags & RXDONE_L2PAD) { + skb_push(skb, iv_len - align); + skb_put(skb, icv_len); + + /* Move ieee80211 header */ + memmove(skb->data + transfer, + skb->data + transfer + (iv_len - align), + header_length); + transfer += header_length; + } else { + skb_push(skb, iv_len + align); + if (align < icv_len) + skb_put(skb, icv_len - align); + else if (align > icv_len) + skb_trim(skb, rxdesc->size + iv_len + icv_len); + + /* Move ieee80211 header */ + memmove(skb->data + transfer, + skb->data + transfer + iv_len + align, + header_length); + transfer += header_length; + } + + /* Copy IV/EIV data */ + memcpy(skb->data + transfer, rxdesc->iv, iv_len); + transfer += iv_len; + + /* + * Move payload for alignment purposes. Note that + * this is only needed when no l2 padding is present. + */ + if (!(rxdesc->dev_flags & RXDONE_L2PAD)) { + memmove(skb->data + transfer, + skb->data + transfer + align, + payload_len); + } + + /* + * NOTE: Always count the payload as transferred, + * even when alignment was set to zero. This is required + * for determining the correct offset for the ICV data. + */ + transfer += payload_len; + + /* + * Copy ICV data + * AES appends 8 bytes, we can't fill the upper + * 4 bytes, but mac80211 doesn't care about what + * we provide here anyway and strips it immediately. + */ + memcpy(skb->data + transfer, &rxdesc->icv, 4); + transfer += icv_len; + + /* IV/EIV/ICV has been inserted into frame */ + rxdesc->size = transfer; + rxdesc->flags &= ~RX_FLAG_IV_STRIPPED; +} diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index b44a9f4b9b7..90fdb02b55e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -27,13 +25,22 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/poll.h> +#include <linux/sched.h> +#include <linux/slab.h> #include <linux/uaccess.h> #include "rt2x00.h" #include "rt2x00lib.h" #include "rt2x00dump.h" -#define PRINT_LINE_LEN_MAX 32 +#define MAX_LINE_LENGTH 64 + +struct rt2x00debug_crypto { + unsigned long success; + unsigned long icv_error; + unsigned long mic_error; + unsigned long key_error; +}; struct rt2x00debug_intf { /* @@ -54,19 +61,24 @@ struct rt2x00debug_intf { * - driver folder * - driver file * - chipset file - * - device flags file + * - device state flags file + * - device capability flags file * - register folder * - csr offset/value files * - eeprom offset/value files * - bbp offset/value files * - rf offset/value files - * - frame dump folder + * - rfcsr offset/value files + * - queue folder * - frame dump file + * - queue stats file + * - crypto stats file */ struct dentry *driver_folder; struct dentry *driver_entry; struct dentry *chipset_entry; struct dentry *dev_flags; + struct dentry *cap_flags; struct dentry *register_folder; struct dentry *csr_off_entry; struct dentry *csr_val_entry; @@ -76,8 +88,12 @@ struct rt2x00debug_intf { struct dentry *bbp_val_entry; struct dentry *rf_off_entry; struct dentry *rf_val_entry; - struct dentry *frame_folder; - struct dentry *frame_dump_entry; + struct dentry *rfcsr_off_entry; + struct dentry *rfcsr_val_entry; + struct dentry *queue_folder; + struct dentry *queue_frame_dump_entry; + struct dentry *queue_stats_entry; + struct dentry *crypto_stats_entry; /* * The frame dump file only allows a single reader, @@ -96,6 +112,12 @@ struct rt2x00debug_intf { wait_queue_head_t frame_dump_waitqueue; /* + * HW crypto statistics. + * All statistics are stored separately per cipher type. + */ + struct rt2x00debug_crypto crypto_stats[CIPHER_MAX]; + + /* * Driver and chipset files will use a data buffer * that has been created in advance. This will simplify * the code since we can use the debugfs functions. @@ -110,50 +132,79 @@ struct rt2x00debug_intf { unsigned int offset_eeprom; unsigned int offset_bbp; unsigned int offset_rf; + unsigned int offset_rfcsr; }; +void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc) +{ + struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; + enum cipher cipher = rxdesc->cipher; + enum rx_crypto status = rxdesc->cipher_status; + + if (cipher == CIPHER_TKIP_NO_MIC) + cipher = CIPHER_TKIP; + if (cipher == CIPHER_NONE || cipher >= CIPHER_MAX) + return; + + /* Remove CIPHER_NONE index */ + cipher--; + + intf->crypto_stats[cipher].success += (status == RX_CRYPTO_SUCCESS); + intf->crypto_stats[cipher].icv_error += (status == RX_CRYPTO_FAIL_ICV); + intf->crypto_stats[cipher].mic_error += (status == RX_CRYPTO_FAIL_MIC); + intf->crypto_stats[cipher].key_error += (status == RX_CRYPTO_FAIL_KEY); +} + void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb) + enum rt2x00_dump_type type, struct sk_buff *skb) { struct rt2x00debug_intf *intf = rt2x00dev->debugfs_intf; - struct skb_desc *desc = get_skb_desc(skb); + struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb); struct sk_buff *skbcopy; struct rt2x00dump_hdr *dump_hdr; struct timeval timestamp; + u32 data_len; - do_gettimeofday(×tamp); - - if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) + if (likely(!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags))) return; + do_gettimeofday(×tamp); + if (skb_queue_len(&intf->frame_dump_skbqueue) > 20) { - DEBUG(rt2x00dev, "txrx dump queue length exceeded.\n"); + rt2x00_dbg(rt2x00dev, "txrx dump queue length exceeded\n"); return; } - skbcopy = alloc_skb(sizeof(*dump_hdr) + desc->desc_len + desc->data_len, + data_len = skb->len; + if (skbdesc->flags & SKBDESC_DESC_IN_SKB) + data_len -= skbdesc->desc_len; + + skbcopy = alloc_skb(sizeof(*dump_hdr) + skbdesc->desc_len + data_len, GFP_ATOMIC); if (!skbcopy) { - DEBUG(rt2x00dev, "Failed to copy skb for dump.\n"); + rt2x00_dbg(rt2x00dev, "Failed to copy skb for dump\n"); return; } dump_hdr = (struct rt2x00dump_hdr *)skb_put(skbcopy, sizeof(*dump_hdr)); dump_hdr->version = cpu_to_le32(DUMP_HEADER_VERSION); dump_hdr->header_length = cpu_to_le32(sizeof(*dump_hdr)); - dump_hdr->desc_length = cpu_to_le32(desc->desc_len); - dump_hdr->data_length = cpu_to_le32(desc->data_len); + dump_hdr->desc_length = cpu_to_le32(skbdesc->desc_len); + dump_hdr->data_length = cpu_to_le32(data_len); dump_hdr->chip_rt = cpu_to_le16(rt2x00dev->chip.rt); dump_hdr->chip_rf = cpu_to_le16(rt2x00dev->chip.rf); - dump_hdr->chip_rev = cpu_to_le32(rt2x00dev->chip.rev); - dump_hdr->type = cpu_to_le16(desc->frame_type); - dump_hdr->ring_index = desc->ring->queue_idx; - dump_hdr->entry_index = desc->entry->entry_idx; + dump_hdr->chip_rev = cpu_to_le16(rt2x00dev->chip.rev); + dump_hdr->type = cpu_to_le16(type); + dump_hdr->queue_index = skbdesc->entry->queue->qid; + dump_hdr->entry_index = skbdesc->entry->entry_idx; dump_hdr->timestamp_sec = cpu_to_le32(timestamp.tv_sec); dump_hdr->timestamp_usec = cpu_to_le32(timestamp.tv_usec); - memcpy(skb_put(skbcopy, desc->desc_len), desc->desc, desc->desc_len); - memcpy(skb_put(skbcopy, desc->data_len), desc->data, desc->data_len); + if (!(skbdesc->flags & SKBDESC_DESC_IN_SKB)) + memcpy(skb_put(skbcopy, skbdesc->desc_len), skbdesc->desc, + skbdesc->desc_len); + memcpy(skb_put(skbcopy, skb->len), skb->data, skb->len); skb_queue_tail(&intf->frame_dump_skbqueue, skbcopy); wake_up_interruptible(&intf->frame_dump_waitqueue); @@ -164,6 +215,7 @@ void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, if (!test_bit(FRAME_DUMP_FILE_OPEN, &intf->frame_dump_flags)) skb_queue_purge(&intf->frame_dump_skbqueue); } +EXPORT_SYMBOL_GPL(rt2x00debug_dump_frame); static int rt2x00debug_file_open(struct inode *inode, struct file *file) { @@ -186,7 +238,7 @@ static int rt2x00debug_file_release(struct inode *inode, struct file *file) return 0; } -static int rt2x00debug_open_ring_dump(struct inode *inode, struct file *file) +static int rt2x00debug_open_queue_dump(struct inode *inode, struct file *file) { struct rt2x00debug_intf *intf = inode->i_private; int retval; @@ -203,7 +255,7 @@ static int rt2x00debug_open_ring_dump(struct inode *inode, struct file *file) return 0; } -static int rt2x00debug_release_ring_dump(struct inode *inode, struct file *file) +static int rt2x00debug_release_queue_dump(struct inode *inode, struct file *file) { struct rt2x00debug_intf *intf = inode->i_private; @@ -214,10 +266,10 @@ static int rt2x00debug_release_ring_dump(struct inode *inode, struct file *file) return rt2x00debug_file_release(inode, file); } -static ssize_t rt2x00debug_read_ring_dump(struct file *file, - char __user *buf, - size_t length, - loff_t *offset) +static ssize_t rt2x00debug_read_queue_dump(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) { struct rt2x00debug_intf *intf = file->private_data; struct sk_buff *skb; @@ -234,7 +286,7 @@ static ssize_t rt2x00debug_read_ring_dump(struct file *file, if (retval) return retval; - status = min((size_t)skb->len, length); + status = min_t(size_t, skb->len, length); if (copy_to_user(buf, skb->data, status)) { status = -EFAULT; goto exit; @@ -248,8 +300,8 @@ exit: return status; } -static unsigned int rt2x00debug_poll_ring_dump(struct file *file, - poll_table *wait) +static unsigned int rt2x00debug_poll_queue_dump(struct file *file, + poll_table *wait) { struct rt2x00debug_intf *intf = file->private_data; @@ -261,13 +313,126 @@ static unsigned int rt2x00debug_poll_ring_dump(struct file *file, return 0; } -static const struct file_operations rt2x00debug_fop_ring_dump = { +static const struct file_operations rt2x00debug_fop_queue_dump = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_queue_dump, + .poll = rt2x00debug_poll_queue_dump, + .open = rt2x00debug_open_queue_dump, + .release = rt2x00debug_release_queue_dump, + .llseek = default_llseek, +}; + +static ssize_t rt2x00debug_read_queue_stats(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + struct data_queue *queue; + unsigned long irqflags; + unsigned int lines = 1 + intf->rt2x00dev->data_queues; + size_t size; + char *data; + char *temp; + + if (*offset) + return 0; + + data = kcalloc(lines, MAX_LINE_LENGTH, GFP_KERNEL); + if (!data) + return -ENOMEM; + + temp = data + + sprintf(data, "qid\tflags\t\tcount\tlimit\tlength\tindex\tdma done\tdone\n"); + + queue_for_each(intf->rt2x00dev, queue) { + spin_lock_irqsave(&queue->index_lock, irqflags); + + temp += sprintf(temp, "%d\t0x%.8x\t%d\t%d\t%d\t%d\t%d\t\t%d\n", + queue->qid, (unsigned int)queue->flags, + queue->count, queue->limit, queue->length, + queue->index[Q_INDEX], + queue->index[Q_INDEX_DMA_DONE], + queue->index[Q_INDEX_DONE]); + + spin_unlock_irqrestore(&queue->index_lock, irqflags); + } + + size = strlen(data); + size = min(size, length); + + if (copy_to_user(buf, data, size)) { + kfree(data); + return -EFAULT; + } + + kfree(data); + + *offset += size; + return size; +} + +static const struct file_operations rt2x00debug_fop_queue_stats = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_queue_stats, + .open = rt2x00debug_file_open, + .release = rt2x00debug_file_release, + .llseek = default_llseek, +}; + +#ifdef CONFIG_RT2X00_LIB_CRYPTO +static ssize_t rt2x00debug_read_crypto_stats(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + static const char * const name[] = { "WEP64", "WEP128", "TKIP", "AES" }; + char *data; + char *temp; + size_t size; + unsigned int i; + + if (*offset) + return 0; + + data = kzalloc((1 + CIPHER_MAX) * MAX_LINE_LENGTH, GFP_KERNEL); + if (!data) + return -ENOMEM; + + temp = data; + temp += sprintf(data, "cipher\tsuccess\ticv err\tmic err\tkey err\n"); + + for (i = 0; i < CIPHER_MAX; i++) { + temp += sprintf(temp, "%s\t%lu\t%lu\t%lu\t%lu\n", name[i], + intf->crypto_stats[i].success, + intf->crypto_stats[i].icv_error, + intf->crypto_stats[i].mic_error, + intf->crypto_stats[i].key_error); + } + + size = strlen(data); + size = min(size, length); + + if (copy_to_user(buf, data, size)) { + kfree(data); + return -EFAULT; + } + + kfree(data); + + *offset += size; + return size; +} + +static const struct file_operations rt2x00debug_fop_crypto_stats = { .owner = THIS_MODULE, - .read = rt2x00debug_read_ring_dump, - .poll = rt2x00debug_poll_ring_dump, - .open = rt2x00debug_open_ring_dump, - .release = rt2x00debug_release_ring_dump, + .read = rt2x00debug_read_crypto_stats, + .open = rt2x00debug_file_open, + .release = rt2x00debug_file_release, + .llseek = default_llseek, }; +#endif #define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \ static ssize_t rt2x00debug_read_##__name(struct file *file, \ @@ -279,16 +444,22 @@ static ssize_t rt2x00debug_read_##__name(struct file *file, \ const struct rt2x00debug *debug = intf->debug; \ char line[16]; \ size_t size; \ + unsigned int index = intf->offset_##__name; \ __type value; \ \ if (*offset) \ return 0; \ \ - if (intf->offset_##__name >= debug->__name.word_count) \ + if (index >= debug->__name.word_count) \ return -EINVAL; \ \ - debug->__name.read(intf->rt2x00dev, \ - intf->offset_##__name, &value); \ + index += (debug->__name.word_base / \ + debug->__name.word_size); \ + \ + if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \ + index *= debug->__name.word_size; \ + \ + debug->__name.read(intf->rt2x00dev, index, &value); \ \ size = sprintf(line, __format, value); \ \ @@ -309,15 +480,16 @@ static ssize_t rt2x00debug_write_##__name(struct file *file, \ const struct rt2x00debug *debug = intf->debug; \ char line[16]; \ size_t size; \ + unsigned int index = intf->offset_##__name; \ __type value; \ \ if (*offset) \ return 0; \ \ - if (!capable(CAP_NET_ADMIN)) \ - return -EPERM; \ + if (index >= debug->__name.word_count) \ + return -EINVAL; \ \ - if (intf->offset_##__name >= debug->__name.word_count) \ + if (length > sizeof(line)) \ return -EINVAL; \ \ if (copy_from_user(line, buf, length)) \ @@ -326,8 +498,13 @@ static ssize_t rt2x00debug_write_##__name(struct file *file, \ size = strlen(line); \ value = simple_strtoul(line, NULL, 0); \ \ - debug->__name.write(intf->rt2x00dev, \ - intf->offset_##__name, value); \ + index += (debug->__name.word_base / \ + debug->__name.word_size); \ + \ + if (debug->__name.flags & RT2X00DEBUGFS_OFFSET) \ + index *= debug->__name.word_size; \ + \ + debug->__name.write(intf->rt2x00dev, index, value); \ \ *offset += size; \ return size; \ @@ -343,12 +520,14 @@ static const struct file_operations rt2x00debug_fop_##__name = {\ .write = rt2x00debug_write_##__name, \ .open = rt2x00debug_file_open, \ .release = rt2x00debug_file_release, \ + .llseek = generic_file_llseek, \ }; RT2X00DEBUGFS_OPS(csr, "0x%.8x\n", u32); RT2X00DEBUGFS_OPS(eeprom, "0x%.4x\n", u16); RT2X00DEBUGFS_OPS(bbp, "0x%.2x\n", u8); RT2X00DEBUGFS_OPS(rf, "0x%.8x\n", u32); +RT2X00DEBUGFS_OPS(rfcsr, "0x%.2x\n", u8); static ssize_t rt2x00debug_read_dev_flags(struct file *file, char __user *buf, @@ -376,6 +555,36 @@ static const struct file_operations rt2x00debug_fop_dev_flags = { .read = rt2x00debug_read_dev_flags, .open = rt2x00debug_file_open, .release = rt2x00debug_file_release, + .llseek = default_llseek, +}; + +static ssize_t rt2x00debug_read_cap_flags(struct file *file, + char __user *buf, + size_t length, + loff_t *offset) +{ + struct rt2x00debug_intf *intf = file->private_data; + char line[16]; + size_t size; + + if (*offset) + return 0; + + size = sprintf(line, "0x%.8x\n", (unsigned int)intf->rt2x00dev->cap_flags); + + if (copy_to_user(buf, line, size)) + return -EFAULT; + + *offset += size; + return size; +} + +static const struct file_operations rt2x00debug_fop_cap_flags = { + .owner = THIS_MODULE, + .read = rt2x00debug_read_cap_flags, + .open = rt2x00debug_file_open, + .release = rt2x00debug_file_release, + .llseek = default_llseek, }; static struct dentry *rt2x00debug_create_file_driver(const char *name, @@ -386,17 +595,16 @@ static struct dentry *rt2x00debug_create_file_driver(const char *name, { char *data; - data = kzalloc(3 * PRINT_LINE_LEN_MAX, GFP_KERNEL); + data = kzalloc(3 * MAX_LINE_LENGTH, GFP_KERNEL); if (!data) return NULL; blob->data = data; - data += sprintf(data, "driver: %s\n", intf->rt2x00dev->ops->name); - data += sprintf(data, "version: %s\n", DRV_VERSION); - data += sprintf(data, "compiled: %s %s\n", __DATE__, __TIME__); + data += sprintf(data, "driver:\t%s\n", intf->rt2x00dev->ops->name); + data += sprintf(data, "version:\t%s\n", DRV_VERSION); blob->size = strlen(blob->data); - return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob); + return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); } static struct dentry *rt2x00debug_create_file_chipset(const char *name, @@ -409,22 +617,35 @@ static struct dentry *rt2x00debug_create_file_chipset(const char *name, const struct rt2x00debug *debug = intf->debug; char *data; - data = kzalloc(8 * PRINT_LINE_LEN_MAX, GFP_KERNEL); + data = kzalloc(9 * MAX_LINE_LENGTH, GFP_KERNEL); if (!data) return NULL; blob->data = data; - data += sprintf(data, "rt chip: %04x\n", intf->rt2x00dev->chip.rt); - data += sprintf(data, "rf chip: %04x\n", intf->rt2x00dev->chip.rf); - data += sprintf(data, "revision:%08x\n", intf->rt2x00dev->chip.rev); + data += sprintf(data, "rt chip:\t%04x\n", intf->rt2x00dev->chip.rt); + data += sprintf(data, "rf chip:\t%04x\n", intf->rt2x00dev->chip.rf); + data += sprintf(data, "revision:\t%04x\n", intf->rt2x00dev->chip.rev); data += sprintf(data, "\n"); - data += sprintf(data, "csr length: %d\n", debug->csr.word_count); - data += sprintf(data, "eeprom length: %d\n", debug->eeprom.word_count); - data += sprintf(data, "bbp length: %d\n", debug->bbp.word_count); - data += sprintf(data, "rf length: %d\n", debug->rf.word_count); + data += sprintf(data, "register\tbase\twords\twordsize\n"); +#define RT2X00DEBUGFS_SPRINTF_REGISTER(__name) \ +{ \ + if(debug->__name.read) \ + data += sprintf(data, __stringify(__name) \ + "\t%d\t%d\t%d\n", \ + debug->__name.word_base, \ + debug->__name.word_count, \ + debug->__name.word_size); \ +} + RT2X00DEBUGFS_SPRINTF_REGISTER(csr); + RT2X00DEBUGFS_SPRINTF_REGISTER(eeprom); + RT2X00DEBUGFS_SPRINTF_REGISTER(bbp); + RT2X00DEBUGFS_SPRINTF_REGISTER(rf); + RT2X00DEBUGFS_SPRINTF_REGISTER(rfcsr); +#undef RT2X00DEBUGFS_SPRINTF_REGISTER + blob->size = strlen(blob->data); - return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob); + return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); } void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) @@ -434,7 +655,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL); if (!intf) { - ERROR(rt2x00dev, "Failed to allocate debug handler.\n"); + rt2x00_err(rt2x00dev, "Failed to allocate debug handler\n"); return; } @@ -445,78 +666,99 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) intf->driver_folder = debugfs_create_dir(intf->rt2x00dev->ops->name, rt2x00dev->hw->wiphy->debugfsdir); - if (IS_ERR(intf->driver_folder)) + if (IS_ERR(intf->driver_folder) || !intf->driver_folder) goto exit; intf->driver_entry = rt2x00debug_create_file_driver("driver", intf, &intf->driver_blob); - if (IS_ERR(intf->driver_entry)) + if (IS_ERR(intf->driver_entry) || !intf->driver_entry) goto exit; intf->chipset_entry = rt2x00debug_create_file_chipset("chipset", intf, &intf->chipset_blob); - if (IS_ERR(intf->chipset_entry)) + if (IS_ERR(intf->chipset_entry) || !intf->chipset_entry) goto exit; - intf->dev_flags = debugfs_create_file("dev_flags", S_IRUGO, + intf->dev_flags = debugfs_create_file("dev_flags", S_IRUSR, intf->driver_folder, intf, &rt2x00debug_fop_dev_flags); - if (IS_ERR(intf->dev_flags)) + if (IS_ERR(intf->dev_flags) || !intf->dev_flags) + goto exit; + + intf->cap_flags = debugfs_create_file("cap_flags", S_IRUSR, + intf->driver_folder, intf, + &rt2x00debug_fop_cap_flags); + if (IS_ERR(intf->cap_flags) || !intf->cap_flags) goto exit; intf->register_folder = debugfs_create_dir("register", intf->driver_folder); - if (IS_ERR(intf->register_folder)) + if (IS_ERR(intf->register_folder) || !intf->register_folder) goto exit; -#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ -({ \ - (__intf)->__name##_off_entry = \ - debugfs_create_u32(__stringify(__name) "_offset", \ - S_IRUGO | S_IWUSR, \ - (__intf)->register_folder, \ - &(__intf)->offset_##__name); \ - if (IS_ERR((__intf)->__name##_off_entry)) \ - goto exit; \ - \ - (__intf)->__name##_val_entry = \ - debugfs_create_file(__stringify(__name) "_value", \ - S_IRUGO | S_IWUSR, \ - (__intf)->register_folder, \ - (__intf), &rt2x00debug_fop_##__name);\ - if (IS_ERR((__intf)->__name##_val_entry)) \ - goto exit; \ +#define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \ +({ \ + if(debug->__name.read) { \ + (__intf)->__name##_off_entry = \ + debugfs_create_u32(__stringify(__name) "_offset", \ + S_IRUSR | S_IWUSR, \ + (__intf)->register_folder, \ + &(__intf)->offset_##__name); \ + if (IS_ERR((__intf)->__name##_off_entry) \ + || !(__intf)->__name##_off_entry) \ + goto exit; \ + \ + (__intf)->__name##_val_entry = \ + debugfs_create_file(__stringify(__name) "_value", \ + S_IRUSR | S_IWUSR, \ + (__intf)->register_folder, \ + (__intf), &rt2x00debug_fop_##__name); \ + if (IS_ERR((__intf)->__name##_val_entry) \ + || !(__intf)->__name##_val_entry) \ + goto exit; \ + } \ }) RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, csr); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, eeprom); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, bbp); RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rf); + RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(intf, rfcsr); #undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY - intf->frame_folder = - debugfs_create_dir("frame", intf->driver_folder); - if (IS_ERR(intf->frame_folder)) + intf->queue_folder = + debugfs_create_dir("queue", intf->driver_folder); + if (IS_ERR(intf->queue_folder) || !intf->queue_folder) goto exit; - intf->frame_dump_entry = - debugfs_create_file("dump", S_IRUGO, intf->frame_folder, - intf, &rt2x00debug_fop_ring_dump); - if (IS_ERR(intf->frame_dump_entry)) + intf->queue_frame_dump_entry = + debugfs_create_file("dump", S_IRUSR, intf->queue_folder, + intf, &rt2x00debug_fop_queue_dump); + if (IS_ERR(intf->queue_frame_dump_entry) + || !intf->queue_frame_dump_entry) goto exit; skb_queue_head_init(&intf->frame_dump_skbqueue); init_waitqueue_head(&intf->frame_dump_waitqueue); + intf->queue_stats_entry = + debugfs_create_file("queue", S_IRUSR, intf->queue_folder, + intf, &rt2x00debug_fop_queue_stats); + +#ifdef CONFIG_RT2X00_LIB_CRYPTO + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) + intf->crypto_stats_entry = + debugfs_create_file("crypto", S_IRUGO, intf->queue_folder, + intf, &rt2x00debug_fop_crypto_stats); +#endif + return; exit: rt2x00debug_deregister(rt2x00dev); - ERROR(rt2x00dev, "Failed to register debug handler.\n"); - - return; + rt2x00_err(rt2x00dev, "Failed to register debug handler\n"); } void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) @@ -528,8 +770,14 @@ void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) skb_queue_purge(&intf->frame_dump_skbqueue); - debugfs_remove(intf->frame_dump_entry); - debugfs_remove(intf->frame_folder); +#ifdef CONFIG_RT2X00_LIB_CRYPTO + debugfs_remove(intf->crypto_stats_entry); +#endif + debugfs_remove(intf->queue_stats_entry); + debugfs_remove(intf->queue_frame_dump_entry); + debugfs_remove(intf->queue_folder); + debugfs_remove(intf->rfcsr_val_entry); + debugfs_remove(intf->rfcsr_off_entry); debugfs_remove(intf->rf_val_entry); debugfs_remove(intf->rf_off_entry); debugfs_remove(intf->bbp_val_entry); @@ -540,6 +788,7 @@ void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) debugfs_remove(intf->csr_off_entry); debugfs_remove(intf->register_folder); debugfs_remove(intf->dev_flags); + debugfs_remove(intf->cap_flags); debugfs_remove(intf->chipset_entry); debugfs_remove(intf->driver_entry); debugfs_remove(intf->driver_folder); diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/rt2x00/rt2x00debug.h index d37efbd09c4..e65712c235b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.h +++ b/drivers/net/wireless/rt2x00/rt2x00debug.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -28,6 +26,16 @@ struct rt2x00_dev; +/** + * enum rt2x00debugfs_entry_flags: Flags for debugfs registry entry + * + * @RT2X00DEBUGFS_OFFSET: rt2x00lib should pass the register offset + * as argument when using the callback function read()/write() + */ +enum rt2x00debugfs_entry_flags { + RT2X00DEBUGFS_OFFSET = (1 << 0), +}; + #define RT2X00DEBUGFS_REGISTER_ENTRY(__name, __type) \ struct reg##__name { \ void (*read)(struct rt2x00_dev *rt2x00dev, \ @@ -35,6 +43,9 @@ struct reg##__name { \ void (*write)(struct rt2x00_dev *rt2x00dev, \ const unsigned int word, __type data); \ \ + unsigned int flags; \ + \ + unsigned int word_base; \ unsigned int word_size; \ unsigned int word_count; \ } __name @@ -52,6 +63,7 @@ struct rt2x00debug { RT2X00DEBUGFS_REGISTER_ENTRY(eeprom, u16); RT2X00DEBUGFS_REGISTER_ENTRY(bbp, u8); RT2X00DEBUGFS_REGISTER_ENTRY(rf, u32); + RT2X00DEBUGFS_REGISTER_ENTRY(rfcsr, u8); }; #endif /* RT2X00DEBUG_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index e873a39fcce..4fa43a2eeb7 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1,5 +1,6 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> + Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +14,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -25,132 +24,27 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> +#include <linux/log2.h> #include "rt2x00.h" #include "rt2x00lib.h" -#include "rt2x00dump.h" /* - * Ring handler. + * Utility functions. */ -struct data_ring *rt2x00lib_get_ring(struct rt2x00_dev *rt2x00dev, - const unsigned int queue) +u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif) { - int beacon = test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags); - - /* - * Check if we are requesting a reqular TX ring, - * or if we are requesting a Beacon or Atim ring. - * For Atim rings, we should check if it is supported. - */ - if (queue < rt2x00dev->hw->queues && rt2x00dev->tx) - return &rt2x00dev->tx[queue]; - - if (!rt2x00dev->bcn || !beacon) - return NULL; - - if (queue == IEEE80211_TX_QUEUE_BEACON) - return &rt2x00dev->bcn[0]; - else if (queue == IEEE80211_TX_QUEUE_AFTER_BEACON) - return &rt2x00dev->bcn[1]; - - return NULL; -} -EXPORT_SYMBOL_GPL(rt2x00lib_get_ring); - -/* - * Link tuning handlers - */ -void rt2x00lib_reset_link_tuner(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) - return; - /* - * Reset link information. - * Both the currently active vgc level as well as - * the link tuner counter should be reset. Resetting - * the counter is important for devices where the - * device should only perform link tuning during the - * first minute after being enabled. + * When in STA mode, bssidx is always 0 otherwise local_address[5] + * contains the bss number, see BSS_ID_MASK comments for details. */ - rt2x00dev->link.count = 0; - rt2x00dev->link.vgc_level = 0; - - /* - * Reset the link tuner. - */ - rt2x00dev->ops->lib->reset_tuner(rt2x00dev); -} - -static void rt2x00lib_start_link_tuner(struct rt2x00_dev *rt2x00dev) -{ - /* - * Clear all (possibly) pre-existing quality statistics. - */ - memset(&rt2x00dev->link.qual, 0, sizeof(rt2x00dev->link.qual)); - - /* - * The RX and TX percentage should start at 50% - * this will assure we will get at least get some - * decent value when the link tuner starts. - * The value will be dropped and overwritten with - * the correct (measured )value anyway during the - * first run of the link tuner. - */ - rt2x00dev->link.qual.rx_percentage = 50; - rt2x00dev->link.qual.tx_percentage = 50; - - rt2x00lib_reset_link_tuner(rt2x00dev); - - queue_delayed_work(rt2x00dev->hw->workqueue, - &rt2x00dev->link.work, LINK_TUNE_INTERVAL); -} - -static void rt2x00lib_stop_link_tuner(struct rt2x00_dev *rt2x00dev) -{ - cancel_delayed_work_sync(&rt2x00dev->link.work); -} - -/* - * Ring initialization - */ -static void rt2x00lib_init_rxrings(struct rt2x00_dev *rt2x00dev) -{ - struct data_ring *ring = rt2x00dev->rx; - unsigned int i; - - if (!rt2x00dev->ops->lib->init_rxentry) - return; - - if (ring->data_addr) - memset(ring->data_addr, 0, rt2x00_get_ring_size(ring)); - - for (i = 0; i < ring->stats.limit; i++) - rt2x00dev->ops->lib->init_rxentry(rt2x00dev, &ring->entry[i]); - - rt2x00_ring_index_clear(ring); -} - -static void rt2x00lib_init_txrings(struct rt2x00_dev *rt2x00dev) -{ - struct data_ring *ring; - unsigned int i; - - if (!rt2x00dev->ops->lib->init_txentry) - return; - - txringall_for_each(rt2x00dev, ring) { - if (ring->data_addr) - memset(ring->data_addr, 0, rt2x00_get_ring_size(ring)); - - for (i = 0; i < ring->stats.limit; i++) - rt2x00dev->ops->lib->init_txentry(rt2x00dev, - &ring->entry[i]); - - rt2x00_ring_index_clear(ring); - } + if (rt2x00dev->intf_sta_count) + return 0; + return vif->addr[5] & (rt2x00dev->ops->max_ap_intf - 1); } +EXPORT_SYMBOL_GPL(rt2x00lib_get_bssidx); /* * Radio control handlers. @@ -163,768 +57,929 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) * Don't enable the radio twice. * And check if the hardware button has been disabled. */ - if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags) || - test_bit(DEVICE_DISABLED_RADIO_HW, &rt2x00dev->flags)) + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return 0; /* - * Initialize all data rings. + * Initialize all data queues. */ - rt2x00lib_init_rxrings(rt2x00dev); - rt2x00lib_init_txrings(rt2x00dev); + rt2x00queue_init_queues(rt2x00dev); /* * Enable radio. */ - status = rt2x00dev->ops->lib->set_device_state(rt2x00dev, - STATE_RADIO_ON); + status = + rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_ON); if (status) return status; - __set_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags); + rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_ON); + + rt2x00leds_led_radio(rt2x00dev, true); + rt2x00led_led_activity(rt2x00dev, true); + + set_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags); /* - * Enable RX. + * Enable queues. */ - rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); + rt2x00queue_start_queues(rt2x00dev); + rt2x00link_start_tuner(rt2x00dev); + rt2x00link_start_agc(rt2x00dev); + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) + rt2x00link_start_vcocal(rt2x00dev); /* - * Start the TX queues. + * Start watchdog monitoring. */ - ieee80211_start_queues(rt2x00dev->hw); + rt2x00link_start_watchdog(rt2x00dev); return 0; } void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) { - if (!__test_and_clear_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) + if (!test_and_clear_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return; /* - * Stop all scheduled work. + * Stop watchdog monitoring. */ - if (work_pending(&rt2x00dev->beacon_work)) - cancel_work_sync(&rt2x00dev->beacon_work); - if (work_pending(&rt2x00dev->filter_work)) - cancel_work_sync(&rt2x00dev->filter_work); - if (work_pending(&rt2x00dev->config_work)) - cancel_work_sync(&rt2x00dev->config_work); + rt2x00link_stop_watchdog(rt2x00dev); /* - * Stop the TX queues. + * Stop all queues */ - ieee80211_stop_queues(rt2x00dev->hw); - - /* - * Disable RX. - */ - rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); + rt2x00link_stop_agc(rt2x00dev); + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) + rt2x00link_stop_vcocal(rt2x00dev); + rt2x00link_stop_tuner(rt2x00dev); + rt2x00queue_stop_queues(rt2x00dev); + rt2x00queue_flush_queues(rt2x00dev, true); /* * Disable radio. */ rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_OFF); + rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF); + rt2x00led_led_activity(rt2x00dev, false); + rt2x00leds_led_radio(rt2x00dev, false); } -void rt2x00lib_toggle_rx(struct rt2x00_dev *rt2x00dev, enum dev_state state) +static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) { + struct rt2x00_dev *rt2x00dev = data; + struct rt2x00_intf *intf = vif_to_intf(vif); + /* - * When we are disabling the RX, we should also stop the link tuner. + * It is possible the radio was disabled while the work had been + * scheduled. If that happens we should return here immediately, + * note that in the spinlock protected area above the delayed_flags + * have been cleared correctly. */ - if (state == STATE_RADIO_RX_OFF) - rt2x00lib_stop_link_tuner(rt2x00dev); + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) + rt2x00queue_update_beacon(rt2x00dev, vif); +} - rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); +static void rt2x00lib_intf_scheduled(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, intf_work); /* - * When we are enabling the RX, we should also start the link tuner. + * Iterate over each interface and perform the + * requested configurations. */ - if (state == STATE_RADIO_RX_ON && - is_interface_present(&rt2x00dev->interface)) - rt2x00lib_start_link_tuner(rt2x00dev); + ieee80211_iterate_active_interfaces(rt2x00dev->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_intf_scheduled_iter, + rt2x00dev); } -static void rt2x00lib_evaluate_antenna_sample(struct rt2x00_dev *rt2x00dev) +static void rt2x00lib_autowakeup(struct work_struct *work) { - enum antenna rx = rt2x00dev->link.ant.active.rx; - enum antenna tx = rt2x00dev->link.ant.active.tx; - int sample_a = - rt2x00_get_link_ant_rssi_history(&rt2x00dev->link, ANTENNA_A); - int sample_b = - rt2x00_get_link_ant_rssi_history(&rt2x00dev->link, ANTENNA_B); + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, autowakeup_work.work); + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) + rt2x00_err(rt2x00dev, "Device failed to wakeup\n"); + clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); +} + +/* + * Interrupt context handlers. + */ +static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ieee80211_tx_control control = {}; + struct rt2x00_dev *rt2x00dev = data; + struct sk_buff *skb; /* - * We are done sampling. Now we should evaluate the results. + * Only AP mode interfaces do broad- and multicast buffering */ - rt2x00dev->link.ant.flags &= ~ANTENNA_MODE_SAMPLE; + if (vif->type != NL80211_IFTYPE_AP) + return; /* - * During the last period we have sampled the RSSI - * from both antenna's. It now is time to determine - * which antenna demonstrated the best performance. - * When we are already on the antenna with the best - * performance, then there really is nothing for us - * left to do. + * Send out buffered broad- and multicast frames */ - if (sample_a == sample_b) - return; + skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); + while (skb) { + rt2x00mac_tx(rt2x00dev->hw, &control, skb); + skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); + } +} - if (rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY) - rx = (sample_a > sample_b) ? ANTENNA_A : ANTENNA_B; +static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct rt2x00_dev *rt2x00dev = data; - if (rt2x00dev->link.ant.flags & ANTENNA_TX_DIVERSITY) - tx = (sample_a > sample_b) ? ANTENNA_A : ANTENNA_B; + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC && + vif->type != NL80211_IFTYPE_MESH_POINT && + vif->type != NL80211_IFTYPE_WDS) + return; - rt2x00lib_config_antenna(rt2x00dev, rx, tx); + /* + * Update the beacon without locking. This is safe on PCI devices + * as they only update the beacon periodically here. This should + * never be called for USB devices. + */ + WARN_ON(rt2x00_is_usb(rt2x00dev)); + rt2x00queue_update_beacon_locked(rt2x00dev, vif); } -static void rt2x00lib_evaluate_antenna_eval(struct rt2x00_dev *rt2x00dev) +void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) { - enum antenna rx = rt2x00dev->link.ant.active.rx; - enum antenna tx = rt2x00dev->link.ant.active.tx; - int rssi_curr = rt2x00_get_link_ant_rssi(&rt2x00dev->link); - int rssi_old = rt2x00_update_ant_rssi(&rt2x00dev->link, rssi_curr); + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + /* send buffered bc/mc frames out for every bssid */ + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_bc_buffer_iter, rt2x00dev); /* - * Legacy driver indicates that we should swap antenna's - * when the difference in RSSI is greater that 5. This - * also should be done when the RSSI was actually better - * then the previous sample. - * When the difference exceeds the threshold we should - * sample the rssi from the other antenna to make a valid - * comparison between the 2 antennas. + * Devices with pre tbtt interrupt don't need to update the beacon + * here as they will fetch the next beacon directly prior to + * transmission. */ - if (abs(rssi_curr - rssi_old) < 5) + if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev)) return; - rt2x00dev->link.ant.flags |= ANTENNA_MODE_SAMPLE; + /* fetch next beacon */ + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_beaconupdate_iter, rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); - if (rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY) - rx = (rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; +void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev) +{ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; - if (rt2x00dev->link.ant.flags & ANTENNA_TX_DIVERSITY) - tx = (tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; + /* fetch next beacon */ + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_beaconupdate_iter, rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); - rt2x00lib_config_antenna(rt2x00dev, rx, tx); +void rt2x00lib_dmastart(struct queue_entry *entry) +{ + set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); + rt2x00queue_index_inc(entry, Q_INDEX); } +EXPORT_SYMBOL_GPL(rt2x00lib_dmastart); -static void rt2x00lib_evaluate_antenna(struct rt2x00_dev *rt2x00dev) +void rt2x00lib_dmadone(struct queue_entry *entry) { - /* - * Determine if software diversity is enabled for - * either the TX or RX antenna (or both). - * Always perform this check since within the link - * tuner interval the configuration might have changed. - */ - rt2x00dev->link.ant.flags &= ~ANTENNA_RX_DIVERSITY; - rt2x00dev->link.ant.flags &= ~ANTENNA_TX_DIVERSITY; + set_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags); + clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); + rt2x00queue_index_inc(entry, Q_INDEX_DMA_DONE); +} +EXPORT_SYMBOL_GPL(rt2x00lib_dmadone); + +static inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_bar *bar = (void *) entry->skb->data; + struct rt2x00_bar_list_entry *bar_entry; + int ret; - if (rt2x00dev->hw->conf.antenna_sel_rx == 0 && - rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY) - rt2x00dev->link.ant.flags |= ANTENNA_RX_DIVERSITY; - if (rt2x00dev->hw->conf.antenna_sel_tx == 0 && - rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY) - rt2x00dev->link.ant.flags |= ANTENNA_TX_DIVERSITY; + if (likely(!ieee80211_is_back_req(bar->frame_control))) + return 0; - if (!(rt2x00dev->link.ant.flags & ANTENNA_RX_DIVERSITY) && - !(rt2x00dev->link.ant.flags & ANTENNA_TX_DIVERSITY)) { - rt2x00dev->link.ant.flags = 0; - return; + /* + * Unlike all other frames, the status report for BARs does + * not directly come from the hardware as it is incapable of + * matching a BA to a previously send BAR. The hardware will + * report all BARs as if they weren't acked at all. + * + * Instead the RX-path will scan for incoming BAs and set the + * block_acked flag if it sees one that was likely caused by + * a BAR from us. + * + * Remove remaining BARs here and return their status for + * TX done processing. + */ + ret = 0; + rcu_read_lock(); + list_for_each_entry_rcu(bar_entry, &rt2x00dev->bar_list, list) { + if (bar_entry->entry != entry) + continue; + + spin_lock_bh(&rt2x00dev->bar_list_lock); + /* Return whether this BAR was blockacked or not */ + ret = bar_entry->block_acked; + /* Remove the BAR from our checklist */ + list_del_rcu(&bar_entry->list); + spin_unlock_bh(&rt2x00dev->bar_list_lock); + kfree_rcu(bar_entry, head); + + break; } + rcu_read_unlock(); - /* - * If we have only sampled the data over the last period - * we should now harvest the data. Otherwise just evaluate - * the data. The latter should only be performed once - * every 2 seconds. - */ - if (rt2x00dev->link.ant.flags & ANTENNA_MODE_SAMPLE) - rt2x00lib_evaluate_antenna_sample(rt2x00dev); - else if (rt2x00dev->link.count & 1) - rt2x00lib_evaluate_antenna_eval(rt2x00dev); + return ret; } -static void rt2x00lib_update_link_stats(struct link *link, int rssi) +void rt2x00lib_txdone(struct queue_entry *entry, + struct txdone_entry_desc *txdesc) { - int avg_rssi = rssi; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + unsigned int header_length, i; + u8 rate_idx, rate_flags, retry_rates; + u8 skbdesc_flags = skbdesc->flags; + bool success; /* - * Update global RSSI + * Unmap the skb. */ - if (link->qual.avg_rssi) - avg_rssi = MOVING_AVERAGE(link->qual.avg_rssi, rssi, 8); - link->qual.avg_rssi = avg_rssi; + rt2x00queue_unmap_skb(entry); /* - * Update antenna RSSI + * Remove the extra tx headroom from the skb. */ - if (link->ant.rssi_ant) - rssi = MOVING_AVERAGE(link->ant.rssi_ant, rssi, 8); - link->ant.rssi_ant = rssi; -} + skb_pull(entry->skb, rt2x00dev->extra_tx_headroom); -static void rt2x00lib_precalculate_link_signal(struct link_qual *qual) -{ - if (qual->rx_failed || qual->rx_success) - qual->rx_percentage = - (qual->rx_success * 100) / - (qual->rx_failed + qual->rx_success); - else - qual->rx_percentage = 50; - - if (qual->tx_failed || qual->tx_success) - qual->tx_percentage = - (qual->tx_success * 100) / - (qual->tx_failed + qual->tx_success); - else - qual->tx_percentage = 50; - - qual->rx_success = 0; - qual->rx_failed = 0; - qual->tx_success = 0; - qual->tx_failed = 0; -} + /* + * Signal that the TX descriptor is no longer in the skb. + */ + skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; -static int rt2x00lib_calculate_link_signal(struct rt2x00_dev *rt2x00dev, - int rssi) -{ - int rssi_percentage = 0; - int signal; + /* + * Determine the length of 802.11 header. + */ + header_length = ieee80211_get_hdrlen_from_skb(entry->skb); /* - * We need a positive value for the RSSI. + * Remove L2 padding which was added during */ - if (rssi < 0) - rssi += rt2x00dev->rssi_offset; + if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags)) + rt2x00queue_remove_l2pad(entry->skb, header_length); /* - * Calculate the different percentages, - * which will be used for the signal. + * If the IV/EIV data was stripped from the frame before it was + * passed to the hardware, we should now reinsert it again because + * mac80211 will expect the same data to be present it the + * frame as it was passed to us. */ - if (rt2x00dev->rssi_offset) - rssi_percentage = (rssi * 100) / rt2x00dev->rssi_offset; + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) + rt2x00crypto_tx_insert_iv(entry->skb, header_length); /* - * Add the individual percentages and use the WEIGHT - * defines to calculate the current link signal. + * Send frame to debugfs immediately, after this call is completed + * we are going to overwrite the skb->cb array. */ - signal = ((WEIGHT_RSSI * rssi_percentage) + - (WEIGHT_TX * rt2x00dev->link.qual.tx_percentage) + - (WEIGHT_RX * rt2x00dev->link.qual.rx_percentage)) / 100; + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry->skb); - return (signal > 100) ? 100 : signal; -} + /* + * Determine if the frame has been successfully transmitted and + * remove BARs from our check list while checking for their + * TX status. + */ + success = + rt2x00lib_txdone_bar_status(entry) || + test_bit(TXDONE_SUCCESS, &txdesc->flags) || + test_bit(TXDONE_UNKNOWN, &txdesc->flags); -static void rt2x00lib_link_tuner(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, link.work.work); + /* + * Update TX statistics. + */ + rt2x00dev->link.qual.tx_success += success; + rt2x00dev->link.qual.tx_failed += !success; + + rate_idx = skbdesc->tx_rate_idx; + rate_flags = skbdesc->tx_rate_flags; + retry_rates = test_bit(TXDONE_FALLBACK, &txdesc->flags) ? + (txdesc->retry + 1) : 1; /* - * When the radio is shutting down we should - * immediately cease all link tuning. + * Initialize TX status */ - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) - return; + memset(&tx_info->status, 0, sizeof(tx_info->status)); + tx_info->status.ack_signal = 0; /* - * Update statistics. + * Frame was send with retries, hardware tried + * different rates to send out the frame, at each + * retry it lowered the rate 1 step except when the + * lowest rate was used. */ - rt2x00dev->ops->lib->link_stats(rt2x00dev, &rt2x00dev->link.qual); - rt2x00dev->low_level_stats.dot11FCSErrorCount += - rt2x00dev->link.qual.rx_failed; + for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) { + tx_info->status.rates[i].idx = rate_idx - i; + tx_info->status.rates[i].flags = rate_flags; + + if (rate_idx - i == 0) { + /* + * The lowest rate (index 0) was used until the + * number of max retries was reached. + */ + tx_info->status.rates[i].count = retry_rates - i; + i++; + break; + } + tx_info->status.rates[i].count = 1; + } + if (i < (IEEE80211_TX_MAX_RATES - 1)) + tx_info->status.rates[i].idx = -1; /* terminate */ + + if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { + if (success) + tx_info->flags |= IEEE80211_TX_STAT_ACK; + else + rt2x00dev->low_level_stats.dot11ACKFailureCount++; + } /* - * Only perform the link tuning when Link tuning - * has been enabled (This could have been disabled from the EEPROM). + * Every single frame has it's own tx status, hence report + * every frame as ampdu of size 1. + * + * TODO: if we can find out how many frames were aggregated + * by the hw we could provide the real ampdu_len to mac80211 + * which would allow the rc algorithm to better decide on + * which rates are suitable. */ - if (!test_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags)) - rt2x00dev->ops->lib->link_tuner(rt2x00dev); + if (test_bit(TXDONE_AMPDU, &txdesc->flags) || + tx_info->flags & IEEE80211_TX_CTL_AMPDU) { + tx_info->flags |= IEEE80211_TX_STAT_AMPDU; + tx_info->status.ampdu_len = 1; + tx_info->status.ampdu_ack_len = success ? 1 : 0; + + if (!success) + tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; + } + + if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) { + if (success) + rt2x00dev->low_level_stats.dot11RTSSuccessCount++; + else + rt2x00dev->low_level_stats.dot11RTSFailureCount++; + } /* - * Precalculate a portion of the link signal which is - * in based on the tx/rx success/failure counters. + * Only send the status report to mac80211 when it's a frame + * that originated in mac80211. If this was a extra frame coming + * through a mac80211 library call (RTS/CTS) then we should not + * send the status report back. */ - rt2x00lib_precalculate_link_signal(&rt2x00dev->link.qual); + if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { + if (test_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags)) + ieee80211_tx_status(rt2x00dev->hw, entry->skb); + else + ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); + } else + dev_kfree_skb_any(entry->skb); /* - * Evaluate antenna setup, make this the last step since this could - * possibly reset some statistics. + * Make this entry available for reuse. */ - rt2x00lib_evaluate_antenna(rt2x00dev); + entry->skb = NULL; + entry->flags = 0; + + rt2x00dev->ops->lib->clear_entry(entry); + + rt2x00queue_index_inc(entry, Q_INDEX_DONE); /* - * Increase tuner counter, and reschedule the next link tuner run. + * If the data queue was below the threshold before the txdone + * handler we must make sure the packet queue in the mac80211 stack + * is reenabled when the txdone handler has finished. This has to be + * serialized with rt2x00mac_tx(), otherwise we can wake up queue + * before it was stopped. */ - rt2x00dev->link.count++; - queue_delayed_work(rt2x00dev->hw->workqueue, &rt2x00dev->link.work, - LINK_TUNE_INTERVAL); + spin_lock_bh(&entry->queue->tx_lock); + if (!rt2x00queue_threshold(entry->queue)) + rt2x00queue_unpause_queue(entry->queue); + spin_unlock_bh(&entry->queue->tx_lock); } +EXPORT_SYMBOL_GPL(rt2x00lib_txdone); -static void rt2x00lib_packetfilter_scheduled(struct work_struct *work) +void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status) { - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, filter_work); - unsigned int filter = rt2x00dev->packet_filter; + struct txdone_entry_desc txdesc; - /* - * Since we had stored the filter inside interface.filter, - * we should now clear that field. Otherwise the driver will - * assume nothing has changed (*total_flags will be compared - * to interface.filter to determine if any action is required). - */ - rt2x00dev->packet_filter = 0; + txdesc.flags = 0; + __set_bit(status, &txdesc.flags); + txdesc.retry = 0; - rt2x00dev->ops->hw->configure_filter(rt2x00dev->hw, - filter, &filter, 0, NULL); + rt2x00lib_txdone(entry, &txdesc); } +EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo); -static void rt2x00lib_configuration_scheduled(struct work_struct *work) +static u8 *rt2x00lib_find_ie(u8 *data, unsigned int len, u8 ie) +{ + struct ieee80211_mgmt *mgmt = (void *)data; + u8 *pos, *end; + + pos = (u8 *)mgmt->u.beacon.variable; + end = data + len; + while (pos < end) { + if (pos + 2 + pos[1] > end) + return NULL; + + if (pos[0] == ie) + return pos; + + pos += 2 + pos[1]; + } + + return NULL; +} + +static void rt2x00lib_sleep(struct work_struct *work) { struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, config_work); - struct ieee80211_bss_conf bss_conf; + container_of(work, struct rt2x00_dev, sleep_work); - bss_conf.use_short_preamble = - test_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; /* - * FIXME: shouldn't invoke it this way because all other contents - * of bss_conf is invalid. + * Check again is powersaving is enabled, to prevent races from delayed + * work execution. */ - rt2x00mac_bss_info_changed(rt2x00dev->hw, rt2x00dev->interface.id, - &bss_conf, BSS_CHANGED_ERP_PREAMBLE); + if (!test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) + rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, + IEEE80211_CONF_CHANGE_PS); } -/* - * Interrupt context handlers. - */ -static void rt2x00lib_beacondone_scheduled(struct work_struct *work) +static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc) { - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, beacon_work); - struct data_ring *ring = - rt2x00lib_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); - struct data_entry *entry = rt2x00_get_data_entry(ring); - struct sk_buff *skb; + struct rt2x00_bar_list_entry *entry; + struct ieee80211_bar *ba = (void *)skb->data; - skb = ieee80211_beacon_get(rt2x00dev->hw, - rt2x00dev->interface.id, - &entry->tx_status.control); - if (!skb) + if (likely(!ieee80211_is_back(ba->frame_control))) + return; + + if (rxdesc->size < sizeof(*ba) + FCS_LEN) return; - rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, skb, - &entry->tx_status.control); + rcu_read_lock(); + list_for_each_entry_rcu(entry, &rt2x00dev->bar_list, list) { + + if (ba->start_seq_num != entry->start_seq_num) + continue; + +#define TID_CHECK(a, b) ( \ + ((a) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK)) == \ + ((b) & cpu_to_le16(IEEE80211_BAR_CTRL_TID_INFO_MASK))) \ + + if (!TID_CHECK(ba->control, entry->control)) + continue; + +#undef TID_CHECK + + if (!ether_addr_equal_64bits(ba->ra, entry->ta)) + continue; + + if (!ether_addr_equal_64bits(ba->ta, entry->ra)) + continue; + + /* Mark BAR since we received the according BA */ + spin_lock_bh(&rt2x00dev->bar_list_lock); + entry->block_acked = 1; + spin_unlock_bh(&rt2x00dev->bar_list_lock); + break; + } + rcu_read_unlock(); - dev_kfree_skb(skb); } -void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) +static void rt2x00lib_rxdone_check_ps(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc) { - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) + struct ieee80211_hdr *hdr = (void *) skb->data; + struct ieee80211_tim_ie *tim_ie; + u8 *tim; + u8 tim_len; + bool cam; + + /* If this is not a beacon, or if mac80211 has no powersaving + * configured, or if the device is already in powersaving mode + * we can exit now. */ + if (likely(!ieee80211_is_beacon(hdr->frame_control) || + !(rt2x00dev->hw->conf.flags & IEEE80211_CONF_PS))) return; - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->beacon_work); -} -EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); + /* min. beacon length + FCS_LEN */ + if (skb->len <= 40 + FCS_LEN) + return; -void rt2x00lib_txdone(struct data_entry *entry, - const int status, const int retry) -{ - struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; - struct ieee80211_tx_status *tx_status = &entry->tx_status; - struct ieee80211_low_level_stats *stats = &rt2x00dev->low_level_stats; - int success = !!(status == TX_SUCCESS || status == TX_SUCCESS_RETRY); - int fail = !!(status == TX_FAIL_RETRY || status == TX_FAIL_INVALID || - status == TX_FAIL_OTHER); + /* and only beacons from the associated BSSID, please */ + if (!(rxdesc->dev_flags & RXDONE_MY_BSS) || + !rt2x00dev->aid) + return; - /* - * Update TX statistics. - */ - tx_status->flags = 0; - tx_status->ack_signal = 0; - tx_status->excessive_retries = (status == TX_FAIL_RETRY); - tx_status->retry_count = retry; - rt2x00dev->link.qual.tx_success += success; - rt2x00dev->link.qual.tx_failed += retry + fail; + rt2x00dev->last_beacon = jiffies; - if (!(tx_status->control.flags & IEEE80211_TXCTL_NO_ACK)) { - if (success) - tx_status->flags |= IEEE80211_TX_STATUS_ACK; - else - stats->dot11ACKFailureCount++; - } + tim = rt2x00lib_find_ie(skb->data, skb->len - FCS_LEN, WLAN_EID_TIM); + if (!tim) + return; - tx_status->queue_length = entry->ring->stats.limit; - tx_status->queue_number = tx_status->control.queue; + if (tim[1] < sizeof(*tim_ie)) + return; - if (tx_status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) { - if (success) - stats->dot11RTSSuccessCount++; - else - stats->dot11RTSFailureCount++; - } + tim_len = tim[1]; + tim_ie = (struct ieee80211_tim_ie *) &tim[2]; - /* - * Send the tx_status to mac80211 & debugfs. - * mac80211 will clean up the skb structure. - */ - get_skb_desc(entry->skb)->frame_type = DUMP_FRAME_TXDONE; - rt2x00debug_dump_frame(rt2x00dev, entry->skb); - ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb, tx_status); - entry->skb = NULL; + /* Check whenever the PHY can be turned off again. */ + + /* 1. What about buffered unicast traffic for our AID? */ + cam = ieee80211_check_tim(tim_ie, tim_len, rt2x00dev->aid); + + /* 2. Maybe the AP wants to send multicast/broadcast data? */ + cam |= (tim_ie->bitmap_ctrl & 0x01); + + if (!cam && !test_bit(CONFIG_POWERSAVING, &rt2x00dev->flags)) + queue_work(rt2x00dev->workqueue, &rt2x00dev->sleep_work); } -EXPORT_SYMBOL_GPL(rt2x00lib_txdone); -void rt2x00lib_rxdone(struct data_entry *entry, struct sk_buff *skb, - struct rxdata_entry_desc *desc) +static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc) { - struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; - struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status; - struct ieee80211_hw_mode *mode; - struct ieee80211_rate *rate; - struct ieee80211_hdr *hdr; + struct ieee80211_supported_band *sband; + const struct rt2x00_rate *rate; unsigned int i; - int val = 0; - u16 fc; - - /* - * Update RX statistics. - */ - mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; - for (i = 0; i < mode->num_rates; i++) { - rate = &mode->rates[i]; + int signal = rxdesc->signal; + int type = (rxdesc->dev_flags & RXDONE_SIGNAL_MASK); + switch (rxdesc->rate_mode) { + case RATE_MODE_CCK: + case RATE_MODE_OFDM: /* - * When frame was received with an OFDM bitrate, - * the signal is the PLCP value. If it was received with - * a CCK bitrate the signal is the rate in 0.5kbit/s. + * For non-HT rates the MCS value needs to contain the + * actually used rate modulation (CCK or OFDM). */ - if (!desc->ofdm) - val = DEVICE_GET_RATE_FIELD(rate->val, RATE); - else - val = DEVICE_GET_RATE_FIELD(rate->val, PLCP); - - if (val == desc->signal) { - val = rate->val; - break; + if (rxdesc->dev_flags & RXDONE_SIGNAL_MCS) + signal = RATE_MCS(rxdesc->rate_mode, signal); + + sband = &rt2x00dev->bands[rt2x00dev->curr_band]; + for (i = 0; i < sband->n_bitrates; i++) { + rate = rt2x00_get_rate(sband->bitrates[i].hw_value); + if (((type == RXDONE_SIGNAL_PLCP) && + (rate->plcp == signal)) || + ((type == RXDONE_SIGNAL_BITRATE) && + (rate->bitrate == signal)) || + ((type == RXDONE_SIGNAL_MCS) && + (rate->mcs == signal))) { + return i; + } } + break; + case RATE_MODE_HT_MIX: + case RATE_MODE_HT_GREENFIELD: + if (signal >= 0 && signal <= 76) + return signal; + break; + default: + break; } - /* - * Only update link status if this is a beacon frame carrying our bssid. - */ - hdr = (struct ieee80211_hdr*)skb->data; - fc = le16_to_cpu(hdr->frame_control); - if (is_beacon(fc) && desc->my_bss) - rt2x00lib_update_link_stats(&rt2x00dev->link, desc->rssi); - - rt2x00dev->link.qual.rx_success++; - - rx_status->rate = val; - rx_status->signal = - rt2x00lib_calculate_link_signal(rt2x00dev, desc->rssi); - rx_status->ssi = desc->rssi; - rx_status->flag = desc->flags; - rx_status->antenna = rt2x00dev->link.ant.active.rx; - - /* - * Send frame to mac80211 & debugfs - */ - get_skb_desc(skb)->frame_type = DUMP_FRAME_RXDONE; - rt2x00debug_dump_frame(rt2x00dev, skb); - ieee80211_rx_irqsafe(rt2x00dev->hw, skb, rx_status); + rt2x00_warn(rt2x00dev, "Frame received with unrecognized signal, mode=0x%.4x, signal=0x%.4x, type=%d\n", + rxdesc->rate_mode, signal, type); + return 0; } -EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); -/* - * TX descriptor initializer - */ -void rt2x00lib_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct ieee80211_tx_control *control) +void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) { - struct txdata_entry_desc desc; - struct skb_desc *skbdesc = get_skb_desc(skb); - struct ieee80211_hdr *ieee80211hdr = skbdesc->data; - int tx_rate; - int bitrate; - int length; - int duration; - int residual; - u16 frame_control; - u16 seq_ctrl; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct rxdone_entry_desc rxdesc; + struct sk_buff *skb; + struct ieee80211_rx_status *rx_status; + unsigned int header_length; + int rate_idx; - memset(&desc, 0, sizeof(desc)); + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || + !test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + goto submit_entry; - desc.cw_min = skbdesc->ring->tx_params.cw_min; - desc.cw_max = skbdesc->ring->tx_params.cw_max; - desc.aifs = skbdesc->ring->tx_params.aifs; + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + goto submit_entry; /* - * Identify queue + * Allocate a new sk_buffer. If no new buffer available, drop the + * received frame and reuse the existing buffer. */ - if (control->queue < rt2x00dev->hw->queues) - desc.queue = control->queue; - else if (control->queue == IEEE80211_TX_QUEUE_BEACON || - control->queue == IEEE80211_TX_QUEUE_AFTER_BEACON) - desc.queue = QUEUE_MGMT; - else - desc.queue = QUEUE_OTHER; + skb = rt2x00queue_alloc_rxskb(entry, gfp); + if (!skb) + goto submit_entry; /* - * Read required fields from ieee80211 header. + * Unmap the skb. */ - frame_control = le16_to_cpu(ieee80211hdr->frame_control); - seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); - - tx_rate = control->tx_rate; + rt2x00queue_unmap_skb(entry); /* - * Check whether this frame is to be acked + * Extract the RXD details. */ - if (!(control->flags & IEEE80211_TXCTL_NO_ACK)) - __set_bit(ENTRY_TXD_ACK, &desc.flags); + memset(&rxdesc, 0, sizeof(rxdesc)); + rt2x00dev->ops->lib->fill_rxdone(entry, &rxdesc); /* - * Check if this is a RTS/CTS frame + * Check for valid size in case we get corrupted descriptor from + * hardware. */ - if (is_rts_frame(frame_control) || is_cts_frame(frame_control)) { - __set_bit(ENTRY_TXD_BURST, &desc.flags); - if (is_rts_frame(frame_control)) { - __set_bit(ENTRY_TXD_RTS_FRAME, &desc.flags); - __set_bit(ENTRY_TXD_ACK, &desc.flags); - } else - __clear_bit(ENTRY_TXD_ACK, &desc.flags); - if (control->rts_cts_rate) - tx_rate = control->rts_cts_rate; + if (unlikely(rxdesc.size == 0 || + rxdesc.size > entry->queue->data_size)) { + rt2x00_err(rt2x00dev, "Wrong frame size %d max %d\n", + rxdesc.size, entry->queue->data_size); + dev_kfree_skb(entry->skb); + goto renew_skb; } /* - * Check for OFDM + * The data behind the ieee80211 header must be + * aligned on a 4 byte boundary. */ - if (DEVICE_GET_RATE_FIELD(tx_rate, RATEMASK) & DEV_OFDM_RATEMASK) - __set_bit(ENTRY_TXD_OFDM_RATE, &desc.flags); + header_length = ieee80211_get_hdrlen_from_skb(entry->skb); /* - * Check if more fragments are pending + * Hardware might have stripped the IV/EIV/ICV data, + * in that case it is possible that the data was + * provided separately (through hardware descriptor) + * in which case we should reinsert the data into the frame. */ - if (ieee80211_get_morefrag(ieee80211hdr)) { - __set_bit(ENTRY_TXD_BURST, &desc.flags); - __set_bit(ENTRY_TXD_MORE_FRAG, &desc.flags); - } + if ((rxdesc.dev_flags & RXDONE_CRYPTO_IV) && + (rxdesc.flags & RX_FLAG_IV_STRIPPED)) + rt2x00crypto_rx_insert_iv(entry->skb, header_length, + &rxdesc); + else if (header_length && + (rxdesc.size > header_length) && + (rxdesc.dev_flags & RXDONE_L2PAD)) + rt2x00queue_remove_l2pad(entry->skb, header_length); + + /* Trim buffer to correct size */ + skb_trim(entry->skb, rxdesc.size); /* - * Beacons and probe responses require the tsf timestamp - * to be inserted into the frame. + * Translate the signal to the correct bitrate index. */ - if (control->queue == IEEE80211_TX_QUEUE_BEACON || - is_probe_resp(frame_control)) - __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &desc.flags); + rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); + if (rxdesc.rate_mode == RATE_MODE_HT_MIX || + rxdesc.rate_mode == RATE_MODE_HT_GREENFIELD) + rxdesc.flags |= RX_FLAG_HT; /* - * Determine with what IFS priority this frame should be send. - * Set ifs to IFS_SIFS when the this is not the first fragment, - * or this fragment came after RTS/CTS. + * Check if this is a beacon, and more frames have been + * buffered while we were in powersaving mode. */ - if ((seq_ctrl & IEEE80211_SCTL_FRAG) > 0 || - test_bit(ENTRY_TXD_RTS_FRAME, &desc.flags)) - desc.ifs = IFS_SIFS; - else - desc.ifs = IFS_BACKOFF; + rt2x00lib_rxdone_check_ps(rt2x00dev, entry->skb, &rxdesc); /* - * PLCP setup - * Length calculation depends on OFDM/CCK rate. + * Check for incoming BlockAcks to match to the BlockAckReqs + * we've send out. */ - desc.signal = DEVICE_GET_RATE_FIELD(tx_rate, PLCP); - desc.service = 0x04; - - length = skbdesc->data_len + FCS_LEN; - if (test_bit(ENTRY_TXD_OFDM_RATE, &desc.flags)) { - desc.length_high = (length >> 6) & 0x3f; - desc.length_low = length & 0x3f; - } else { - bitrate = DEVICE_GET_RATE_FIELD(tx_rate, RATE); - - /* - * Convert length to microseconds. - */ - residual = get_duration_res(length, bitrate); - duration = get_duration(length, bitrate); + rt2x00lib_rxdone_check_ba(rt2x00dev, entry->skb, &rxdesc); - if (residual != 0) { - duration++; + /* + * Update extra components + */ + rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc); + rt2x00debug_update_crypto(rt2x00dev, &rxdesc); + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb); - /* - * Check if we need to set the Length Extension - */ - if (bitrate == 110 && residual <= 30) - desc.service |= 0x80; - } + /* + * Initialize RX status information, and send frame + * to mac80211. + */ + rx_status = IEEE80211_SKB_RXCB(entry->skb); - desc.length_high = (duration >> 8) & 0xff; - desc.length_low = duration & 0xff; + /* Ensure that all fields of rx_status are initialized + * properly. The skb->cb array was used for driver + * specific informations, so rx_status might contain + * garbage. + */ + memset(rx_status, 0, sizeof(*rx_status)); - /* - * When preamble is enabled we should set the - * preamble bit for the signal. - */ - if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) - desc.signal |= 0x08; - } + rx_status->mactime = rxdesc.timestamp; + rx_status->band = rt2x00dev->curr_band; + rx_status->freq = rt2x00dev->curr_freq; + rx_status->rate_idx = rate_idx; + rx_status->signal = rxdesc.rssi; + rx_status->flag = rxdesc.flags; + rx_status->antenna = rt2x00dev->link.ant.active.rx; - rt2x00dev->ops->lib->write_tx_desc(rt2x00dev, skb, &desc, control); + ieee80211_rx_ni(rt2x00dev->hw, entry->skb); +renew_skb: /* - * Update ring entry. + * Replace the skb with the freshly allocated one. */ - skbdesc->entry->skb = skb; - memcpy(&skbdesc->entry->tx_status.control, control, sizeof(*control)); + entry->skb = skb; - /* - * The frame has been completely initialized and ready - * for sending to the device. The caller will push the - * frame to the device, but we are going to push the - * frame to debugfs here. - */ - skbdesc->frame_type = DUMP_FRAME_TX; - rt2x00debug_dump_frame(rt2x00dev, skb); +submit_entry: + entry->flags = 0; + rt2x00queue_index_inc(entry, Q_INDEX_DONE); + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2x00dev->ops->lib->clear_entry(entry); } -EXPORT_SYMBOL_GPL(rt2x00lib_write_tx_desc); +EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); /* * Driver initialization handlers. */ +const struct rt2x00_rate rt2x00_supported_rates[12] = { + { + .flags = DEV_RATE_CCK, + .bitrate = 10, + .ratemask = BIT(0), + .plcp = 0x00, + .mcs = RATE_MCS(RATE_MODE_CCK, 0), + }, + { + .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, + .bitrate = 20, + .ratemask = BIT(1), + .plcp = 0x01, + .mcs = RATE_MCS(RATE_MODE_CCK, 1), + }, + { + .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, + .bitrate = 55, + .ratemask = BIT(2), + .plcp = 0x02, + .mcs = RATE_MCS(RATE_MODE_CCK, 2), + }, + { + .flags = DEV_RATE_CCK | DEV_RATE_SHORT_PREAMBLE, + .bitrate = 110, + .ratemask = BIT(3), + .plcp = 0x03, + .mcs = RATE_MCS(RATE_MODE_CCK, 3), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 60, + .ratemask = BIT(4), + .plcp = 0x0b, + .mcs = RATE_MCS(RATE_MODE_OFDM, 0), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 90, + .ratemask = BIT(5), + .plcp = 0x0f, + .mcs = RATE_MCS(RATE_MODE_OFDM, 1), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 120, + .ratemask = BIT(6), + .plcp = 0x0a, + .mcs = RATE_MCS(RATE_MODE_OFDM, 2), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 180, + .ratemask = BIT(7), + .plcp = 0x0e, + .mcs = RATE_MCS(RATE_MODE_OFDM, 3), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 240, + .ratemask = BIT(8), + .plcp = 0x09, + .mcs = RATE_MCS(RATE_MODE_OFDM, 4), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 360, + .ratemask = BIT(9), + .plcp = 0x0d, + .mcs = RATE_MCS(RATE_MODE_OFDM, 5), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 480, + .ratemask = BIT(10), + .plcp = 0x08, + .mcs = RATE_MCS(RATE_MODE_OFDM, 6), + }, + { + .flags = DEV_RATE_OFDM, + .bitrate = 540, + .ratemask = BIT(11), + .plcp = 0x0c, + .mcs = RATE_MCS(RATE_MODE_OFDM, 7), + }, +}; + static void rt2x00lib_channel(struct ieee80211_channel *entry, const int channel, const int tx_power, const int value) { - entry->chan = channel; - if (channel <= 14) - entry->freq = 2407 + (5 * channel); - else - entry->freq = 5000 + (5 * channel); - entry->val = value; - entry->flag = - IEEE80211_CHAN_W_IBSS | - IEEE80211_CHAN_W_ACTIVE_SCAN | - IEEE80211_CHAN_W_SCAN; - entry->power_level = tx_power; - entry->antenna_max = 0xff; + /* XXX: this assumption about the band is wrong for 802.11j */ + entry->band = channel <= 14 ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + entry->center_freq = ieee80211_channel_to_frequency(channel, + entry->band); + entry->hw_value = value; + entry->max_power = tx_power; + entry->max_antenna_gain = 0xff; } static void rt2x00lib_rate(struct ieee80211_rate *entry, - const int rate, const int mask, - const int plcp, const int flags) + const u16 index, const struct rt2x00_rate *rate) { - entry->rate = rate; - entry->val = - DEVICE_SET_RATE_FIELD(rate, RATE) | - DEVICE_SET_RATE_FIELD(mask, RATEMASK) | - DEVICE_SET_RATE_FIELD(plcp, PLCP); - entry->flags = flags; - entry->val2 = entry->val; - if (entry->flags & IEEE80211_RATE_PREAMBLE2) - entry->val2 |= DEVICE_SET_RATE_FIELD(1, PREAMBLE); - entry->min_rssi_ack = 0; - entry->min_rssi_ack_delta = 0; + entry->flags = 0; + entry->bitrate = rate->bitrate; + entry->hw_value = index; + entry->hw_value_short = index; + + if (rate->flags & DEV_RATE_SHORT_PREAMBLE) + entry->flags |= IEEE80211_RATE_SHORT_PREAMBLE; } static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, struct hw_mode_spec *spec) { struct ieee80211_hw *hw = rt2x00dev->hw; - struct ieee80211_hw_mode *hwmodes; struct ieee80211_channel *channels; struct ieee80211_rate *rates; + unsigned int num_rates; unsigned int i; - unsigned char tx_power; - hwmodes = kzalloc(sizeof(*hwmodes) * spec->num_modes, GFP_KERNEL); - if (!hwmodes) - goto exit; + num_rates = 0; + if (spec->supported_rates & SUPPORT_RATE_CCK) + num_rates += 4; + if (spec->supported_rates & SUPPORT_RATE_OFDM) + num_rates += 8; - channels = kzalloc(sizeof(*channels) * spec->num_channels, GFP_KERNEL); + channels = kcalloc(spec->num_channels, sizeof(*channels), GFP_KERNEL); if (!channels) - goto exit_free_modes; + return -ENOMEM; - rates = kzalloc(sizeof(*rates) * spec->num_rates, GFP_KERNEL); + rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL); if (!rates) goto exit_free_channels; /* * Initialize Rate list. */ - rt2x00lib_rate(&rates[0], 10, DEV_RATEMASK_1MB, - 0x00, IEEE80211_RATE_CCK); - rt2x00lib_rate(&rates[1], 20, DEV_RATEMASK_2MB, - 0x01, IEEE80211_RATE_CCK_2); - rt2x00lib_rate(&rates[2], 55, DEV_RATEMASK_5_5MB, - 0x02, IEEE80211_RATE_CCK_2); - rt2x00lib_rate(&rates[3], 110, DEV_RATEMASK_11MB, - 0x03, IEEE80211_RATE_CCK_2); - - if (spec->num_rates > 4) { - rt2x00lib_rate(&rates[4], 60, DEV_RATEMASK_6MB, - 0x0b, IEEE80211_RATE_OFDM); - rt2x00lib_rate(&rates[5], 90, DEV_RATEMASK_9MB, - 0x0f, IEEE80211_RATE_OFDM); - rt2x00lib_rate(&rates[6], 120, DEV_RATEMASK_12MB, - 0x0a, IEEE80211_RATE_OFDM); - rt2x00lib_rate(&rates[7], 180, DEV_RATEMASK_18MB, - 0x0e, IEEE80211_RATE_OFDM); - rt2x00lib_rate(&rates[8], 240, DEV_RATEMASK_24MB, - 0x09, IEEE80211_RATE_OFDM); - rt2x00lib_rate(&rates[9], 360, DEV_RATEMASK_36MB, - 0x0d, IEEE80211_RATE_OFDM); - rt2x00lib_rate(&rates[10], 480, DEV_RATEMASK_48MB, - 0x08, IEEE80211_RATE_OFDM); - rt2x00lib_rate(&rates[11], 540, DEV_RATEMASK_54MB, - 0x0c, IEEE80211_RATE_OFDM); - } + for (i = 0; i < num_rates; i++) + rt2x00lib_rate(&rates[i], i, rt2x00_get_rate(i)); /* * Initialize Channel list. */ for (i = 0; i < spec->num_channels; i++) { - if (spec->channels[i].channel <= 14) - tx_power = spec->tx_power_bg[i]; - else if (spec->tx_power_a) - tx_power = spec->tx_power_a[i]; - else - tx_power = spec->tx_power_default; - rt2x00lib_channel(&channels[i], - spec->channels[i].channel, tx_power, i); + spec->channels[i].channel, + spec->channels_info[i].max_power, i); } /* - * Intitialize 802.11b - * Rates: CCK. - * Channels: OFDM. - */ - if (spec->num_modes > HWMODE_B) { - hwmodes[HWMODE_B].mode = MODE_IEEE80211B; - hwmodes[HWMODE_B].num_channels = 14; - hwmodes[HWMODE_B].num_rates = 4; - hwmodes[HWMODE_B].channels = channels; - hwmodes[HWMODE_B].rates = rates; - } - - /* - * Intitialize 802.11g + * Intitialize 802.11b, 802.11g * Rates: CCK, OFDM. - * Channels: OFDM. - */ - if (spec->num_modes > HWMODE_G) { - hwmodes[HWMODE_G].mode = MODE_IEEE80211G; - hwmodes[HWMODE_G].num_channels = 14; - hwmodes[HWMODE_G].num_rates = spec->num_rates; - hwmodes[HWMODE_G].channels = channels; - hwmodes[HWMODE_G].rates = rates; + * Channels: 2.4 GHz + */ + if (spec->supported_bands & SUPPORT_BAND_2GHZ) { + rt2x00dev->bands[IEEE80211_BAND_2GHZ].n_channels = 14; + rt2x00dev->bands[IEEE80211_BAND_2GHZ].n_bitrates = num_rates; + rt2x00dev->bands[IEEE80211_BAND_2GHZ].channels = channels; + rt2x00dev->bands[IEEE80211_BAND_2GHZ].bitrates = rates; + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &rt2x00dev->bands[IEEE80211_BAND_2GHZ]; + memcpy(&rt2x00dev->bands[IEEE80211_BAND_2GHZ].ht_cap, + &spec->ht, sizeof(spec->ht)); } /* @@ -932,55 +987,40 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, * Rates: OFDM. * Channels: OFDM, UNII, HiperLAN2. */ - if (spec->num_modes > HWMODE_A) { - hwmodes[HWMODE_A].mode = MODE_IEEE80211A; - hwmodes[HWMODE_A].num_channels = spec->num_channels - 14; - hwmodes[HWMODE_A].num_rates = spec->num_rates - 4; - hwmodes[HWMODE_A].channels = &channels[14]; - hwmodes[HWMODE_A].rates = &rates[4]; + if (spec->supported_bands & SUPPORT_BAND_5GHZ) { + rt2x00dev->bands[IEEE80211_BAND_5GHZ].n_channels = + spec->num_channels - 14; + rt2x00dev->bands[IEEE80211_BAND_5GHZ].n_bitrates = + num_rates - 4; + rt2x00dev->bands[IEEE80211_BAND_5GHZ].channels = &channels[14]; + rt2x00dev->bands[IEEE80211_BAND_5GHZ].bitrates = &rates[4]; + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &rt2x00dev->bands[IEEE80211_BAND_5GHZ]; + memcpy(&rt2x00dev->bands[IEEE80211_BAND_5GHZ].ht_cap, + &spec->ht, sizeof(spec->ht)); } - if (spec->num_modes > HWMODE_G && - ieee80211_register_hwmode(hw, &hwmodes[HWMODE_G])) - goto exit_free_rates; - - if (spec->num_modes > HWMODE_B && - ieee80211_register_hwmode(hw, &hwmodes[HWMODE_B])) - goto exit_free_rates; - - if (spec->num_modes > HWMODE_A && - ieee80211_register_hwmode(hw, &hwmodes[HWMODE_A])) - goto exit_free_rates; - - rt2x00dev->hwmodes = hwmodes; - return 0; -exit_free_rates: - kfree(rates); - -exit_free_channels: + exit_free_channels: kfree(channels); - -exit_free_modes: - kfree(hwmodes); - -exit: - ERROR(rt2x00dev, "Allocation ieee80211 modes failed.\n"); + rt2x00_err(rt2x00dev, "Allocation ieee80211 modes failed\n"); return -ENOMEM; } static void rt2x00lib_remove_hw(struct rt2x00_dev *rt2x00dev) { - if (test_bit(DEVICE_REGISTERED_HW, &rt2x00dev->flags)) + if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) ieee80211_unregister_hw(rt2x00dev->hw); - if (likely(rt2x00dev->hwmodes)) { - kfree(rt2x00dev->hwmodes->channels); - kfree(rt2x00dev->hwmodes->rates); - kfree(rt2x00dev->hwmodes); - rt2x00dev->hwmodes = NULL; + if (likely(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ])) { + kfree(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels); + kfree(rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->bitrates); + rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + rt2x00dev->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; } + + kfree(rt2x00dev->spec.channels_info); } static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) @@ -988,6 +1028,9 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) struct hw_mode_spec *spec = &rt2x00dev->spec; int status; + if (test_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags)) + return 0; + /* * Initialize HW modes. */ @@ -996,111 +1039,97 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) return status; /* - * Register HW. + * Initialize HW fields. */ - status = ieee80211_register_hw(rt2x00dev->hw); - if (status) { - rt2x00lib_remove_hw(rt2x00dev); - return status; - } - - __set_bit(DEVICE_REGISTERED_HW, &rt2x00dev->flags); - - return 0; -} - -/* - * Initialization/uninitialization handlers. - */ -static int rt2x00lib_alloc_entries(struct data_ring *ring, - const u16 max_entries, const u16 data_size, - const u16 desc_size) -{ - struct data_entry *entry; - unsigned int i; - - ring->stats.limit = max_entries; - ring->data_size = data_size; - ring->desc_size = desc_size; + rt2x00dev->hw->queues = rt2x00dev->ops->tx_queues; /* - * Allocate all ring entries. + * Initialize extra TX headroom required. */ - entry = kzalloc(ring->stats.limit * sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; + rt2x00dev->hw->extra_tx_headroom = + max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM, + rt2x00dev->extra_tx_headroom); - for (i = 0; i < ring->stats.limit; i++) { - entry[i].flags = 0; - entry[i].ring = ring; - entry[i].skb = NULL; - entry[i].entry_idx = i; - } - - ring->entry = entry; - - return 0; -} - -static int rt2x00lib_alloc_ring_entries(struct rt2x00_dev *rt2x00dev) -{ - struct data_ring *ring; + /* + * Take TX headroom required for alignment into account. + */ + if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags)) + rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; + else if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) + rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; /* - * Allocate the RX ring. + * Tell mac80211 about the size of our private STA structure. */ - if (rt2x00lib_alloc_entries(rt2x00dev->rx, RX_ENTRIES, DATA_FRAME_SIZE, - rt2x00dev->ops->rxd_size)) - return -ENOMEM; + rt2x00dev->hw->sta_data_size = sizeof(struct rt2x00_sta); /* - * First allocate the TX rings. + * Allocate tx status FIFO for driver use. */ - txring_for_each(rt2x00dev, ring) { - if (rt2x00lib_alloc_entries(ring, TX_ENTRIES, DATA_FRAME_SIZE, - rt2x00dev->ops->txd_size)) - return -ENOMEM; + if (test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags)) { + /* + * Allocate the txstatus fifo. In the worst case the tx + * status fifo has to hold the tx status of all entries + * in all tx queues. Hence, calculate the kfifo size as + * tx_queues * entry_num and round up to the nearest + * power of 2. + */ + int kfifo_size = + roundup_pow_of_two(rt2x00dev->ops->tx_queues * + rt2x00dev->tx->limit * + sizeof(u32)); + + status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size, + GFP_KERNEL); + if (status) + return status; } - if (!test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags)) - return 0; - /* - * Allocate the BEACON ring. + * Initialize tasklets if used by the driver. Tasklets are + * disabled until the interrupts are turned on. The driver + * has to handle that. */ - if (rt2x00lib_alloc_entries(&rt2x00dev->bcn[0], BEACON_ENTRIES, - MGMT_FRAME_SIZE, rt2x00dev->ops->txd_size)) - return -ENOMEM; +#define RT2X00_TASKLET_INIT(taskletname) \ + if (rt2x00dev->ops->lib->taskletname) { \ + tasklet_init(&rt2x00dev->taskletname, \ + rt2x00dev->ops->lib->taskletname, \ + (unsigned long)rt2x00dev); \ + } + + RT2X00_TASKLET_INIT(txstatus_tasklet); + RT2X00_TASKLET_INIT(pretbtt_tasklet); + RT2X00_TASKLET_INIT(tbtt_tasklet); + RT2X00_TASKLET_INIT(rxdone_tasklet); + RT2X00_TASKLET_INIT(autowake_tasklet); + +#undef RT2X00_TASKLET_INIT /* - * Allocate the Atim ring. + * Register HW. */ - if (rt2x00lib_alloc_entries(&rt2x00dev->bcn[1], ATIM_ENTRIES, - DATA_FRAME_SIZE, rt2x00dev->ops->txd_size)) - return -ENOMEM; - - return 0; -} + status = ieee80211_register_hw(rt2x00dev->hw); + if (status) + return status; -static void rt2x00lib_free_ring_entries(struct rt2x00_dev *rt2x00dev) -{ - struct data_ring *ring; + set_bit(DEVICE_STATE_REGISTERED_HW, &rt2x00dev->flags); - ring_for_each(rt2x00dev, ring) { - kfree(ring->entry); - ring->entry = NULL; - } + return 0; } +/* + * Initialization/uninitialization handlers. + */ static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) { - if (!__test_and_clear_bit(DEVICE_INITIALIZED, &rt2x00dev->flags)) + if (!test_and_clear_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) return; /* - * Unregister extra components. + * Stop rfkill polling. */ - rt2x00rfkill_unregister(rt2x00dev); + if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_unregister(rt2x00dev); /* * Allow the HW to uninitialize. @@ -1108,65 +1137,59 @@ static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) rt2x00dev->ops->lib->uninitialize(rt2x00dev); /* - * Free allocated ring entries. + * Free allocated queue entries. */ - rt2x00lib_free_ring_entries(rt2x00dev); + rt2x00queue_uninitialize(rt2x00dev); } static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) { int status; - if (test_bit(DEVICE_INITIALIZED, &rt2x00dev->flags)) + if (test_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags)) return 0; /* - * Allocate all ring entries. + * Allocate all queue entries. */ - status = rt2x00lib_alloc_ring_entries(rt2x00dev); - if (status) { - ERROR(rt2x00dev, "Ring entries allocation failed.\n"); + status = rt2x00queue_initialize(rt2x00dev); + if (status) return status; - } /* * Initialize the device. */ status = rt2x00dev->ops->lib->initialize(rt2x00dev); - if (status) - goto exit; + if (status) { + rt2x00queue_uninitialize(rt2x00dev); + return status; + } - __set_bit(DEVICE_INITIALIZED, &rt2x00dev->flags); + set_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags); /* - * Register the extra components. + * Start rfkill polling. */ - rt2x00rfkill_register(rt2x00dev); + if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_register(rt2x00dev); return 0; - -exit: - rt2x00lib_free_ring_entries(rt2x00dev); - - return status; } int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) { int retval; - if (test_bit(DEVICE_STARTED, &rt2x00dev->flags)) + if (test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) return 0; /* * If this is the first interface which is added, * we should load the firmware now. */ - if (test_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags)) { - retval = rt2x00lib_load_firmware(rt2x00dev); - if (retval) - return retval; - } + retval = rt2x00lib_load_firmware(rt2x00dev); + if (retval) + return retval; /* * Initialize the device. @@ -1175,23 +1198,23 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) if (retval) return retval; - /* - * Enable radio. - */ + rt2x00dev->intf_ap_count = 0; + rt2x00dev->intf_sta_count = 0; + rt2x00dev->intf_associated = 0; + + /* Enable the radio */ retval = rt2x00lib_enable_radio(rt2x00dev); - if (retval) { - rt2x00lib_uninitialize(rt2x00dev); + if (retval) return retval; - } - __set_bit(DEVICE_STARTED, &rt2x00dev->flags); + set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); return 0; } void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev) { - if (!test_bit(DEVICE_STARTED, &rt2x00dev->flags)) + if (!test_and_clear_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) return; /* @@ -1200,120 +1223,171 @@ void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev) */ rt2x00lib_disable_radio(rt2x00dev); - __clear_bit(DEVICE_STARTED, &rt2x00dev->flags); + rt2x00dev->intf_ap_count = 0; + rt2x00dev->intf_sta_count = 0; + rt2x00dev->intf_associated = 0; } -/* - * driver allocation handlers. - */ -static int rt2x00lib_alloc_rings(struct rt2x00_dev *rt2x00dev) +static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev) { - struct data_ring *ring; - unsigned int index; + struct ieee80211_iface_limit *if_limit; + struct ieee80211_iface_combination *if_combination; + + if (rt2x00dev->ops->max_ap_intf < 2) + return; /* - * We need the following rings: - * RX: 1 - * TX: hw->queues - * Beacon: 1 (if required) - * Atim: 1 (if required) + * Build up AP interface limits structure. */ - rt2x00dev->data_rings = 1 + rt2x00dev->hw->queues + - (2 * test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags)); - - ring = kzalloc(rt2x00dev->data_rings * sizeof(*ring), GFP_KERNEL); - if (!ring) { - ERROR(rt2x00dev, "Ring allocation failed.\n"); - return -ENOMEM; - } + if_limit = &rt2x00dev->if_limits_ap; + if_limit->max = rt2x00dev->ops->max_ap_intf; + if_limit->types = BIT(NL80211_IFTYPE_AP); +#ifdef CONFIG_MAC80211_MESH + if_limit->types |= BIT(NL80211_IFTYPE_MESH_POINT); +#endif /* - * Initialize pointers + * Build up AP interface combinations structure. */ - rt2x00dev->rx = ring; - rt2x00dev->tx = &rt2x00dev->rx[1]; - if (test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags)) - rt2x00dev->bcn = &rt2x00dev->tx[rt2x00dev->hw->queues]; + if_combination = &rt2x00dev->if_combinations[IF_COMB_AP]; + if_combination->limits = if_limit; + if_combination->n_limits = 1; + if_combination->max_interfaces = if_limit->max; + if_combination->num_different_channels = 1; /* - * Initialize ring parameters. - * RX: queue_idx = 0 - * TX: queue_idx = IEEE80211_TX_QUEUE_DATA0 + index - * TX: cw_min: 2^5 = 32. - * TX: cw_max: 2^10 = 1024. + * Finally, specify the possible combinations to mac80211. */ - rt2x00dev->rx->rt2x00dev = rt2x00dev; - rt2x00dev->rx->queue_idx = 0; - - index = IEEE80211_TX_QUEUE_DATA0; - txring_for_each(rt2x00dev, ring) { - ring->rt2x00dev = rt2x00dev; - ring->queue_idx = index++; - ring->tx_params.aifs = 2; - ring->tx_params.cw_min = 5; - ring->tx_params.cw_max = 10; - } - - return 0; + rt2x00dev->hw->wiphy->iface_combinations = rt2x00dev->if_combinations; + rt2x00dev->hw->wiphy->n_iface_combinations = 1; } -static void rt2x00lib_free_rings(struct rt2x00_dev *rt2x00dev) +static unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev) { - kfree(rt2x00dev->rx); - rt2x00dev->rx = NULL; - rt2x00dev->tx = NULL; - rt2x00dev->bcn = NULL; + if (WARN_ON(!rt2x00dev->tx)) + return 0; + + if (rt2x00_is_usb(rt2x00dev)) + return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size; + + return rt2x00dev->tx[0].winfo_size; } +/* + * driver allocation handlers. + */ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) { int retval = -ENOMEM; /* - * Let the driver probe the device to detect the capabilities. + * Set possible interface combinations. */ - retval = rt2x00dev->ops->lib->probe_hw(rt2x00dev); - if (retval) { - ERROR(rt2x00dev, "Failed to allocate device.\n"); - goto exit; + rt2x00lib_set_if_combinations(rt2x00dev); + + /* + * Allocate the driver data memory, if necessary. + */ + if (rt2x00dev->ops->drv_data_size > 0) { + rt2x00dev->drv_data = kzalloc(rt2x00dev->ops->drv_data_size, + GFP_KERNEL); + if (!rt2x00dev->drv_data) { + retval = -ENOMEM; + goto exit; + } } + spin_lock_init(&rt2x00dev->irqmask_lock); + mutex_init(&rt2x00dev->csr_mutex); + INIT_LIST_HEAD(&rt2x00dev->bar_list); + spin_lock_init(&rt2x00dev->bar_list_lock); + + set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + /* - * Initialize configuration work. + * Make room for rt2x00_intf inside the per-interface + * structure ieee80211_vif. */ - INIT_WORK(&rt2x00dev->beacon_work, rt2x00lib_beacondone_scheduled); - INIT_WORK(&rt2x00dev->filter_work, rt2x00lib_packetfilter_scheduled); - INIT_WORK(&rt2x00dev->config_work, rt2x00lib_configuration_scheduled); - INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00lib_link_tuner); + rt2x00dev->hw->vif_data_size = sizeof(struct rt2x00_intf); /* - * Reset current working type. + * rt2x00 devices can only use the last n bits of the MAC address + * for virtual interfaces. */ - rt2x00dev->interface.type = IEEE80211_IF_TYPE_INVALID; + rt2x00dev->hw->wiphy->addr_mask[ETH_ALEN - 1] = + (rt2x00dev->ops->max_ap_intf - 1); /* - * Allocate ring array. + * Initialize work. */ - retval = rt2x00lib_alloc_rings(rt2x00dev); + rt2x00dev->workqueue = + alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); + if (!rt2x00dev->workqueue) { + retval = -ENOMEM; + goto exit; + } + + INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); + INIT_DELAYED_WORK(&rt2x00dev->autowakeup_work, rt2x00lib_autowakeup); + INIT_WORK(&rt2x00dev->sleep_work, rt2x00lib_sleep); + + /* + * Let the driver probe the device to detect the capabilities. + */ + retval = rt2x00dev->ops->lib->probe_hw(rt2x00dev); + if (retval) { + rt2x00_err(rt2x00dev, "Failed to allocate device\n"); + goto exit; + } + + /* + * Allocate queue array. + */ + retval = rt2x00queue_allocate(rt2x00dev); if (retval) goto exit; + /* Cache TX headroom value */ + rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev); + + /* + * Determine which operating modes are supported, all modes + * which require beaconing, depend on the availability of + * beacon entries. + */ + rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + if (rt2x00dev->bcn->limit > 0) + rt2x00dev->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_WDS); + + rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + /* * Initialize ieee80211 structure. */ retval = rt2x00lib_probe_hw(rt2x00dev); if (retval) { - ERROR(rt2x00dev, "Failed to initialize hw.\n"); + rt2x00_err(rt2x00dev, "Failed to initialize hw\n"); goto exit; } /* * Register extra components. */ - rt2x00rfkill_allocate(rt2x00dev); + rt2x00link_register(rt2x00dev); + rt2x00leds_register(rt2x00dev); rt2x00debug_register(rt2x00dev); - __set_bit(DEVICE_PRESENT, &rt2x00dev->flags); + /* + * Start rfkill polling. + */ + if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_register(rt2x00dev); return 0; @@ -1326,7 +1400,13 @@ EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) { - __clear_bit(DEVICE_PRESENT, &rt2x00dev->flags); + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + + /* + * Stop rfkill polling. + */ + if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_unregister(rt2x00dev); /* * Disable radio. @@ -1334,6 +1414,34 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) rt2x00lib_disable_radio(rt2x00dev); /* + * Stop all work. + */ + cancel_work_sync(&rt2x00dev->intf_work); + cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); + cancel_work_sync(&rt2x00dev->sleep_work); + if (rt2x00_is_usb(rt2x00dev)) { + hrtimer_cancel(&rt2x00dev->txstatus_timer); + cancel_work_sync(&rt2x00dev->rxdone_work); + cancel_work_sync(&rt2x00dev->txdone_work); + } + if (rt2x00dev->workqueue) + destroy_workqueue(rt2x00dev->workqueue); + + /* + * Free the tx status fifo. + */ + kfifo_free(&rt2x00dev->txstatus_fifo); + + /* + * Kill the tx status tasklet. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->autowake_tasklet); + + /* * Uninitialize device. */ rt2x00lib_uninitialize(rt2x00dev); @@ -1342,7 +1450,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) * Free extra components */ rt2x00debug_deregister(rt2x00dev); - rt2x00rfkill_free(rt2x00dev); + rt2x00leds_unregister(rt2x00dev); /* * Free ieee80211_hw memory. @@ -1355,9 +1463,15 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) rt2x00lib_free_firmware(rt2x00dev); /* - * Free ring structures. + * Free queue structures. + */ + rt2x00queue_free(rt2x00dev); + + /* + * Free the driver data. */ - rt2x00lib_free_rings(rt2x00dev); + if (rt2x00dev->drv_data) + kfree(rt2x00dev->drv_data); } EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); @@ -1367,31 +1481,25 @@ EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); #ifdef CONFIG_PM int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state) { - int retval; - - NOTICE(rt2x00dev, "Going to sleep.\n"); - __clear_bit(DEVICE_PRESENT, &rt2x00dev->flags); + rt2x00_dbg(rt2x00dev, "Going to sleep\n"); /* - * Only continue if mac80211 has open interfaces. + * Prevent mac80211 from accessing driver while suspended. */ - if (!test_bit(DEVICE_STARTED, &rt2x00dev->flags)) - goto exit; - __set_bit(DEVICE_STARTED_SUSPEND, &rt2x00dev->flags); + if (!test_and_clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; /* - * Disable radio. + * Cleanup as much as possible. */ - rt2x00lib_stop(rt2x00dev); rt2x00lib_uninitialize(rt2x00dev); /* * Suspend/disable extra components. */ - rt2x00rfkill_suspend(rt2x00dev); + rt2x00leds_suspend(rt2x00dev); rt2x00debug_deregister(rt2x00dev); -exit: /* * Set device mode to sleep for power management, * on some hardware this call seems to consistently fail. @@ -1403,10 +1511,8 @@ exit: * the radio and the other components already disabled the * device is as good as disabled. */ - retval = rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_SLEEP); - if (retval) - WARNING(rt2x00dev, "Device failed to enter sleep state, " - "continue suspending.\n"); + if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_SLEEP)) + rt2x00_warn(rt2x00dev, "Device failed to enter sleep state, continue suspending\n"); return 0; } @@ -1414,70 +1520,20 @@ EXPORT_SYMBOL_GPL(rt2x00lib_suspend); int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev) { - struct interface *intf = &rt2x00dev->interface; - int retval; - - NOTICE(rt2x00dev, "Waking up.\n"); + rt2x00_dbg(rt2x00dev, "Waking up\n"); /* * Restore/enable extra components. */ rt2x00debug_register(rt2x00dev); - rt2x00rfkill_resume(rt2x00dev); - - /* - * Only continue if mac80211 had open interfaces. - */ - if (!__test_and_clear_bit(DEVICE_STARTED_SUSPEND, &rt2x00dev->flags)) - return 0; - - /* - * Reinitialize device and all active interfaces. - */ - retval = rt2x00lib_start(rt2x00dev); - if (retval) - goto exit; - - /* - * Reconfigure device. - */ - rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, 1); - if (!rt2x00dev->hw->conf.radio_enabled) - rt2x00lib_disable_radio(rt2x00dev); - - rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); - rt2x00lib_config_bssid(rt2x00dev, intf->bssid); - rt2x00lib_config_type(rt2x00dev, intf->type); + rt2x00leds_resume(rt2x00dev); /* * We are ready again to receive requests from mac80211. */ - __set_bit(DEVICE_PRESENT, &rt2x00dev->flags); - - /* - * It is possible that during that mac80211 has attempted - * to send frames while we were suspending or resuming. - * In that case we have disabled the TX queue and should - * now enable it again - */ - ieee80211_start_queues(rt2x00dev->hw); - - /* - * When in Master or Ad-hoc mode, - * restart Beacon transmitting by faking a beacondone event. - */ - if (intf->type == IEEE80211_IF_TYPE_AP || - intf->type == IEEE80211_IF_TYPE_IBSS) - rt2x00lib_beacondone(rt2x00dev); + set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); return 0; - -exit: - rt2x00lib_disable_radio(rt2x00dev); - rt2x00lib_uninitialize(rt2x00dev); - rt2x00debug_deregister(rt2x00dev); - - return retval; } EXPORT_SYMBOL_GPL(rt2x00lib_resume); #endif /* CONFIG_PM */ diff --git a/drivers/net/wireless/rt2x00/rt2x00dump.h b/drivers/net/wireless/rt2x00/rt2x00dump.h index 99f3f367adc..4c0e01b5d51 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dump.h +++ b/drivers/net/wireless/rt2x00/rt2x00dump.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,14 +13,17 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* Module: rt2x00dump - Abstract: Data structures for the rt2x00debug & userspace. + Abstract: + Data structures for the rt2x00debug & userspace. + + The declarations in this file can be used by both rt2x00 + and userspace and therefore should be kept together in + this file. */ #ifndef RT2X00DUMP_H @@ -46,7 +49,7 @@ * [rt2x00dump header][hardware descriptor][ieee802.11 frame] * * rt2x00dump header: The description of the dumped frame, as well as - * additional information usefull for debugging. See &rt2x00dump_hdr. + * additional information useful for debugging. See &rt2x00dump_hdr. * hardware descriptor: Descriptor that was used to receive or transmit * the frame. * ieee802.11 frame: The actual frame that was received or transmitted. @@ -62,11 +65,14 @@ * the tx event which has either succeeded or failed. A frame * with this type should also have been reported with as a * %DUMP_FRAME_TX frame. + * @DUMP_FRAME_BEACON: This beacon frame is queued for transmission to the + * hardware. */ enum rt2x00_dump_type { DUMP_FRAME_RXDONE = 1, DUMP_FRAME_TX = 2, DUMP_FRAME_TXDONE = 3, + DUMP_FRAME_BEACON = 4, }; /** @@ -93,8 +99,8 @@ enum rt2x00_dump_type { * @chip_rf: RF chipset * @chip_rev: Chipset revision * @type: The frame type (&rt2x00_dump_type) - * @ring_index: The index number of the data ring. - * @entry_index: The index number of the entry inside the data ring. + * @queue_index: The index number of the data queue. + * @entry_index: The index number of the entry inside the data queue. * @timestamp_sec: Timestamp - seconds * @timestamp_usec: Timestamp - microseconds */ @@ -108,10 +114,10 @@ struct rt2x00dump_hdr { __le16 chip_rt; __le16 chip_rf; - __le32 chip_rev; + __le16 chip_rev; __le16 type; - __u8 ring_index; + __u8 queue_index; __u8 entry_index; __le32 timestamp_sec; diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c index 0a475e4e244..fbae2799e3e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00firmware.c +++ b/drivers/net/wireless/rt2x00/rt2x00firmware.c @@ -1,5 +1,6 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +14,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -23,7 +22,6 @@ Abstract: rt2x00 firmware loading routines. */ -#include <linux/crc-itu-t.h> #include <linux/kernel.h> #include <linux/module.h> @@ -36,52 +34,54 @@ static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev) const struct firmware *fw; char *fw_name; int retval; - u16 crc; - u16 tmp; /* * Read correct firmware from harddisk. */ fw_name = rt2x00dev->ops->lib->get_firmware_name(rt2x00dev); if (!fw_name) { - ERROR(rt2x00dev, - "Invalid firmware filename.\n" - "Please file bug report to %s.\n", DRV_PROJECT); + rt2x00_err(rt2x00dev, + "Invalid firmware filename\n" + "Please file bug report to %s\n", DRV_PROJECT); return -EINVAL; } - INFO(rt2x00dev, "Loading firmware file '%s'.\n", fw_name); + rt2x00_info(rt2x00dev, "Loading firmware file '%s'\n", fw_name); retval = request_firmware(&fw, fw_name, device); if (retval) { - ERROR(rt2x00dev, "Failed to request Firmware.\n"); + rt2x00_err(rt2x00dev, "Failed to request Firmware\n"); return retval; } if (!fw || !fw->size || !fw->data) { - ERROR(rt2x00dev, "Failed to read Firmware.\n"); + rt2x00_err(rt2x00dev, "Failed to read Firmware\n"); + release_firmware(fw); return -ENOENT; } - /* - * Validate the firmware using 16 bit CRC. - * The last 2 bytes of the firmware are the CRC - * so substract those 2 bytes from the CRC checksum, - * and set those 2 bytes to 0 when calculating CRC. - */ - tmp = 0; - crc = crc_itu_t(0, fw->data, fw->size - 2); - crc = crc_itu_t(crc, (u8 *)&tmp, 2); - - if (crc != (fw->data[fw->size - 2] << 8 | fw->data[fw->size - 1])) { - ERROR(rt2x00dev, "Firmware CRC error.\n"); - retval = -ENOENT; + rt2x00_info(rt2x00dev, "Firmware detected - version: %d.%d\n", + fw->data[fw->size - 4], fw->data[fw->size - 3]); + snprintf(rt2x00dev->hw->wiphy->fw_version, + sizeof(rt2x00dev->hw->wiphy->fw_version), "%d.%d", + fw->data[fw->size - 4], fw->data[fw->size - 3]); + + retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size); + switch (retval) { + case FW_OK: + break; + case FW_BAD_CRC: + rt2x00_err(rt2x00dev, "Firmware checksum error\n"); + goto exit; + case FW_BAD_LENGTH: + rt2x00_err(rt2x00dev, "Invalid firmware file length (len=%zu)\n", + fw->size); + goto exit; + case FW_BAD_VERSION: + rt2x00_err(rt2x00dev, "Current firmware does not support detected chipset\n"); goto exit; } - INFO(rt2x00dev, "Firmware detected - version: %d.%d.\n", - fw->data[fw->size - 4], fw->data[fw->size - 3]); - rt2x00dev->fw = fw; return 0; @@ -89,13 +89,16 @@ static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev) exit: release_firmware(fw); - return retval; + return -ENOENT; } int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) { int retval; + if (!test_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags)) + return 0; + if (!rt2x00dev->fw) { retval = rt2x00lib_request_firmware(rt2x00dev); if (retval) @@ -108,6 +111,14 @@ int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) retval = rt2x00dev->ops->lib->load_firmware(rt2x00dev, rt2x00dev->fw->data, rt2x00dev->fw->size); + + /* + * When the firmware is uploaded to the hardware the LED + * association status might have been triggered, for correct + * LED handling it should now be reset. + */ + rt2x00leds_led_assoc(rt2x00dev, false); + return retval; } @@ -116,4 +127,3 @@ void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) release_firmware(rt2x00dev->fw); rt2x00dev->fw = NULL; } - diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/rt2x00/rt2x00leds.c new file mode 100644 index 00000000000..c681d04b506 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00leds.c @@ -0,0 +1,244 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 led specific routines. + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "rt2x00.h" +#include "rt2x00lib.h" + +void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi) +{ + struct rt2x00_led *led = &rt2x00dev->led_qual; + unsigned int brightness; + + if ((led->type != LED_TYPE_QUALITY) || !(led->flags & LED_REGISTERED)) + return; + + /* + * Led handling requires a positive value for the rssi, + * to do that correctly we need to add the correction. + */ + rssi += rt2x00dev->rssi_offset; + + /* + * Get the rssi level, this is used to convert the rssi + * to a LED value inside the range LED_OFF - LED_FULL. + */ + if (rssi <= 30) + rssi = 0; + else if (rssi <= 39) + rssi = 1; + else if (rssi <= 49) + rssi = 2; + else if (rssi <= 53) + rssi = 3; + else if (rssi <= 63) + rssi = 4; + else + rssi = 5; + + /* + * Note that we must _not_ send LED_OFF since the driver + * is going to calculate the value and might use it in a + * division. + */ + brightness = ((LED_FULL / 6) * rssi) + 1; + if (brightness != led->led_dev.brightness) { + led->led_dev.brightness_set(&led->led_dev, brightness); + led->led_dev.brightness = brightness; + } +} + +static void rt2x00led_led_simple(struct rt2x00_led *led, bool enabled) +{ + unsigned int brightness = enabled ? LED_FULL : LED_OFF; + + if (!(led->flags & LED_REGISTERED)) + return; + + led->led_dev.brightness_set(&led->led_dev, brightness); + led->led_dev.brightness = brightness; +} + +void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled) +{ + if (rt2x00dev->led_qual.type == LED_TYPE_ACTIVITY) + rt2x00led_led_simple(&rt2x00dev->led_qual, enabled); +} + +void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled) +{ + if (rt2x00dev->led_assoc.type == LED_TYPE_ASSOC) + rt2x00led_led_simple(&rt2x00dev->led_assoc, enabled); +} + +void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled) +{ + if (rt2x00dev->led_radio.type == LED_TYPE_RADIO) + rt2x00led_led_simple(&rt2x00dev->led_radio, enabled); +} + +static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + const char *name) +{ + struct device *device = wiphy_dev(rt2x00dev->hw->wiphy); + int retval; + + led->led_dev.name = name; + led->led_dev.brightness = LED_OFF; + + retval = led_classdev_register(device, &led->led_dev); + if (retval) { + rt2x00_err(rt2x00dev, "Failed to register led handler\n"); + return retval; + } + + led->flags |= LED_REGISTERED; + + return 0; +} + +void rt2x00leds_register(struct rt2x00_dev *rt2x00dev) +{ + char name[36]; + int retval; + unsigned long on_period; + unsigned long off_period; + const char *phy_name = wiphy_name(rt2x00dev->hw->wiphy); + + if (rt2x00dev->led_radio.flags & LED_INITIALIZED) { + snprintf(name, sizeof(name), "%s-%s::radio", + rt2x00dev->ops->name, phy_name); + + retval = rt2x00leds_register_led(rt2x00dev, + &rt2x00dev->led_radio, + name); + if (retval) + goto exit_fail; + } + + if (rt2x00dev->led_assoc.flags & LED_INITIALIZED) { + snprintf(name, sizeof(name), "%s-%s::assoc", + rt2x00dev->ops->name, phy_name); + + retval = rt2x00leds_register_led(rt2x00dev, + &rt2x00dev->led_assoc, + name); + if (retval) + goto exit_fail; + } + + if (rt2x00dev->led_qual.flags & LED_INITIALIZED) { + snprintf(name, sizeof(name), "%s-%s::quality", + rt2x00dev->ops->name, phy_name); + + retval = rt2x00leds_register_led(rt2x00dev, + &rt2x00dev->led_qual, + name); + if (retval) + goto exit_fail; + } + + /* + * Initialize blink time to default value: + * On period: 70ms + * Off period: 30ms + */ + if (rt2x00dev->led_radio.led_dev.blink_set) { + on_period = 70; + off_period = 30; + rt2x00dev->led_radio.led_dev.blink_set( + &rt2x00dev->led_radio.led_dev, &on_period, &off_period); + } + + return; + +exit_fail: + rt2x00leds_unregister(rt2x00dev); +} + +static void rt2x00leds_unregister_led(struct rt2x00_led *led) +{ + led_classdev_unregister(&led->led_dev); + + /* + * This might look weird, but when we are unregistering while + * suspended the led is already off, and since we haven't + * fully resumed yet, access to the device might not be + * possible yet. + */ + if (!(led->led_dev.flags & LED_SUSPENDED)) + led->led_dev.brightness_set(&led->led_dev, LED_OFF); + + led->flags &= ~LED_REGISTERED; +} + +void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev) +{ + if (rt2x00dev->led_qual.flags & LED_REGISTERED) + rt2x00leds_unregister_led(&rt2x00dev->led_qual); + if (rt2x00dev->led_assoc.flags & LED_REGISTERED) + rt2x00leds_unregister_led(&rt2x00dev->led_assoc); + if (rt2x00dev->led_radio.flags & LED_REGISTERED) + rt2x00leds_unregister_led(&rt2x00dev->led_radio); +} + +static inline void rt2x00leds_suspend_led(struct rt2x00_led *led) +{ + led_classdev_suspend(&led->led_dev); + + /* This shouldn't be needed, but just to be safe */ + led->led_dev.brightness_set(&led->led_dev, LED_OFF); + led->led_dev.brightness = LED_OFF; +} + +void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev) +{ + if (rt2x00dev->led_qual.flags & LED_REGISTERED) + rt2x00leds_suspend_led(&rt2x00dev->led_qual); + if (rt2x00dev->led_assoc.flags & LED_REGISTERED) + rt2x00leds_suspend_led(&rt2x00dev->led_assoc); + if (rt2x00dev->led_radio.flags & LED_REGISTERED) + rt2x00leds_suspend_led(&rt2x00dev->led_radio); +} + +static inline void rt2x00leds_resume_led(struct rt2x00_led *led) +{ + led_classdev_resume(&led->led_dev); + + /* Device might have enabled the LEDS during resume */ + led->led_dev.brightness_set(&led->led_dev, LED_OFF); + led->led_dev.brightness = LED_OFF; +} + +void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev) +{ + if (rt2x00dev->led_radio.flags & LED_REGISTERED) + rt2x00leds_resume_led(&rt2x00dev->led_radio); + if (rt2x00dev->led_assoc.flags & LED_REGISTERED) + rt2x00leds_resume_led(&rt2x00dev->led_assoc); + if (rt2x00dev->led_qual.flags & LED_REGISTERED) + rt2x00leds_resume_led(&rt2x00dev->led_qual); +} diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.h b/drivers/net/wireless/rt2x00/rt2x00leds.h new file mode 100644 index 00000000000..b2c5269570d --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00leds.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 led datastructures and routines + */ + +#ifndef RT2X00LEDS_H +#define RT2X00LEDS_H + +enum led_type { + LED_TYPE_RADIO, + LED_TYPE_ASSOC, + LED_TYPE_ACTIVITY, + LED_TYPE_QUALITY, +}; + +struct rt2x00_led { + struct rt2x00_dev *rt2x00dev; + struct led_classdev led_dev; + + enum led_type type; + unsigned int flags; +#define LED_INITIALIZED ( 1 << 0 ) +#define LED_REGISTERED ( 1 << 1 ) +}; + +#endif /* RT2X00LEDS_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index ce58c654ade..fb7c349ccc9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -1,5 +1,6 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +14,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -28,18 +27,48 @@ /* * Interval defines - * Both the link tuner as the rfkill will be called once per second. */ -#define LINK_TUNE_INTERVAL ( round_jiffies_relative(HZ) ) -#define RFKILL_POLL_INTERVAL ( 1000 ) +#define WATCHDOG_INTERVAL round_jiffies_relative(HZ) +#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) +#define AGC_INTERVAL round_jiffies_relative(4 * HZ) +#define VCO_INTERVAL round_jiffies_relative(10 * HZ) /* 10 sec */ + +/* + * rt2x00_rate: Per rate device information + */ +struct rt2x00_rate { + unsigned short flags; +#define DEV_RATE_CCK 0x0001 +#define DEV_RATE_OFDM 0x0002 +#define DEV_RATE_SHORT_PREAMBLE 0x0004 + + unsigned short bitrate; /* In 100kbit/s */ + unsigned short ratemask; + + unsigned short plcp; + unsigned short mcs; +}; + +extern const struct rt2x00_rate rt2x00_supported_rates[12]; + +static inline const struct rt2x00_rate *rt2x00_get_rate(const u16 hw_value) +{ + return &rt2x00_supported_rates[hw_value & 0xff]; +} + +#define RATE_MCS(__mode, __mcs) \ + ((((__mode) & 0x00ff) << 8) | ((__mcs) & 0x00ff)) + +static inline int rt2x00_get_rate_mcs(const u16 mcs_value) +{ + return (mcs_value & 0x00ff); +} /* * Radio control handlers. */ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev); void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev); -void rt2x00lib_toggle_rx(struct rt2x00_dev *rt2x00dev, enum dev_state state); -void rt2x00lib_reset_link_tuner(struct rt2x00_dev *rt2x00dev); /* * Initialization handlers. @@ -50,13 +79,229 @@ void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev); /* * Configuration handlers. */ -void rt2x00lib_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *mac); -void rt2x00lib_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid); -void rt2x00lib_config_type(struct rt2x00_dev *rt2x00dev, const int type); +void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + enum nl80211_iftype type, + const u8 *mac, const u8 *bssid); +void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct ieee80211_bss_conf *conf, + u32 changed); void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, - enum antenna rx, enum antenna tx); + struct antenna_setup ant); void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, - struct ieee80211_conf *conf, const int force_config); + struct ieee80211_conf *conf, + const unsigned int changed_flags); + +/** + * DOC: Queue handlers + */ + +/** + * rt2x00queue_alloc_rxskb - allocate a skb for RX purposes. + * @entry: The entry for which the skb will be applicable. + */ +struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp); + +/** + * rt2x00queue_free_skb - free a skb + * @entry: The entry for which the skb will be applicable. + */ +void rt2x00queue_free_skb(struct queue_entry *entry); + +/** + * rt2x00queue_align_frame - Align 802.11 frame to 4-byte boundary + * @skb: The skb to align + * + * Align the start of the 802.11 frame to a 4-byte boundary, this could + * mean the payload is not aligned properly though. + */ +void rt2x00queue_align_frame(struct sk_buff *skb); + +/** + * rt2x00queue_insert_l2pad - Align 802.11 header & payload to 4-byte boundary + * @skb: The skb to align + * @header_length: Length of 802.11 header + * + * Apply L2 padding to align both header and payload to 4-byte boundary + */ +void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length); + +/** + * rt2x00queue_insert_l2pad - Remove L2 padding from 802.11 frame + * @skb: The skb to align + * @header_length: Length of 802.11 header + * + * Remove L2 padding used to align both header and payload to 4-byte boundary, + * by removing the L2 padding the header will no longer be 4-byte aligned. + */ +void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length); + +/** + * rt2x00queue_write_tx_frame - Write TX frame to hardware + * @queue: Queue over which the frame should be send + * @skb: The skb to send + * @local: frame is not from mac80211 + */ +int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, + struct ieee80211_sta *sta, bool local); + +/** + * rt2x00queue_update_beacon - Send new beacon from mac80211 + * to hardware. Handles locking by itself (mutex). + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @vif: Interface for which the beacon should be updated. + */ +int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif); + +/** + * rt2x00queue_update_beacon_locked - Send new beacon from mac80211 + * to hardware. Caller needs to ensure locking. + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @vif: Interface for which the beacon should be updated. + */ +int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif); + +/** + * rt2x00queue_clear_beacon - Clear beacon in hardware + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @vif: Interface for which the beacon should be updated. + */ +int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif); + +/** + * rt2x00queue_index_inc - Index incrementation function + * @entry: Queue entry (&struct queue_entry) to perform the action on. + * @index: Index type (&enum queue_index) to perform the action on. + * + * This function will increase the requested index on the entry's queue, + * it will grab the appropriate locks and handle queue overflow events by + * resetting the index to the start of the queue. + */ +void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index); + +/** + * rt2x00queue_init_queues - Initialize all data queues + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This function will loop through all available queues to clear all + * index numbers and set the queue entry to the correct initialization + * state. + */ +void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev); + +int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev); +void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev); +int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev); +void rt2x00queue_free(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_update_stats - Update link statistics from RX frame + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @skb: Received frame + * @rxdesc: Received frame descriptor + * + * Update link statistics based on the information from the + * received frame descriptor. + */ +void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc); + +/** + * rt2x00link_start_tuner - Start periodic link tuner work + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This start the link tuner periodic work, this work will + * be executed periodically until &rt2x00link_stop_tuner has + * been called. + */ +void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_stop_tuner - Stop periodic link tuner work + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * After this function completed the link tuner will not + * be running until &rt2x00link_start_tuner is called. + */ +void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_reset_tuner - Reset periodic link tuner work + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * @antenna: Should the antenna tuning also be reset + * + * The VGC limit configured in the hardware will be reset to 0 + * which forces the driver to rediscover the correct value for + * the current association. This is needed when configuration + * options have changed which could drastically change the + * SNR level or link quality (i.e. changing the antenna setting). + * + * Resetting the link tuner will also cause the periodic work counter + * to be reset. Any driver which has a fixed limit on the number + * of rounds the link tuner is supposed to work will accept the + * tuner actions again if this limit was previously reached. + * + * If @antenna is set to true a the software antenna diversity + * tuning will also be reset. + */ +void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna); + +/** + * rt2x00link_start_watchdog - Start periodic watchdog monitoring + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * This start the watchdog periodic work, this work will + *be executed periodically until &rt2x00link_stop_watchdog has + * been called. + */ +void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_stop_watchdog - Stop periodic watchdog monitoring + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * After this function completed the watchdog monitoring will not + * be running until &rt2x00link_start_watchdog is called. + */ +void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_start_agc - Start periodic gain calibration + * @rt2x00dev: Pointer to &struct rt2x00_dev. + */ +void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_start_vcocal - Start periodic VCO calibration + * @rt2x00dev: Pointer to &struct rt2x00_dev. + */ +void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_stop_agc - Stop periodic gain calibration + * @rt2x00dev: Pointer to &struct rt2x00_dev. + */ +void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_stop_vcocal - Stop periodic VCO calibration + * @rt2x00dev: Pointer to &struct rt2x00_dev. + */ +void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00link_register - Initialize link tuning & watchdog functionality + * @rt2x00dev: Pointer to &struct rt2x00_dev. + * + * Initialize work structure and all link tuning and watchdog related + * parameters. This will not start the periodic work itself. + */ +void rt2x00link_register(struct rt2x00_dev *rt2x00dev); /* * Firmware handlers. @@ -80,7 +325,8 @@ static inline void rt2x00lib_free_firmware(struct rt2x00_dev *rt2x00dev) #ifdef CONFIG_RT2X00_LIB_DEBUGFS void rt2x00debug_register(struct rt2x00_dev *rt2x00dev); void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev); -void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb); +void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc); #else static inline void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) { @@ -90,46 +336,133 @@ static inline void rt2x00debug_deregister(struct rt2x00_dev *rt2x00dev) { } -static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb) +static inline void rt2x00debug_update_crypto(struct rt2x00_dev *rt2x00dev, + struct rxdone_entry_desc *rxdesc) { } #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ /* - * RFkill handlers. + * Crypto handlers. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL -void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev); -void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev); -void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev); -void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev); -void rt2x00rfkill_suspend(struct rt2x00_dev *rt2x00dev); -void rt2x00rfkill_resume(struct rt2x00_dev *rt2x00dev); +#ifdef CONFIG_RT2X00_LIB_CRYPTO +enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key); +void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc); +unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb); +void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, + struct txentry_desc *txdesc); +void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, + struct txentry_desc *txdesc); +void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, unsigned int header_length); +void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc); #else +static inline enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key) +{ + return CIPHER_NONE; +} + +static inline void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc) +{ +} + +static inline unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb) +{ + return 0; +} + +static inline void rt2x00crypto_tx_copy_iv(struct sk_buff *skb, + struct txentry_desc *txdesc) +{ +} + +static inline void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, + struct txentry_desc *txdesc) +{ +} + +static inline void rt2x00crypto_tx_insert_iv(struct sk_buff *skb, + unsigned int header_length) +{ +} + +static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, + unsigned int header_length, + struct rxdone_entry_desc *rxdesc) +{ +} +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ + +/* + * RFkill handlers. + */ static inline void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev) { + if (test_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags)) + wiphy_rfkill_start_polling(rt2x00dev->hw->wiphy); } static inline void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev) { + if (test_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags)) + wiphy_rfkill_stop_polling(rt2x00dev->hw->wiphy); +} + +/* + * LED handlers + */ +#ifdef CONFIG_RT2X00_LIB_LEDS +void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi); +void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, bool enabled); +void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, bool enabled); +void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, bool enabled); +void rt2x00leds_register(struct rt2x00_dev *rt2x00dev); +void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev); +void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev); +void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev); +#else +static inline void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, + int rssi) +{ +} + +static inline void rt2x00led_led_activity(struct rt2x00_dev *rt2x00dev, + bool enabled) +{ +} + +static inline void rt2x00leds_led_assoc(struct rt2x00_dev *rt2x00dev, + bool enabled) +{ +} + +static inline void rt2x00leds_led_radio(struct rt2x00_dev *rt2x00dev, + bool enabled) +{ } -static inline void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev) +static inline void rt2x00leds_register(struct rt2x00_dev *rt2x00dev) { } -static inline void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev) +static inline void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev) { } -static inline void rt2x00rfkill_suspend(struct rt2x00_dev *rt2x00dev) +static inline void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev) { } -static inline void rt2x00rfkill_resume(struct rt2x00_dev *rt2x00dev) +static inline void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev) { } -#endif /* CONFIG_RT2X00_LIB_RFKILL */ +#endif /* CONFIG_RT2X00_LIB_LEDS */ #endif /* RT2X00LIB_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c new file mode 100644 index 00000000000..9b941c0c126 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00link.c @@ -0,0 +1,498 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 generic link tuning routines. + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "rt2x00.h" +#include "rt2x00lib.h" + +/* + * When we lack RSSI information return something less then -80 to + * tell the driver to tune the device to maximum sensitivity. + */ +#define DEFAULT_RSSI -128 + +/* Constants for EWMA calculations. */ +#define RT2X00_EWMA_FACTOR 1024 +#define RT2X00_EWMA_WEIGHT 8 + +static inline int rt2x00link_get_avg_rssi(struct ewma *ewma) +{ + unsigned long avg; + + avg = ewma_read(ewma); + if (avg) + return -avg; + + return DEFAULT_RSSI; +} + +static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + + if (rt2x00dev->link.qual.rx_success) + return rt2x00link_get_avg_rssi(&ant->rssi_ant); + + return DEFAULT_RSSI; +} + +static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + + if (ant->rssi_history) + return ant->rssi_history; + return DEFAULT_RSSI; +} + +static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev, + int rssi) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + ant->rssi_history = rssi; +} + +static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev) +{ + ewma_init(&rt2x00dev->link.ant.rssi_ant, RT2X00_EWMA_FACTOR, + RT2X00_EWMA_WEIGHT); +} + +static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup new_ant; + int other_antenna; + + int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev); + int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev); + + memcpy(&new_ant, &ant->active, sizeof(new_ant)); + + /* + * We are done sampling. Now we should evaluate the results. + */ + ant->flags &= ~ANTENNA_MODE_SAMPLE; + + /* + * During the last period we have sampled the RSSI + * from both antennas. It now is time to determine + * which antenna demonstrated the best performance. + * When we are already on the antenna with the best + * performance, just create a good starting point + * for the history and we are done. + */ + if (sample_current >= sample_other) { + rt2x00link_antenna_update_rssi_history(rt2x00dev, + sample_current); + return; + } + + other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; + + if (ant->flags & ANTENNA_RX_DIVERSITY) + new_ant.rx = other_antenna; + + if (ant->flags & ANTENNA_TX_DIVERSITY) + new_ant.tx = other_antenna; + + rt2x00lib_config_antenna(rt2x00dev, new_ant); +} + +static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup new_ant; + int rssi_curr; + int rssi_old; + + memcpy(&new_ant, &ant->active, sizeof(new_ant)); + + /* + * Get current RSSI value along with the historical value, + * after that update the history with the current value. + */ + rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev); + rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev); + rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr); + + /* + * Legacy driver indicates that we should swap antenna's + * when the difference in RSSI is greater that 5. This + * also should be done when the RSSI was actually better + * then the previous sample. + * When the difference exceeds the threshold we should + * sample the rssi from the other antenna to make a valid + * comparison between the 2 antennas. + */ + if (abs(rssi_curr - rssi_old) < 5) + return; + + ant->flags |= ANTENNA_MODE_SAMPLE; + + if (ant->flags & ANTENNA_RX_DIVERSITY) + new_ant.rx = (new_ant.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; + + if (ant->flags & ANTENNA_TX_DIVERSITY) + new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; + + rt2x00lib_config_antenna(rt2x00dev, new_ant); +} + +static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev) +{ + struct link_ant *ant = &rt2x00dev->link.ant; + + /* + * Determine if software diversity is enabled for + * either the TX or RX antenna (or both). + */ + if (!(ant->flags & ANTENNA_RX_DIVERSITY) && + !(ant->flags & ANTENNA_TX_DIVERSITY)) { + ant->flags = 0; + return true; + } + + /* + * If we have only sampled the data over the last period + * we should now harvest the data. Otherwise just evaluate + * the data. The latter should only be performed once + * every 2 seconds. + */ + if (ant->flags & ANTENNA_MODE_SAMPLE) { + rt2x00lib_antenna_diversity_sample(rt2x00dev); + return true; + } else if (rt2x00dev->link.count & 1) { + rt2x00lib_antenna_diversity_eval(rt2x00dev); + return true; + } + + return false; +} + +void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct rxdone_entry_desc *rxdesc) +{ + struct link *link = &rt2x00dev->link; + struct link_qual *qual = &rt2x00dev->link.qual; + struct link_ant *ant = &rt2x00dev->link.ant; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + /* + * No need to update the stats for !=STA interfaces + */ + if (!rt2x00dev->intf_sta_count) + return; + + /* + * Frame was received successfully since non-succesfull + * frames would have been dropped by the hardware. + */ + qual->rx_success++; + + /* + * We are only interested in quality statistics from + * beacons which came from the BSS which we are + * associated with. + */ + if (!ieee80211_is_beacon(hdr->frame_control) || + !(rxdesc->dev_flags & RXDONE_MY_BSS)) + return; + + /* + * Update global RSSI + */ + ewma_add(&link->avg_rssi, -rxdesc->rssi); + + /* + * Update antenna RSSI + */ + ewma_add(&ant->rssi_ant, -rxdesc->rssi); +} + +void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) +{ + struct link *link = &rt2x00dev->link; + + /* + * Link tuning should only be performed when + * an active sta interface exists. AP interfaces + * don't need link tuning and monitor mode interfaces + * should never have to work with link tuners. + */ + if (!rt2x00dev->intf_sta_count) + return; + + /** + * While scanning, link tuning is disabled. By default + * the most sensitive settings will be used to make sure + * that all beacons and probe responses will be received + * during the scan. + */ + if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) + return; + + rt2x00link_reset_tuner(rt2x00dev, false); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); +} + +void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) +{ + cancel_delayed_work_sync(&rt2x00dev->link.work); +} + +void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna) +{ + struct link_qual *qual = &rt2x00dev->link.qual; + u8 vgc_level = qual->vgc_level_reg; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + /* + * Reset link information. + * Both the currently active vgc level as well as + * the link tuner counter should be reset. Resetting + * the counter is important for devices where the + * device should only perform link tuning during the + * first minute after being enabled. + */ + rt2x00dev->link.count = 0; + memset(qual, 0, sizeof(*qual)); + ewma_init(&rt2x00dev->link.avg_rssi, RT2X00_EWMA_FACTOR, + RT2X00_EWMA_WEIGHT); + + /* + * Restore the VGC level as stored in the registers, + * the driver can use this to determine if the register + * must be updated during reset or not. + */ + qual->vgc_level_reg = vgc_level; + + /* + * Reset the link tuner. + */ + rt2x00dev->ops->lib->reset_tuner(rt2x00dev, qual); + + if (antenna) + rt2x00link_antenna_reset(rt2x00dev); +} + +static void rt2x00link_reset_qual(struct rt2x00_dev *rt2x00dev) +{ + struct link_qual *qual = &rt2x00dev->link.qual; + + qual->rx_success = 0; + qual->rx_failed = 0; + qual->tx_success = 0; + qual->tx_failed = 0; +} + +static void rt2x00link_tuner(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.work.work); + struct link *link = &rt2x00dev->link; + struct link_qual *qual = &rt2x00dev->link.qual; + + /* + * When the radio is shutting down we should + * immediately cease all link tuning. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) || + test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) + return; + + /* + * Update statistics. + */ + rt2x00dev->ops->lib->link_stats(rt2x00dev, qual); + rt2x00dev->low_level_stats.dot11FCSErrorCount += qual->rx_failed; + + /* + * Update quality RSSI for link tuning, + * when we have received some frames and we managed to + * collect the RSSI data we could use this. Otherwise we + * must fallback to the default RSSI value. + */ + if (!qual->rx_success) + qual->rssi = DEFAULT_RSSI; + else + qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi); + + /* + * Check if link tuning is supported by the hardware, some hardware + * do not support link tuning at all, while other devices can disable + * the feature from the EEPROM. + */ + if (rt2x00_has_cap_link_tuning(rt2x00dev)) + rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); + + /* + * Send a signal to the led to update the led signal strength. + */ + rt2x00leds_led_quality(rt2x00dev, qual->rssi); + + /* + * Evaluate antenna setup, make this the last step when + * rt2x00lib_antenna_diversity made changes the quality + * statistics will be reset. + */ + if (rt2x00lib_antenna_diversity(rt2x00dev)) + rt2x00link_reset_qual(rt2x00dev); + + /* + * Increase tuner counter, and reschedule the next link tuner run. + */ + link->count++; + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); +} + +void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev) +{ + struct link *link = &rt2x00dev->link; + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + rt2x00dev->ops->lib->watchdog) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->watchdog_work, + WATCHDOG_INTERVAL); +} + +void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev) +{ + cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work); +} + +static void rt2x00link_watchdog(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.watchdog_work.work); + struct link *link = &rt2x00dev->link; + + /* + * When the radio is shutting down we should + * immediately cease the watchdog monitoring. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + rt2x00dev->ops->lib->watchdog(rt2x00dev); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->watchdog_work, + WATCHDOG_INTERVAL); +} + +void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev) +{ + struct link *link = &rt2x00dev->link; + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + rt2x00dev->ops->lib->gain_calibration) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->agc_work, + AGC_INTERVAL); +} + +void rt2x00link_start_vcocal(struct rt2x00_dev *rt2x00dev) +{ + struct link *link = &rt2x00dev->link; + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && + rt2x00dev->ops->lib->vco_calibration) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->vco_work, + VCO_INTERVAL); +} + +void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev) +{ + cancel_delayed_work_sync(&rt2x00dev->link.agc_work); +} + +void rt2x00link_stop_vcocal(struct rt2x00_dev *rt2x00dev) +{ + cancel_delayed_work_sync(&rt2x00dev->link.vco_work); +} + +static void rt2x00link_agc(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.agc_work.work); + struct link *link = &rt2x00dev->link; + + /* + * When the radio is shutting down we should + * immediately cease the watchdog monitoring. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + rt2x00dev->ops->lib->gain_calibration(rt2x00dev); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->agc_work, + AGC_INTERVAL); +} + +static void rt2x00link_vcocal(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.vco_work.work); + struct link *link = &rt2x00dev->link; + + /* + * When the radio is shutting down we should + * immediately cease the VCO calibration. + */ + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return; + + rt2x00dev->ops->lib->vco_calibration(rt2x00dev); + + if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->vco_work, + VCO_INTERVAL); +} + +void rt2x00link_register(struct rt2x00_dev *rt2x00dev) +{ + INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc); + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) + INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal); + INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog); + INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); +} diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index e3f15e518c7..004dff9b962 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -30,51 +28,83 @@ #include "rt2x00lib.h" static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring, - struct sk_buff *frag_skb, - struct ieee80211_tx_control *control) + struct data_queue *queue, + struct sk_buff *frag_skb) { + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(frag_skb); + struct ieee80211_tx_info *rts_info; struct sk_buff *skb; - int size; + unsigned int data_length; + int retval = 0; - if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) - size = sizeof(struct ieee80211_cts); + if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + data_length = sizeof(struct ieee80211_cts); else - size = sizeof(struct ieee80211_rts); + data_length = sizeof(struct ieee80211_rts); - skb = dev_alloc_skb(size + rt2x00dev->hw->extra_tx_headroom); - if (!skb) { - WARNING(rt2x00dev, "Failed to create RTS/CTS frame.\n"); - return NETDEV_TX_BUSY; + skb = dev_alloc_skb(data_length + rt2x00dev->hw->extra_tx_headroom); + if (unlikely(!skb)) { + rt2x00_warn(rt2x00dev, "Failed to create RTS/CTS frame\n"); + return -ENOMEM; } skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); - skb_put(skb, size); + skb_put(skb, data_length); - if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) - ieee80211_ctstoself_get(rt2x00dev->hw, rt2x00dev->interface.id, - frag_skb->data, frag_skb->len, control, + /* + * Copy TX information over from original frame to + * RTS/CTS frame. Note that we set the no encryption flag + * since we don't want this frame to be encrypted. + * RTS frames should be acked, while CTS-to-self frames + * should not. The ready for TX flag is cleared to prevent + * it being automatically send when the descriptor is + * written to the hardware. + */ + memcpy(skb->cb, frag_skb->cb, sizeof(skb->cb)); + rts_info = IEEE80211_SKB_CB(skb); + rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_RTS_CTS; + rts_info->control.rates[0].flags &= ~IEEE80211_TX_RC_USE_CTS_PROTECT; + + if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + rts_info->flags |= IEEE80211_TX_CTL_NO_ACK; + else + rts_info->flags &= ~IEEE80211_TX_CTL_NO_ACK; + + /* Disable hardware encryption */ + rts_info->control.hw_key = NULL; + + /* + * RTS/CTS frame should use the length of the frame plus any + * encryption overhead that will be added by the hardware. + */ + data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb); + + if (tx_info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) + ieee80211_ctstoself_get(rt2x00dev->hw, tx_info->control.vif, + frag_skb->data, data_length, tx_info, (struct ieee80211_cts *)(skb->data)); else - ieee80211_rts_get(rt2x00dev->hw, rt2x00dev->interface.id, - frag_skb->data, frag_skb->len, control, + ieee80211_rts_get(rt2x00dev->hw, tx_info->control.vif, + frag_skb->data, data_length, tx_info, (struct ieee80211_rts *)(skb->data)); - if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) { - WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); - return NETDEV_TX_BUSY; + retval = rt2x00queue_write_tx_frame(queue, skb, NULL, true); + if (retval) { + dev_kfree_skb_any(skb); + rt2x00_warn(rt2x00dev, "Failed to send RTS/CTS frame\n"); } - return NETDEV_TX_OK; + return retval; } -int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_tx_control *control) +void rt2x00mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; - struct data_ring *ring; - u16 frame_control; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + enum data_queue_qid qid = skb_get_queue_mapping(skb); + struct data_queue *queue = NULL; /* * Mac80211 might be calling this function while we are trying @@ -82,57 +112,64 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb, * Note that we can only stop the TX queues inside the TX path * due to possible race conditions in mac80211. */ - if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) { - ieee80211_stop_queues(hw); - return NETDEV_TX_OK; - } + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + goto exit_free_skb; /* - * Determine which ring to put packet on. + * Use the ATIM queue if appropriate and present. */ - ring = rt2x00lib_get_ring(rt2x00dev, control->queue); - if (unlikely(!ring)) { - ERROR(rt2x00dev, - "Attempt to send packet over invalid queue %d.\n" - "Please file bug report to %s.\n", - control->queue, DRV_PROJECT); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && + test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) + qid = QID_ATIM; + + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + if (unlikely(!queue)) { + rt2x00_err(rt2x00dev, + "Attempt to send packet over invalid queue %d\n" + "Please file bug report to %s\n", qid, DRV_PROJECT); + goto exit_free_skb; } /* - * If CTS/RTS is required. and this frame is not CTS or RTS, - * create and queue that frame first. But make sure we have - * at least enough entries available to send this CTS/RTS - * frame as well as the data frame. + * If CTS/RTS is required. create and queue that frame first. + * Make sure we have at least enough entries available to send + * this CTS/RTS frame as well as the data frame. + * Note that when the driver has set the set_rts_threshold() + * callback function it doesn't need software generation of + * either RTS or CTS-to-self frame and handles everything + * inside the hardware. */ - frame_control = le16_to_cpu(ieee80211hdr->frame_control); - if (!is_rts_frame(frame_control) && !is_cts_frame(frame_control) && - (control->flags & (IEEE80211_TXCTL_USE_RTS_CTS | - IEEE80211_TXCTL_USE_CTS_PROTECT))) { - if (rt2x00_ring_free(ring) <= 1) { - ieee80211_stop_queue(rt2x00dev->hw, control->queue); - return NETDEV_TX_BUSY; - } - - if (rt2x00mac_tx_rts_cts(rt2x00dev, ring, skb, control)) { - ieee80211_stop_queue(rt2x00dev->hw, control->queue); - return NETDEV_TX_BUSY; - } - } - - if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) { - ieee80211_stop_queue(rt2x00dev->hw, control->queue); - return NETDEV_TX_BUSY; + if (!rt2x00dev->ops->hw->set_rts_threshold && + (tx_info->control.rates[0].flags & (IEEE80211_TX_RC_USE_RTS_CTS | + IEEE80211_TX_RC_USE_CTS_PROTECT))) { + if (rt2x00queue_available(queue) <= 1) + goto exit_fail; + + if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb)) + goto exit_fail; } - if (rt2x00_ring_full(ring)) - ieee80211_stop_queue(rt2x00dev->hw, control->queue); - - if (rt2x00dev->ops->lib->kick_tx_queue) - rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, control->queue); + if (unlikely(rt2x00queue_write_tx_frame(queue, skb, control->sta, false))) + goto exit_fail; - return NETDEV_TX_OK; + /* + * Pausing queue has to be serialized with rt2x00lib_txdone(). Note + * we should not use spin_lock_bh variant as bottom halve was already + * disabled before ieee80211_xmit() call. + */ + spin_lock(&queue->tx_lock); + if (rt2x00queue_threshold(queue)) + rt2x00queue_pause_queue(queue); + spin_unlock(&queue->tx_lock); + + return; + + exit_fail: + spin_lock(&queue->tx_lock); + rt2x00queue_pause_queue(queue); + spin_unlock(&queue->tx_lock); + exit_free_skb: + ieee80211_free_txskb(hw, skb); } EXPORT_SYMBOL_GPL(rt2x00mac_tx); @@ -140,7 +177,7 @@ int rt2x00mac_start(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; - if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; return rt2x00lib_start(rt2x00dev); @@ -151,7 +188,7 @@ void rt2x00mac_stop(struct ieee80211_hw *hw) { struct rt2x00_dev *rt2x00dev = hw->priv; - if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return; rt2x00lib_stop(rt2x00dev); @@ -159,158 +196,393 @@ void rt2x00mac_stop(struct ieee80211_hw *hw) EXPORT_SYMBOL_GPL(rt2x00mac_stop); int rt2x00mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct interface *intf = &rt2x00dev->interface; + struct rt2x00_intf *intf = vif_to_intf(vif); + struct data_queue *queue = rt2x00dev->bcn; + struct queue_entry *entry = NULL; + unsigned int i; - /* FIXME: Beaconing is broken in rt2x00. */ - if (conf->type == IEEE80211_IF_TYPE_IBSS || - conf->type == IEEE80211_IF_TYPE_AP) { - ERROR(rt2x00dev, - "rt2x00 does not support Adhoc or Master mode"); - return -EOPNOTSUPP; - } + /* + * Don't allow interfaces to be added + * the device has disappeared. + */ + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || + !test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) + return -ENODEV; /* - * Don't allow interfaces to be added while - * either the device has disappeared or when - * another interface is already present. + * Loop through all beacon queues to find a free + * entry. Since there are as much beacon entries + * as the maximum interfaces, this search shouldn't + * fail. */ - if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags) || - is_interface_present(intf)) + for (i = 0; i < queue->limit; i++) { + entry = &queue->entries[i]; + if (!test_and_set_bit(ENTRY_BCN_ASSIGNED, &entry->flags)) + break; + } + + if (unlikely(i == queue->limit)) return -ENOBUFS; - intf->id = conf->vif; - intf->type = conf->type; - if (conf->type == IEEE80211_IF_TYPE_AP) - memcpy(&intf->bssid, conf->mac_addr, ETH_ALEN); - memcpy(&intf->mac, conf->mac_addr, ETH_ALEN); + /* + * We are now absolutely sure the interface can be created, + * increase interface count and start initialization. + */ + + if (vif->type == NL80211_IFTYPE_AP) + rt2x00dev->intf_ap_count++; + else + rt2x00dev->intf_sta_count++; + + mutex_init(&intf->beacon_skb_mutex); + intf->beacon = entry; /* - * The MAC adddress must be configured after the device + * The MAC address must be configured after the device * has been initialized. Otherwise the device can reset * the MAC registers. + * The BSSID address must only be configured in AP mode, + * however we should not send an empty BSSID address for + * STA interfaces at this time, since this can cause + * invalid behavior in the device. + */ + rt2x00lib_config_intf(rt2x00dev, intf, vif->type, + vif->addr, NULL); + + /* + * Some filters depend on the current working mode. We can force + * an update during the next configure_filter() run by mac80211 by + * resetting the current packet_filter state. */ - rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); - rt2x00lib_config_type(rt2x00dev, conf->type); + rt2x00dev->packet_filter = 0; return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_add_interface); void rt2x00mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct interface *intf = &rt2x00dev->interface; + struct rt2x00_intf *intf = vif_to_intf(vif); /* * Don't allow interfaces to be remove while * either the device has disappeared or when * no interface is present. */ - if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags) || - !is_interface_present(intf)) + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) || + (vif->type == NL80211_IFTYPE_AP && !rt2x00dev->intf_ap_count) || + (vif->type != NL80211_IFTYPE_AP && !rt2x00dev->intf_sta_count)) return; - intf->id = 0; - intf->type = IEEE80211_IF_TYPE_INVALID; - memset(&intf->bssid, 0x00, ETH_ALEN); - memset(&intf->mac, 0x00, ETH_ALEN); + if (vif->type == NL80211_IFTYPE_AP) + rt2x00dev->intf_ap_count--; + else + rt2x00dev->intf_sta_count--; + + /* + * Release beacon entry so it is available for + * new interfaces again. + */ + clear_bit(ENTRY_BCN_ASSIGNED, &intf->beacon->flags); /* * Make sure the bssid and mac address registers * are cleared to prevent false ACKing of frames. */ - rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); - rt2x00lib_config_bssid(rt2x00dev, intf->bssid); - rt2x00lib_config_type(rt2x00dev, intf->type); + rt2x00lib_config_intf(rt2x00dev, intf, + NL80211_IFTYPE_UNSPECIFIED, NULL, NULL); } EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface); -int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed) { struct rt2x00_dev *rt2x00dev = hw->priv; + struct ieee80211_conf *conf = &hw->conf; /* - * Mac80211 might be calling this function while we are trying + * mac80211 might be calling this function while we are trying * to remove the device or perhaps suspending it. */ - if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; /* - * Check if we need to disable the radio, - * if this is not the case, at least the RX must be disabled. + * Some configuration parameters (e.g. channel and antenna values) can + * only be set when the radio is enabled, but do require the RX to + * be off. During this period we should keep link tuning enabled, + * if for any reason the link tuner must be reset, this will be + * handled by rt2x00lib_config(). */ - if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) { - if (!conf->radio_enabled) - rt2x00lib_disable_radio(rt2x00dev); - else - rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); - } + rt2x00queue_stop_queue(rt2x00dev->rx); - rt2x00lib_config(rt2x00dev, conf, 0); + /* + * When we've just turned on the radio, we want to reprogram + * everything to ensure a consistent state + */ + rt2x00lib_config(rt2x00dev, conf, changed); /* - * Reenable RX only if the radio should be on. + * After the radio has been enabled we need to configure + * the antenna to the default settings. rt2x00lib_config_antenna() + * should determine if any action should be taken based on + * checking if diversity has been enabled or no antenna changes + * have been made since the last configuration change. */ - if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); - else if (conf->radio_enabled) - return rt2x00lib_enable_radio(rt2x00dev); + rt2x00lib_config_antenna(rt2x00dev, rt2x00dev->default_ant); + + /* Turn RX back on */ + rt2x00queue_start_queue(rt2x00dev->rx); return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_config); -int rt2x00mac_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) +void rt2x00mac_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct interface *intf = &rt2x00dev->interface; - int status; /* - * Mac80211 might be calling this function while we are trying - * to remove the device or perhaps suspending it. + * Mask off any flags we are going to ignore + * from the total_flags field. */ - if (!test_bit(DEVICE_PRESENT, &rt2x00dev->flags)) + *total_flags &= + FIF_ALLMULTI | + FIF_FCSFAIL | + FIF_PLCPFAIL | + FIF_CONTROL | + FIF_PSPOLL | + FIF_OTHER_BSS | + FIF_PROMISC_IN_BSS; + + /* + * Apply some rules to the filters: + * - Some filters imply different filters to be set. + * - Some things we can't filter out at all. + * - Multicast filter seems to kill broadcast traffic so never use it. + */ + *total_flags |= FIF_ALLMULTI; + if (*total_flags & FIF_OTHER_BSS || + *total_flags & FIF_PROMISC_IN_BSS) + *total_flags |= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS; + + /* + * If the device has a single filter for all control frames, + * FIF_CONTROL and FIF_PSPOLL flags imply each other. + * And if the device has more than one filter for control frames + * of different types, but has no a separate filter for PS Poll frames, + * FIF_CONTROL flag implies FIF_PSPOLL. + */ + if (!rt2x00_has_cap_control_filters(rt2x00dev)) { + if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL) + *total_flags |= FIF_CONTROL | FIF_PSPOLL; + } + if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) { + if (*total_flags & FIF_CONTROL) + *total_flags |= FIF_PSPOLL; + } + + /* + * Check if there is any work left for us. + */ + if (rt2x00dev->packet_filter == *total_flags) + return; + rt2x00dev->packet_filter = *total_flags; + + rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); +} +EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); + +static void rt2x00mac_set_tim_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct rt2x00_intf *intf = vif_to_intf(vif); + + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC && + vif->type != NL80211_IFTYPE_MESH_POINT && + vif->type != NL80211_IFTYPE_WDS) + return; + + set_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags); +} + +int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return 0; + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00mac_set_tim_iter, rt2x00dev); + + /* queue work to upodate the beacon template */ + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_set_tim); + +#ifdef CONFIG_RT2X00_LIB_CRYPTO +static void memcpy_tkip(struct rt2x00lib_crypto *crypto, u8 *key, u8 key_len) +{ + if (key_len > NL80211_TKIP_DATA_OFFSET_ENCR_KEY) + memcpy(crypto->key, + &key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY], + sizeof(crypto->key)); + + if (key_len > NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY) + memcpy(crypto->tx_mic, + &key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], + sizeof(crypto->tx_mic)); + + if (key_len > NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY) + memcpy(crypto->rx_mic, + &key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], + sizeof(crypto->rx_mic)); +} + +int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + int (*set_key) (struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key); + struct rt2x00lib_crypto crypto; + static const u8 bcast_addr[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; + struct rt2x00_sta *sta_priv = NULL; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; + + if (!rt2x00_has_cap_hw_crypto(rt2x00dev)) + return -EOPNOTSUPP; + /* - * If the given type does not match the configured type, - * there has been a problem. + * To support IBSS RSN, don't program group keys in IBSS, the + * hardware will then not attempt to decrypt the frames. */ - if (conf->type != intf->type) - return -EINVAL; + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + if (key->keylen > 32) + return -ENOSPC; + + memset(&crypto, 0, sizeof(crypto)); + + crypto.bssidx = rt2x00lib_get_bssidx(rt2x00dev, vif); + crypto.cipher = rt2x00crypto_key_to_cipher(key); + if (crypto.cipher == CIPHER_NONE) + return -EOPNOTSUPP; + if (crypto.cipher == CIPHER_TKIP && rt2x00_is_usb(rt2x00dev)) + return -EOPNOTSUPP; + + crypto.cmd = cmd; + + if (sta) { + crypto.address = sta->addr; + sta_priv = sta_to_rt2x00_sta(sta); + crypto.wcid = sta_priv->wcid; + } else + crypto.address = bcast_addr; + + if (crypto.cipher == CIPHER_TKIP) + memcpy_tkip(&crypto, &key->key[0], key->keylen); + else + memcpy(crypto.key, &key->key[0], key->keylen); + /* + * Each BSS has a maximum of 4 shared keys. + * Shared key index values: + * 0) BSS0 key0 + * 1) BSS0 key1 + * ... + * 4) BSS1 key0 + * ... + * 8) BSS2 key0 + * ... + * Both pairwise as shared key indeces are determined by + * driver. This is required because the hardware requires + * keys to be assigned in correct order (When key 1 is + * provided but key 0 is not, then the key is not found + * by the hardware during RX). + */ + if (cmd == SET_KEY) + key->hw_key_idx = 0; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + set_key = rt2x00dev->ops->lib->config_pairwise_key; + else + set_key = rt2x00dev->ops->lib->config_shared_key; + + if (!set_key) + return -EOPNOTSUPP; + + return set_key(rt2x00dev, &crypto, key); +} +EXPORT_SYMBOL_GPL(rt2x00mac_set_key); +#endif /* CONFIG_RT2X00_LIB_CRYPTO */ + +int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); /* - * If the interface does not work in master mode, - * then the bssid value in the interface structure - * should now be set. + * If there's no space left in the device table store + * -1 as wcid but tell mac80211 everything went ok. */ - if (conf->type != IEEE80211_IF_TYPE_AP) - memcpy(&intf->bssid, conf->bssid, ETH_ALEN); - rt2x00lib_config_bssid(rt2x00dev, intf->bssid); + if (rt2x00dev->ops->lib->sta_add(rt2x00dev, vif, sta)) + sta_priv->wcid = -1; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_sta_add); + +int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta); /* - * We only need to initialize the beacon when master mode is enabled. + * If we never sent the STA to the device no need to clean it up. */ - if (conf->type != IEEE80211_IF_TYPE_AP || !conf->beacon) + if (sta_priv->wcid < 0) return 0; - status = rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, - conf->beacon, - conf->beacon_control); - if (status) - dev_kfree_skb(conf->beacon); + return rt2x00dev->ops->lib->sta_remove(rt2x00dev, sta_priv->wcid); +} +EXPORT_SYMBOL_GPL(rt2x00mac_sta_remove); + +void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + set_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); + rt2x00link_stop_tuner(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_start); - return status; +void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + clear_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); + rt2x00link_start_tuner(rt2x00dev); } -EXPORT_SYMBOL_GPL(rt2x00mac_config_interface); +EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_complete); int rt2x00mac_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) @@ -328,92 +600,262 @@ int rt2x00mac_get_stats(struct ieee80211_hw *hw, } EXPORT_SYMBOL_GPL(rt2x00mac_get_stats); -int rt2x00mac_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - unsigned int i; - - for (i = 0; i < hw->queues; i++) - memcpy(&stats->data[i], &rt2x00dev->tx[i].stats, - sizeof(rt2x00dev->tx[i].stats)); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00mac_get_tx_stats); - void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes) { struct rt2x00_dev *rt2x00dev = hw->priv; - int short_preamble; - int ack_timeout; - int ack_consume_time; - int difs; - int preamble; + struct rt2x00_intf *intf = vif_to_intf(vif); /* - * We only support changing preamble mode. + * mac80211 might be calling this function while we are trying + * to remove the device or perhaps suspending it. */ - if (!(changes & BSS_CHANGED_ERP_PREAMBLE)) + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return; - short_preamble = bss_conf->use_short_preamble; - preamble = bss_conf->use_short_preamble ? - SHORT_PREAMBLE : PREAMBLE; + /* + * Update the BSSID. + */ + if (changes & BSS_CHANGED_BSSID) + rt2x00lib_config_intf(rt2x00dev, intf, vif->type, NULL, + bss_conf->bssid); - difs = (hw->conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME) ? - SHORT_DIFS : DIFS; - ack_timeout = difs + PLCP + preamble + get_duration(ACK_SIZE, 10); + /* + * Start/stop beaconing. + */ + if (changes & BSS_CHANGED_BEACON_ENABLED) { + if (!bss_conf->enable_beacon && intf->enable_beacon) { + rt2x00dev->intf_beaconing--; + intf->enable_beacon = false; + /* + * Clear beacon in the H/W for this vif. This is needed + * to disable beaconing on this particular interface + * and keep it running on other interfaces. + */ + rt2x00queue_clear_beacon(rt2x00dev, vif); + + if (rt2x00dev->intf_beaconing == 0) { + /* + * Last beaconing interface disabled + * -> stop beacon queue. + */ + mutex_lock(&intf->beacon_skb_mutex); + rt2x00queue_stop_queue(rt2x00dev->bcn); + mutex_unlock(&intf->beacon_skb_mutex); + } + } else if (bss_conf->enable_beacon && !intf->enable_beacon) { + rt2x00dev->intf_beaconing++; + intf->enable_beacon = true; + /* + * Upload beacon to the H/W. This is only required on + * USB devices. PCI devices fetch beacons periodically. + */ + if (rt2x00_is_usb(rt2x00dev)) + rt2x00queue_update_beacon(rt2x00dev, vif); + + if (rt2x00dev->intf_beaconing == 1) { + /* + * First beaconing interface enabled + * -> start beacon queue. + */ + mutex_lock(&intf->beacon_skb_mutex); + rt2x00queue_start_queue(rt2x00dev->bcn); + mutex_unlock(&intf->beacon_skb_mutex); + } + } + } - ack_consume_time = SIFS + PLCP + preamble + get_duration(ACK_SIZE, 10); + /* + * When the association status has changed we must reset the link + * tuner counter. This is because some drivers determine if they + * should perform link tuning based on the number of seconds + * while associated or not associated. + */ + if (changes & BSS_CHANGED_ASSOC) { + rt2x00dev->link.count = 0; - if (short_preamble) - __set_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); - else - __clear_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); + if (bss_conf->assoc) + rt2x00dev->intf_associated++; + else + rt2x00dev->intf_associated--; + + rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated); + + clear_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags); + } + + /* + * Check for access point which do not support 802.11e . We have to + * generate data frames sequence number in S/W for such AP, because + * of H/W bug. + */ + if (changes & BSS_CHANGED_QOS && !bss_conf->qos) + set_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags); - rt2x00dev->ops->lib->config_preamble(rt2x00dev, short_preamble, - ack_timeout, ack_consume_time); + /* + * When the erp information has changed, we should perform + * additional configuration steps. For all other changes we are done. + */ + if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | + BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_BEACON_INT | BSS_CHANGED_HT)) + rt2x00lib_config_erp(rt2x00dev, intf, bss_conf, changes); } EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed); -int rt2x00mac_conf_tx(struct ieee80211_hw *hw, int queue, +int rt2x00mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; - struct data_ring *ring; + struct data_queue *queue; - ring = rt2x00lib_get_ring(rt2x00dev, queue); - if (unlikely(!ring)) + queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); + if (unlikely(!queue)) return -EINVAL; /* * The passed variables are stored as real value ((2^n)-1). * Ralink registers require to know the bit number 'n'. */ - if (params->cw_min) - ring->tx_params.cw_min = fls(params->cw_min); + if (params->cw_min > 0) + queue->cw_min = fls(params->cw_min); else - ring->tx_params.cw_min = 5; /* cw_min: 2^5 = 32. */ + queue->cw_min = 5; /* cw_min: 2^5 = 32. */ - if (params->cw_max) - ring->tx_params.cw_max = fls(params->cw_max); + if (params->cw_max > 0) + queue->cw_max = fls(params->cw_max); else - ring->tx_params.cw_max = 10; /* cw_min: 2^10 = 1024. */ + queue->cw_max = 10; /* cw_min: 2^10 = 1024. */ - if (params->aifs) - ring->tx_params.aifs = params->aifs; - else - ring->tx_params.aifs = 2; + queue->aifs = params->aifs; + queue->txop = params->txop; - INFO(rt2x00dev, - "Configured TX ring %d - CWmin: %d, CWmax: %d, Aifs: %d.\n", - queue, ring->tx_params.cw_min, ring->tx_params.cw_max, - ring->tx_params.aifs); + rt2x00_dbg(rt2x00dev, + "Configured TX queue %d - CWmin: %d, CWmax: %d, Aifs: %d, TXop: %d\n", + queue_idx, queue->cw_min, queue->cw_max, queue->aifs, + queue->txop); return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_conf_tx); + +void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + bool active = !!rt2x00dev->ops->lib->rfkill_poll(rt2x00dev); + + wiphy_rfkill_set_hw_state(hw->wiphy, !active); +} +EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll); + +void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_flush_queue(queue, drop); +} +EXPORT_SYMBOL_GPL(rt2x00mac_flush); + +int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup *def = &rt2x00dev->default_ant; + struct antenna_setup setup; + + // The antenna value is not supposed to be 0, + // or exceed the maximum number of antenna's. + if (!tx_ant || (tx_ant & ~3) || !rx_ant || (rx_ant & ~3)) + return -EINVAL; + + // When the client tried to configure the antenna to or from + // diversity mode, we must reset the default antenna as well + // as that controls the diversity switch. + if (ant->flags & ANTENNA_TX_DIVERSITY && tx_ant != 3) + ant->flags &= ~ANTENNA_TX_DIVERSITY; + if (ant->flags & ANTENNA_RX_DIVERSITY && rx_ant != 3) + ant->flags &= ~ANTENNA_RX_DIVERSITY; + + // If diversity is being enabled, check if we need hardware + // or software diversity. In the latter case, reset the value, + // and make sure we update the antenna flags to have the + // link tuner pick up the diversity tuning. + if (tx_ant == 3 && def->tx == ANTENNA_SW_DIVERSITY) { + tx_ant = ANTENNA_SW_DIVERSITY; + ant->flags |= ANTENNA_TX_DIVERSITY; + } + + if (rx_ant == 3 && def->rx == ANTENNA_SW_DIVERSITY) { + rx_ant = ANTENNA_SW_DIVERSITY; + ant->flags |= ANTENNA_RX_DIVERSITY; + } + + setup.tx = tx_ant; + setup.rx = rx_ant; + + rt2x00lib_config_antenna(rt2x00dev, setup); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_set_antenna); + +int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct link_ant *ant = &rt2x00dev->link.ant; + struct antenna_setup *active = &rt2x00dev->link.ant.active; + + // When software diversity is active, we must report this to the + // client and not the current active antenna state. + if (ant->flags & ANTENNA_TX_DIVERSITY) + *tx_ant = ANTENNA_HW_DIVERSITY; + else + *tx_ant = active->tx; + + if (ant->flags & ANTENNA_RX_DIVERSITY) + *rx_ant = ANTENNA_HW_DIVERSITY; + else + *rx_ant = active->rx; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_get_antenna); + +void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, + u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) { + *tx += queue->length; + *tx_max += queue->limit; + } + + *rx = rt2x00dev->rx->length; + *rx_max = rt2x00dev->rx->limit; +} +EXPORT_SYMBOL_GPL(rt2x00mac_get_ringparam); + +bool rt2x00mac_tx_frames_pending(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) { + if (!rt2x00queue_empty(queue)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(rt2x00mac_tx_frames_pending); diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.c b/drivers/net/wireless/rt2x00/rt2x00mmio.c new file mode 100644 index 00000000000..6f236ea180a --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00mmio.c @@ -0,0 +1,214 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00mmio + Abstract: rt2x00 generic mmio device routines. + */ + +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "rt2x00.h" +#include "rt2x00mmio.h" + +/* + * Register access. + */ +int rt2x00mmio_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg) +{ + unsigned int i; + + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return 0; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00mmio_register_read(rt2x00dev, offset, reg); + if (!rt2x00_get_field32(*reg, field)) + return 1; + udelay(REGISTER_BUSY_DELAY); + } + + printk_once(KERN_ERR "%s() Indirect register access failed: " + "offset=0x%.08x, value=0x%.08x\n", __func__, offset, *reg); + *reg = ~0; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mmio_regbusy_read); + +bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue = rt2x00dev->rx; + struct queue_entry *entry; + struct queue_entry_priv_mmio *entry_priv; + struct skb_frame_desc *skbdesc; + int max_rx = 16; + + while (--max_rx) { + entry = rt2x00queue_get_entry(queue, Q_INDEX); + entry_priv = entry->priv_data; + + if (rt2x00dev->ops->lib->get_entry_state(entry)) + break; + + /* + * Fill in desc fields of the skb descriptor + */ + skbdesc = get_skb_frame_desc(entry->skb); + skbdesc->desc = entry_priv->desc; + skbdesc->desc_len = entry->queue->desc_size; + + /* + * DMA is already done, notify rt2x00lib that + * it finished successfully. + */ + rt2x00lib_dmastart(entry); + rt2x00lib_dmadone(entry); + + /* + * Send the frame to rt2x00lib for further processing. + */ + rt2x00lib_rxdone(entry, GFP_ATOMIC); + } + + return !max_rx; +} +EXPORT_SYMBOL_GPL(rt2x00mmio_rxdone); + +void rt2x00mmio_flush_queue(struct data_queue *queue, bool drop) +{ + unsigned int i; + + for (i = 0; !rt2x00queue_empty(queue) && i < 10; i++) + msleep(10); +} +EXPORT_SYMBOL_GPL(rt2x00mmio_flush_queue); + +/* + * Device initialization handlers. + */ +static int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev, + struct data_queue *queue) +{ + struct queue_entry_priv_mmio *entry_priv; + void *addr; + dma_addr_t dma; + unsigned int i; + + /* + * Allocate DMA memory for descriptor and buffer. + */ + addr = dma_alloc_coherent(rt2x00dev->dev, + queue->limit * queue->desc_size, + &dma, GFP_KERNEL); + if (!addr) + return -ENOMEM; + + memset(addr, 0, queue->limit * queue->desc_size); + + /* + * Initialize all queue entries to contain valid addresses. + */ + for (i = 0; i < queue->limit; i++) { + entry_priv = queue->entries[i].priv_data; + entry_priv->desc = addr + i * queue->desc_size; + entry_priv->desc_dma = dma + i * queue->desc_size; + } + + return 0; +} + +static void rt2x00mmio_free_queue_dma(struct rt2x00_dev *rt2x00dev, + struct data_queue *queue) +{ + struct queue_entry_priv_mmio *entry_priv = + queue->entries[0].priv_data; + + if (entry_priv->desc) + dma_free_coherent(rt2x00dev->dev, + queue->limit * queue->desc_size, + entry_priv->desc, entry_priv->desc_dma); + entry_priv->desc = NULL; +} + +int rt2x00mmio_initialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + int status; + + /* + * Allocate DMA + */ + queue_for_each(rt2x00dev, queue) { + status = rt2x00mmio_alloc_queue_dma(rt2x00dev, queue); + if (status) + goto exit; + } + + /* + * Register interrupt handler. + */ + status = request_irq(rt2x00dev->irq, + rt2x00dev->ops->lib->irq_handler, + IRQF_SHARED, rt2x00dev->name, rt2x00dev); + if (status) { + rt2x00_err(rt2x00dev, "IRQ %d allocation failed (error %d)\n", + rt2x00dev->irq, status); + goto exit; + } + + return 0; + +exit: + queue_for_each(rt2x00dev, queue) + rt2x00mmio_free_queue_dma(rt2x00dev, queue); + + return status; +} +EXPORT_SYMBOL_GPL(rt2x00mmio_initialize); + +void rt2x00mmio_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + /* + * Free irq line. + */ + free_irq(rt2x00dev->irq, rt2x00dev); + + /* + * Free DMA + */ + queue_for_each(rt2x00dev, queue) + rt2x00mmio_free_queue_dma(rt2x00dev, queue); +} +EXPORT_SYMBOL_GPL(rt2x00mmio_uninitialize); + +/* + * rt2x00mmio module information. + */ +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 mmio library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.h b/drivers/net/wireless/rt2x00/rt2x00mmio.h new file mode 100644 index 00000000000..701c3127efb --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00mmio.h @@ -0,0 +1,117 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00mmio + Abstract: Data structures for the rt2x00mmio module. + */ + +#ifndef RT2X00MMIO_H +#define RT2X00MMIO_H + +#include <linux/io.h> + +/* + * Register access. + */ +static inline void rt2x00mmio_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + *value = readl(rt2x00dev->csr.base + offset); +} + +static inline void rt2x00mmio_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length) +{ + memcpy_fromio(value, rt2x00dev->csr.base + offset, length); +} + +static inline void rt2x00mmio_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + writel(value, rt2x00dev->csr.base + offset); +} + +static inline void rt2x00mmio_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, + const u32 length) +{ + __iowrite32_copy(rt2x00dev->csr.base + offset, value, length >> 2); +} + +/** + * rt2x00mmio_regbusy_read - Read from register with busy check + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @field: Field to check if register is busy + * @reg: Pointer to where register contents should be stored + * + * This function will read the given register, and checks if the + * register is busy. If it is, it will sleep for a couple of + * microseconds before reading the register again. If the register + * is not read after a certain timeout, this function will return + * FALSE. + */ +int rt2x00mmio_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg); + +/** + * struct queue_entry_priv_mmio: Per entry PCI specific information + * + * @desc: Pointer to device descriptor + * @desc_dma: DMA pointer to &desc. + * @data: Pointer to device's entry memory. + * @data_dma: DMA pointer to &data. + */ +struct queue_entry_priv_mmio { + __le32 *desc; + dma_addr_t desc_dma; +}; + +/** + * rt2x00mmio_rxdone - Handle RX done events + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * + * Returns true if there are still rx frames pending and false if all + * pending rx frames were processed. + */ +bool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev); + +/** + * rt2x00mmio_flush_queue - Flush data queue + * @queue: Data queue to stop + * @drop: True to drop all pending frames. + * + * This will wait for a maximum of 100ms, waiting for the queues + * to become empty. + */ +void rt2x00mmio_flush_queue(struct data_queue *queue, bool drop); + +/* + * Device initialization handlers. + */ +int rt2x00mmio_initialize(struct rt2x00_dev *rt2x00dev); +void rt2x00mmio_uninitialize(struct rt2x00_dev *rt2x00dev); + +#endif /* RT2X00MMIO_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index 804a9980055..d93db4b0371 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -27,316 +25,12 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/slab.h> #include "rt2x00.h" #include "rt2x00pci.h" /* - * Beacon handlers. - */ -int rt2x00pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_tx_control *control) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct skb_desc *desc; - struct data_ring *ring; - struct data_entry *entry; - - /* - * Just in case mac80211 doesn't set this correctly, - * but we need this queue set for the descriptor - * initialization. - */ - control->queue = IEEE80211_TX_QUEUE_BEACON; - ring = rt2x00lib_get_ring(rt2x00dev, control->queue); - entry = rt2x00_get_data_entry(ring); - - /* - * Fill in skb descriptor - */ - desc = get_skb_desc(skb); - desc->desc_len = ring->desc_size; - desc->data_len = skb->len; - desc->desc = entry->priv; - desc->data = skb->data; - desc->ring = ring; - desc->entry = entry; - - memcpy(entry->data_addr, skb->data, skb->len); - rt2x00lib_write_tx_desc(rt2x00dev, skb, control); - - /* - * Enable beacon generation. - */ - rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, control->queue); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00pci_beacon_update); - -/* - * TX data handlers. - */ -int rt2x00pci_write_tx_data(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring, struct sk_buff *skb, - struct ieee80211_tx_control *control) -{ - struct data_entry *entry = rt2x00_get_data_entry(ring); - __le32 *txd = entry->priv; - struct skb_desc *desc; - u32 word; - - if (rt2x00_ring_full(ring)) - return -EINVAL; - - rt2x00_desc_read(txd, 0, &word); - - if (rt2x00_get_field32(word, TXD_ENTRY_OWNER_NIC) || - rt2x00_get_field32(word, TXD_ENTRY_VALID)) { - ERROR(rt2x00dev, - "Arrived at non-free entry in the non-full queue %d.\n" - "Please file bug report to %s.\n", - control->queue, DRV_PROJECT); - return -EINVAL; - } - - /* - * Fill in skb descriptor - */ - desc = get_skb_desc(skb); - desc->desc_len = ring->desc_size; - desc->data_len = skb->len; - desc->desc = entry->priv; - desc->data = skb->data; - desc->ring = ring; - desc->entry = entry; - - memcpy(entry->data_addr, skb->data, skb->len); - rt2x00lib_write_tx_desc(rt2x00dev, skb, control); - - rt2x00_ring_index_inc(ring); - - return 0; -} -EXPORT_SYMBOL_GPL(rt2x00pci_write_tx_data); - -/* - * TX/RX data handlers. - */ -void rt2x00pci_rxdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_ring *ring = rt2x00dev->rx; - struct data_entry *entry; - struct sk_buff *skb; - struct ieee80211_hdr *hdr; - struct skb_desc *skbdesc; - struct rxdata_entry_desc desc; - int header_size; - __le32 *rxd; - int align; - u32 word; - - while (1) { - entry = rt2x00_get_data_entry(ring); - rxd = entry->priv; - rt2x00_desc_read(rxd, 0, &word); - - if (rt2x00_get_field32(word, RXD_ENTRY_OWNER_NIC)) - break; - - memset(&desc, 0, sizeof(desc)); - rt2x00dev->ops->lib->fill_rxdone(entry, &desc); - - hdr = (struct ieee80211_hdr *)entry->data_addr; - header_size = - ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); - - /* - * The data behind the ieee80211 header must be - * aligned on a 4 byte boundary. - */ - align = header_size % 4; - - /* - * Allocate the sk_buffer, initialize it and copy - * all data into it. - */ - skb = dev_alloc_skb(desc.size + align); - if (!skb) - return; - - skb_reserve(skb, align); - memcpy(skb_put(skb, desc.size), entry->data_addr, desc.size); - - /* - * Fill in skb descriptor - */ - skbdesc = get_skb_desc(skb); - skbdesc->desc_len = entry->ring->desc_size; - skbdesc->data_len = skb->len; - skbdesc->desc = entry->priv; - skbdesc->data = skb->data; - skbdesc->ring = ring; - skbdesc->entry = entry; - - /* - * Send the frame to rt2x00lib for further processing. - */ - rt2x00lib_rxdone(entry, skb, &desc); - - if (test_bit(DEVICE_ENABLED_RADIO, &ring->rt2x00dev->flags)) { - rt2x00_set_field32(&word, RXD_ENTRY_OWNER_NIC, 1); - rt2x00_desc_write(rxd, 0, word); - } - - rt2x00_ring_index_inc(ring); - } -} -EXPORT_SYMBOL_GPL(rt2x00pci_rxdone); - -void rt2x00pci_txdone(struct rt2x00_dev *rt2x00dev, struct data_entry *entry, - const int tx_status, const int retry) -{ - u32 word; - - rt2x00lib_txdone(entry, tx_status, retry); - - /* - * Make this entry available for reuse. - */ - entry->flags = 0; - - rt2x00_desc_read(entry->priv, 0, &word); - rt2x00_set_field32(&word, TXD_ENTRY_OWNER_NIC, 0); - rt2x00_set_field32(&word, TXD_ENTRY_VALID, 0); - rt2x00_desc_write(entry->priv, 0, word); - - rt2x00_ring_index_done_inc(entry->ring); - - /* - * If the data ring was full before the txdone handler - * we must make sure the packet queue in the mac80211 stack - * is reenabled when the txdone handler has finished. - */ - if (!rt2x00_ring_full(entry->ring)) - ieee80211_wake_queue(rt2x00dev->hw, - entry->tx_status.control.queue); - -} -EXPORT_SYMBOL_GPL(rt2x00pci_txdone); - -/* - * Device initialization handlers. - */ -#define priv_offset(__ring, __i) \ -({ \ - ring->data_addr + (i * ring->desc_size); \ -}) - -#define data_addr_offset(__ring, __i) \ -({ \ - (__ring)->data_addr + \ - ((__ring)->stats.limit * (__ring)->desc_size) + \ - ((__i) * (__ring)->data_size); \ -}) - -#define data_dma_offset(__ring, __i) \ -({ \ - (__ring)->data_dma + \ - ((__ring)->stats.limit * (__ring)->desc_size) + \ - ((__i) * (__ring)->data_size); \ -}) - -static int rt2x00pci_alloc_dma(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring) -{ - unsigned int i; - - /* - * Allocate DMA memory for descriptor and buffer. - */ - ring->data_addr = pci_alloc_consistent(rt2x00dev_pci(rt2x00dev), - rt2x00_get_ring_size(ring), - &ring->data_dma); - if (!ring->data_addr) - return -ENOMEM; - - /* - * Initialize all ring entries to contain valid - * addresses. - */ - for (i = 0; i < ring->stats.limit; i++) { - ring->entry[i].priv = priv_offset(ring, i); - ring->entry[i].data_addr = data_addr_offset(ring, i); - ring->entry[i].data_dma = data_dma_offset(ring, i); - } - - return 0; -} - -static void rt2x00pci_free_dma(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring) -{ - if (ring->data_addr) - pci_free_consistent(rt2x00dev_pci(rt2x00dev), - rt2x00_get_ring_size(ring), - ring->data_addr, ring->data_dma); - ring->data_addr = NULL; -} - -int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev) -{ - struct pci_dev *pci_dev = rt2x00dev_pci(rt2x00dev); - struct data_ring *ring; - int status; - - /* - * Allocate DMA - */ - ring_for_each(rt2x00dev, ring) { - status = rt2x00pci_alloc_dma(rt2x00dev, ring); - if (status) - goto exit; - } - - /* - * Register interrupt handler. - */ - status = request_irq(pci_dev->irq, rt2x00dev->ops->lib->irq_handler, - IRQF_SHARED, pci_name(pci_dev), rt2x00dev); - if (status) { - ERROR(rt2x00dev, "IRQ %d allocation failed (error %d).\n", - pci_dev->irq, status); - return status; - } - - return 0; - -exit: - rt2x00pci_uninitialize(rt2x00dev); - - return status; -} -EXPORT_SYMBOL_GPL(rt2x00pci_initialize); - -void rt2x00pci_uninitialize(struct rt2x00_dev *rt2x00dev) -{ - struct data_ring *ring; - - /* - * Free irq line. - */ - free_irq(rt2x00dev_pci(rt2x00dev)->irq, rt2x00dev); - - /* - * Free DMA - */ - ring_for_each(rt2x00dev, ring) - rt2x00pci_free_dma(rt2x00dev, ring); -} -EXPORT_SYMBOL_GPL(rt2x00pci_uninitialize); - -/* * PCI driver handlers. */ static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) @@ -347,19 +41,18 @@ static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) kfree(rt2x00dev->eeprom); rt2x00dev->eeprom = NULL; - if (rt2x00dev->csr_addr) { - iounmap(rt2x00dev->csr_addr); - rt2x00dev->csr_addr = NULL; + if (rt2x00dev->csr.base) { + iounmap(rt2x00dev->csr.base); + rt2x00dev->csr.base = NULL; } } static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) { - struct pci_dev *pci_dev = rt2x00dev_pci(rt2x00dev); + struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); - rt2x00dev->csr_addr = ioremap(pci_resource_start(pci_dev, 0), - pci_resource_len(pci_dev, 0)); - if (!rt2x00dev->csr_addr) + rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0); + if (!rt2x00dev->csr.base) goto exit; rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); @@ -373,62 +66,76 @@ static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) return 0; exit: - ERROR_PROBE("Failed to allocate registers.\n"); + rt2x00_probe_err("Failed to allocate registers\n"); rt2x00pci_free_reg(rt2x00dev); return -ENOMEM; } -int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) { - struct rt2x00_ops *ops = (struct rt2x00_ops *)id->driver_data; struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; int retval; + u16 chip; - retval = pci_request_regions(pci_dev, pci_name(pci_dev)); + retval = pci_enable_device(pci_dev); if (retval) { - ERROR_PROBE("PCI request regions failed.\n"); + rt2x00_probe_err("Enable device failed\n"); return retval; } - retval = pci_enable_device(pci_dev); + retval = pci_request_regions(pci_dev, pci_name(pci_dev)); if (retval) { - ERROR_PROBE("Enable device failed.\n"); - goto exit_release_regions; + rt2x00_probe_err("PCI request regions failed\n"); + goto exit_disable_device; } pci_set_master(pci_dev); if (pci_set_mwi(pci_dev)) - ERROR_PROBE("MWI not available.\n"); + rt2x00_probe_err("MWI not available\n"); - if (pci_set_dma_mask(pci_dev, DMA_64BIT_MASK) && - pci_set_dma_mask(pci_dev, DMA_32BIT_MASK)) { - ERROR_PROBE("PCI DMA not supported.\n"); + if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { + rt2x00_probe_err("PCI DMA not supported\n"); retval = -EIO; - goto exit_disable_device; + goto exit_release_regions; } hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { - ERROR_PROBE("Failed to allocate hardware.\n"); + rt2x00_probe_err("Failed to allocate hardware\n"); retval = -ENOMEM; - goto exit_disable_device; + goto exit_release_regions; } pci_set_drvdata(pci_dev, hw); rt2x00dev = hw->priv; - rt2x00dev->dev = pci_dev; + rt2x00dev->dev = &pci_dev->dev; rt2x00dev->ops = ops; rt2x00dev->hw = hw; + rt2x00dev->irq = pci_dev->irq; + rt2x00dev->name = ops->name; + + if (pci_is_pcie(pci_dev)) + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); + else + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); retval = rt2x00pci_alloc_reg(rt2x00dev); if (retval) goto exit_free_device; + /* + * Because rt3290 chip use different efuse offset to read efuse data. + * So before read efuse it need to indicate it is the + * rt3290 or not. + */ + pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip); + rt2x00dev->chip.rt = chip; + retval = rt2x00lib_probe_dev(rt2x00dev); if (retval) goto exit_free_reg; @@ -441,14 +148,11 @@ exit_free_reg: exit_free_device: ieee80211_free_hw(hw); -exit_disable_device: - if (retval != -EBUSY) - pci_disable_device(pci_dev); - exit_release_regions: pci_release_regions(pci_dev); - pci_set_drvdata(pci_dev, NULL); +exit_disable_device: + pci_disable_device(pci_dev); return retval; } @@ -469,7 +173,6 @@ void rt2x00pci_remove(struct pci_dev *pci_dev) /* * Free the PCI device data. */ - pci_set_drvdata(pci_dev, NULL); pci_disable_device(pci_dev); pci_release_regions(pci_dev); } @@ -486,8 +189,6 @@ int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state) if (retval) return retval; - rt2x00pci_free_reg(rt2x00dev); - pci_save_state(pci_dev); pci_disable_device(pci_dev); return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); @@ -498,29 +199,15 @@ int rt2x00pci_resume(struct pci_dev *pci_dev) { struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); struct rt2x00_dev *rt2x00dev = hw->priv; - int retval; if (pci_set_power_state(pci_dev, PCI_D0) || - pci_enable_device(pci_dev) || - pci_restore_state(pci_dev)) { - ERROR(rt2x00dev, "Failed to resume device.\n"); + pci_enable_device(pci_dev)) { + rt2x00_err(rt2x00dev, "Failed to resume device\n"); return -EIO; } - retval = rt2x00pci_alloc_reg(rt2x00dev); - if (retval) - return retval; - - retval = rt2x00lib_resume(rt2x00dev); - if (retval) - goto exit_free_reg; - - return 0; - -exit_free_reg: - rt2x00pci_free_reg(rt2x00dev); - - return retval; + pci_restore_state(pci_dev); + return rt2x00lib_resume(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00pci_resume); #endif /* CONFIG_PM */ @@ -530,5 +217,5 @@ EXPORT_SYMBOL_GPL(rt2x00pci_resume); */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("rt2x00 library"); +MODULE_DESCRIPTION("rt2x00 pci library"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/rt2x00/rt2x00pci.h index 2d1eb8144da..bc0ca5f58f3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.h +++ b/drivers/net/wireless/rt2x00/rt2x00pci.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -27,6 +25,7 @@ #define RT2X00PCI_H #include <linux/io.h> +#include <linux/pci.h> /* * This variable should be used with the @@ -35,88 +34,9 @@ #define PCI_DEVICE_DATA(__ops) .driver_data = (kernel_ulong_t)(__ops) /* - * Register defines. - * Some registers require multiple attempts before success, - * in those cases REGISTER_BUSY_COUNT attempts should be - * taken with a REGISTER_BUSY_DELAY interval. - */ -#define REGISTER_BUSY_COUNT 5 -#define REGISTER_BUSY_DELAY 100 - -/* - * Descriptor availability flags. - * All PCI device descriptors have these 2 flags - * with the exact same definition. - * By storing them here we can use them inside rt2x00pci - * for some simple entry availability checking. - */ -#define TXD_ENTRY_OWNER_NIC FIELD32(0x00000001) -#define TXD_ENTRY_VALID FIELD32(0x00000002) -#define RXD_ENTRY_OWNER_NIC FIELD32(0x00000001) - -/* - * Register access. - */ -static inline void rt2x00pci_register_read(struct rt2x00_dev *rt2x00dev, - const unsigned long offset, - u32 *value) -{ - *value = readl(rt2x00dev->csr_addr + offset); -} - -static inline void -rt2x00pci_register_multiread(struct rt2x00_dev *rt2x00dev, - const unsigned long offset, - void *value, const u16 length) -{ - memcpy_fromio(value, rt2x00dev->csr_addr + offset, length); -} - -static inline void rt2x00pci_register_write(struct rt2x00_dev *rt2x00dev, - const unsigned long offset, - u32 value) -{ - writel(value, rt2x00dev->csr_addr + offset); -} - -static inline void -rt2x00pci_register_multiwrite(struct rt2x00_dev *rt2x00dev, - const unsigned long offset, - void *value, const u16 length) -{ - memcpy_toio(rt2x00dev->csr_addr + offset, value, length); -} - -/* - * Beacon handlers. - */ -int rt2x00pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_tx_control *control); - -/* - * TX data handlers. - */ -int rt2x00pci_write_tx_data(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring, struct sk_buff *skb, - struct ieee80211_tx_control *control); - -/* - * RX/TX data handlers. - */ -void rt2x00pci_rxdone(struct rt2x00_dev *rt2x00dev); -void rt2x00pci_txdone(struct rt2x00_dev *rt2x00dev, struct data_entry *entry, - const int tx_status, const int retry); - -/* - * Device initialization handlers. - */ -int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev); -void rt2x00pci_uninitialize(struct rt2x00_dev *rt2x00dev); - -/* * PCI driver handlers. */ -int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id); +int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops); void rt2x00pci_remove(struct pci_dev *pci_dev); #ifdef CONFIG_PM int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state); diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c new file mode 100644 index 00000000000..5642ccceca7 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -0,0 +1,1333 @@ +/* + Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> + Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 queue specific routines. + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> + +#include "rt2x00.h" +#include "rt2x00lib.h" + +struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp) +{ + struct data_queue *queue = entry->queue; + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct sk_buff *skb; + struct skb_frame_desc *skbdesc; + unsigned int frame_size; + unsigned int head_size = 0; + unsigned int tail_size = 0; + + /* + * The frame size includes descriptor size, because the + * hardware directly receive the frame into the skbuffer. + */ + frame_size = queue->data_size + queue->desc_size + queue->winfo_size; + + /* + * The payload should be aligned to a 4-byte boundary, + * this means we need at least 3 bytes for moving the frame + * into the correct offset. + */ + head_size = 4; + + /* + * For IV/EIV/ICV assembly we must make sure there is + * at least 8 bytes bytes available in headroom for IV/EIV + * and 8 bytes for ICV data as tailroon. + */ + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) { + head_size += 8; + tail_size += 8; + } + + /* + * Allocate skbuffer. + */ + skb = __dev_alloc_skb(frame_size + head_size + tail_size, gfp); + if (!skb) + return NULL; + + /* + * Make sure we not have a frame with the requested bytes + * available in the head and tail. + */ + skb_reserve(skb, head_size); + skb_put(skb, frame_size); + + /* + * Populate skbdesc. + */ + skbdesc = get_skb_frame_desc(skb); + memset(skbdesc, 0, sizeof(*skbdesc)); + skbdesc->entry = entry; + + if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) { + dma_addr_t skb_dma; + + skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(rt2x00dev->dev, skb_dma))) { + dev_kfree_skb_any(skb); + return NULL; + } + + skbdesc->skb_dma = skb_dma; + skbdesc->flags |= SKBDESC_DMA_MAPPED_RX; + } + + return skb; +} + +int rt2x00queue_map_txskb(struct queue_entry *entry) +{ + struct device *dev = entry->queue->rt2x00dev->dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + + skbdesc->skb_dma = + dma_map_single(dev, entry->skb->data, entry->skb->len, DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(dev, skbdesc->skb_dma))) + return -ENOMEM; + + skbdesc->flags |= SKBDESC_DMA_MAPPED_TX; + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00queue_map_txskb); + +void rt2x00queue_unmap_skb(struct queue_entry *entry) +{ + struct device *dev = entry->queue->rt2x00dev->dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + + if (skbdesc->flags & SKBDESC_DMA_MAPPED_RX) { + dma_unmap_single(dev, skbdesc->skb_dma, entry->skb->len, + DMA_FROM_DEVICE); + skbdesc->flags &= ~SKBDESC_DMA_MAPPED_RX; + } else if (skbdesc->flags & SKBDESC_DMA_MAPPED_TX) { + dma_unmap_single(dev, skbdesc->skb_dma, entry->skb->len, + DMA_TO_DEVICE); + skbdesc->flags &= ~SKBDESC_DMA_MAPPED_TX; + } +} +EXPORT_SYMBOL_GPL(rt2x00queue_unmap_skb); + +void rt2x00queue_free_skb(struct queue_entry *entry) +{ + if (!entry->skb) + return; + + rt2x00queue_unmap_skb(entry); + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; +} + +void rt2x00queue_align_frame(struct sk_buff *skb) +{ + unsigned int frame_length = skb->len; + unsigned int align = ALIGN_SIZE(skb, 0); + + if (!align) + return; + + skb_push(skb, align); + memmove(skb->data, skb->data + align, frame_length); + skb_trim(skb, frame_length); +} + +void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length) +{ + unsigned int payload_length = skb->len - header_length; + unsigned int header_align = ALIGN_SIZE(skb, 0); + unsigned int payload_align = ALIGN_SIZE(skb, header_length); + unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0; + + /* + * Adjust the header alignment if the payload needs to be moved more + * than the header. + */ + if (payload_align > header_align) + header_align += 4; + + /* There is nothing to do if no alignment is needed */ + if (!header_align) + return; + + /* Reserve the amount of space needed in front of the frame */ + skb_push(skb, header_align); + + /* + * Move the header. + */ + memmove(skb->data, skb->data + header_align, header_length); + + /* Move the payload, if present and if required */ + if (payload_length && payload_align) + memmove(skb->data + header_length + l2pad, + skb->data + header_length + l2pad + payload_align, + payload_length); + + /* Trim the skb to the correct size */ + skb_trim(skb, header_length + l2pad + payload_length); +} + +void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length) +{ + /* + * L2 padding is only present if the skb contains more than just the + * IEEE 802.11 header. + */ + unsigned int l2pad = (skb->len > header_length) ? + L2PAD_SIZE(header_length) : 0; + + if (!l2pad) + return; + + memmove(skb->data + l2pad, skb->data, header_length); + skb_pull(skb, l2pad); +} + +static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif); + u16 seqno; + + if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)) + return; + + __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); + + if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags)) { + /* + * rt2800 has a H/W (or F/W) bug, device incorrectly increase + * seqno on retransmited data (non-QOS) frames. To workaround + * the problem let's generate seqno in software if QOS is + * disabled. + */ + if (test_bit(CONFIG_QOS_DISABLED, &rt2x00dev->flags)) + __clear_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); + else + /* H/W will generate sequence number */ + return; + } + + /* + * The hardware is not able to insert a sequence number. Assign a + * software generated one here. + * + * This is wrong because beacons are not getting sequence + * numbers assigned properly. + * + * A secondary problem exists for drivers that cannot toggle + * sequence counting per-frame, since those will override the + * sequence counter given by mac80211. + */ + if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) + seqno = atomic_add_return(0x10, &intf->seqno); + else + seqno = atomic_read(&intf->seqno); + + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seqno); +} + +static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc, + const struct rt2x00_rate *hwrate) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; + unsigned int data_length; + unsigned int duration; + unsigned int residual; + + /* + * Determine with what IFS priority this frame should be send. + * Set ifs to IFS_SIFS when the this is not the first fragment, + * or this fragment came after RTS/CTS. + */ + if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) + txdesc->u.plcp.ifs = IFS_BACKOFF; + else + txdesc->u.plcp.ifs = IFS_SIFS; + + /* Data length + CRC + Crypto overhead (IV/EIV/ICV/MIC) */ + data_length = skb->len + 4; + data_length += rt2x00crypto_tx_overhead(rt2x00dev, skb); + + /* + * PLCP setup + * Length calculation depends on OFDM/CCK rate. + */ + txdesc->u.plcp.signal = hwrate->plcp; + txdesc->u.plcp.service = 0x04; + + if (hwrate->flags & DEV_RATE_OFDM) { + txdesc->u.plcp.length_high = (data_length >> 6) & 0x3f; + txdesc->u.plcp.length_low = data_length & 0x3f; + } else { + /* + * Convert length to microseconds. + */ + residual = GET_DURATION_RES(data_length, hwrate->bitrate); + duration = GET_DURATION(data_length, hwrate->bitrate); + + if (residual != 0) { + duration++; + + /* + * Check if we need to set the Length Extension + */ + if (hwrate->bitrate == 110 && residual <= 30) + txdesc->u.plcp.service |= 0x80; + } + + txdesc->u.plcp.length_high = (duration >> 8) & 0xff; + txdesc->u.plcp.length_low = duration & 0xff; + + /* + * When preamble is enabled we should set the + * preamble bit for the signal. + */ + if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + txdesc->u.plcp.signal |= 0x08; + } +} + +static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc, + struct ieee80211_sta *sta, + const struct rt2x00_rate *hwrate) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct rt2x00_sta *sta_priv = NULL; + + if (sta) { + txdesc->u.ht.mpdu_density = + sta->ht_cap.ampdu_density; + + sta_priv = sta_to_rt2x00_sta(sta); + txdesc->u.ht.wcid = sta_priv->wcid; + } + + /* + * If IEEE80211_TX_RC_MCS is set txrate->idx just contains the + * mcs rate to be used + */ + if (txrate->flags & IEEE80211_TX_RC_MCS) { + txdesc->u.ht.mcs = txrate->idx; + + /* + * MIMO PS should be set to 1 for STA's using dynamic SM PS + * when using more then one tx stream (>MCS7). + */ + if (sta && txdesc->u.ht.mcs > 7 && + sta->smps_mode == IEEE80211_SMPS_DYNAMIC) + __set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags); + } else { + txdesc->u.ht.mcs = rt2x00_get_rate_mcs(hwrate->mcs); + if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + txdesc->u.ht.mcs |= 0x08; + } + + if (test_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags)) { + if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) + txdesc->u.ht.txop = TXOP_SIFS; + else + txdesc->u.ht.txop = TXOP_BACKOFF; + + /* Left zero on all other settings. */ + return; + } + + txdesc->u.ht.ba_size = 7; /* FIXME: What value is needed? */ + + /* + * Only one STBC stream is supported for now. + */ + if (tx_info->flags & IEEE80211_TX_CTL_STBC) + txdesc->u.ht.stbc = 1; + + /* + * This frame is eligible for an AMPDU, however, don't aggregate + * frames that are intended to probe a specific tx rate. + */ + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU && + !(tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) + __set_bit(ENTRY_TXD_HT_AMPDU, &txdesc->flags); + + /* + * Set 40Mhz mode if necessary (for legacy rates this will + * duplicate the frame to both channels). + */ + if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH || + txrate->flags & IEEE80211_TX_RC_DUP_DATA) + __set_bit(ENTRY_TXD_HT_BW_40, &txdesc->flags); + if (txrate->flags & IEEE80211_TX_RC_SHORT_GI) + __set_bit(ENTRY_TXD_HT_SHORT_GI, &txdesc->flags); + + /* + * Determine IFS values + * - Use TXOP_BACKOFF for management frames except beacons + * - Use TXOP_SIFS for fragment bursts + * - Use TXOP_HTTXOP for everything else + * + * Note: rt2800 devices won't use CTS protection (if used) + * for frames not transmitted with TXOP_HTTXOP + */ + if (ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_beacon(hdr->frame_control)) + txdesc->u.ht.txop = TXOP_BACKOFF; + else if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) + txdesc->u.ht.txop = TXOP_SIFS; + else + txdesc->u.ht.txop = TXOP_HTTXOP; +} + +static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, + struct sk_buff *skb, + struct txentry_desc *txdesc, + struct ieee80211_sta *sta) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; + struct ieee80211_rate *rate; + const struct rt2x00_rate *hwrate = NULL; + + memset(txdesc, 0, sizeof(*txdesc)); + + /* + * Header and frame information. + */ + txdesc->length = skb->len; + txdesc->header_length = ieee80211_get_hdrlen_from_skb(skb); + + /* + * Check whether this frame is to be acked. + */ + if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) + __set_bit(ENTRY_TXD_ACK, &txdesc->flags); + + /* + * Check if this is a RTS/CTS frame + */ + if (ieee80211_is_rts(hdr->frame_control) || + ieee80211_is_cts(hdr->frame_control)) { + __set_bit(ENTRY_TXD_BURST, &txdesc->flags); + if (ieee80211_is_rts(hdr->frame_control)) + __set_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags); + else + __set_bit(ENTRY_TXD_CTS_FRAME, &txdesc->flags); + if (tx_info->control.rts_cts_rate_idx >= 0) + rate = + ieee80211_get_rts_cts_rate(rt2x00dev->hw, tx_info); + } + + /* + * Determine retry information. + */ + txdesc->retry_limit = tx_info->control.rates[0].count - 1; + if (txdesc->retry_limit >= rt2x00dev->long_retry) + __set_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags); + + /* + * Check if more fragments are pending + */ + if (ieee80211_has_morefrags(hdr->frame_control)) { + __set_bit(ENTRY_TXD_BURST, &txdesc->flags); + __set_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags); + } + + /* + * Check if more frames (!= fragments) are pending + */ + if (tx_info->flags & IEEE80211_TX_CTL_MORE_FRAMES) + __set_bit(ENTRY_TXD_BURST, &txdesc->flags); + + /* + * Beacons and probe responses require the tsf timestamp + * to be inserted into the frame. + */ + if (ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) + __set_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags); + + if ((tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) && + !test_bit(ENTRY_TXD_RTS_FRAME, &txdesc->flags)) + __set_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags); + + /* + * Determine rate modulation. + */ + if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD) + txdesc->rate_mode = RATE_MODE_HT_GREENFIELD; + else if (txrate->flags & IEEE80211_TX_RC_MCS) + txdesc->rate_mode = RATE_MODE_HT_MIX; + else { + rate = ieee80211_get_tx_rate(rt2x00dev->hw, tx_info); + hwrate = rt2x00_get_rate(rate->hw_value); + if (hwrate->flags & DEV_RATE_OFDM) + txdesc->rate_mode = RATE_MODE_OFDM; + else + txdesc->rate_mode = RATE_MODE_CCK; + } + + /* + * Apply TX descriptor handling by components + */ + rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc); + rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc); + + if (test_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags)) + rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc, + sta, hwrate); + else + rt2x00queue_create_tx_descriptor_plcp(rt2x00dev, skb, txdesc, + hwrate); +} + +static int rt2x00queue_write_tx_data(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + + /* + * This should not happen, we already checked the entry + * was ours. When the hardware disagrees there has been + * a queue corruption! + */ + if (unlikely(rt2x00dev->ops->lib->get_entry_state && + rt2x00dev->ops->lib->get_entry_state(entry))) { + rt2x00_err(rt2x00dev, + "Corrupt queue %d, accessing entry which is not ours\n" + "Please file bug report to %s\n", + entry->queue->qid, DRV_PROJECT); + return -EINVAL; + } + + /* + * Add the requested extra tx headroom in front of the skb. + */ + skb_push(entry->skb, rt2x00dev->extra_tx_headroom); + memset(entry->skb->data, 0, rt2x00dev->extra_tx_headroom); + + /* + * Call the driver's write_tx_data function, if it exists. + */ + if (rt2x00dev->ops->lib->write_tx_data) + rt2x00dev->ops->lib->write_tx_data(entry, txdesc); + + /* + * Map the skb to DMA. + */ + if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags) && + rt2x00queue_map_txskb(entry)) + return -ENOMEM; + + return 0; +} + +static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct data_queue *queue = entry->queue; + + queue->rt2x00dev->ops->lib->write_tx_desc(entry, txdesc); + + /* + * All processing on the frame has been completed, this means + * it is now ready to be dumped to userspace through debugfs. + */ + rt2x00debug_dump_frame(queue->rt2x00dev, DUMP_FRAME_TX, entry->skb); +} + +static void rt2x00queue_kick_tx_queue(struct data_queue *queue, + struct txentry_desc *txdesc) +{ + /* + * Check if we need to kick the queue, there are however a few rules + * 1) Don't kick unless this is the last in frame in a burst. + * When the burst flag is set, this frame is always followed + * by another frame which in some way are related to eachother. + * This is true for fragments, RTS or CTS-to-self frames. + * 2) Rule 1 can be broken when the available entries + * in the queue are less then a certain threshold. + */ + if (rt2x00queue_threshold(queue) || + !test_bit(ENTRY_TXD_BURST, &txdesc->flags)) + queue->rt2x00dev->ops->lib->kick_queue(queue); +} + +static void rt2x00queue_bar_check(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_bar *bar = (void *) (entry->skb->data + + rt2x00dev->extra_tx_headroom); + struct rt2x00_bar_list_entry *bar_entry; + + if (likely(!ieee80211_is_back_req(bar->frame_control))) + return; + + bar_entry = kmalloc(sizeof(*bar_entry), GFP_ATOMIC); + + /* + * If the alloc fails we still send the BAR out but just don't track + * it in our bar list. And as a result we will report it to mac80211 + * back as failed. + */ + if (!bar_entry) + return; + + bar_entry->entry = entry; + bar_entry->block_acked = 0; + + /* + * Copy the relevant parts of the 802.11 BAR into out check list + * such that we can use RCU for less-overhead in the RX path since + * sending BARs and processing the according BlockAck should be + * the exception. + */ + memcpy(bar_entry->ra, bar->ra, sizeof(bar->ra)); + memcpy(bar_entry->ta, bar->ta, sizeof(bar->ta)); + bar_entry->control = bar->control; + bar_entry->start_seq_num = bar->start_seq_num; + + /* + * Insert BAR into our BAR check list. + */ + spin_lock_bh(&rt2x00dev->bar_list_lock); + list_add_tail_rcu(&bar_entry->list, &rt2x00dev->bar_list); + spin_unlock_bh(&rt2x00dev->bar_list_lock); +} + +int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, + struct ieee80211_sta *sta, bool local) +{ + struct ieee80211_tx_info *tx_info; + struct queue_entry *entry; + struct txentry_desc txdesc; + struct skb_frame_desc *skbdesc; + u8 rate_idx, rate_flags; + int ret = 0; + + /* + * Copy all TX descriptor information into txdesc, + * after that we are free to use the skb->cb array + * for our information. + */ + rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, sta); + + /* + * All information is retrieved from the skb->cb array, + * now we should claim ownership of the driver part of that + * array, preserving the bitrate index and flags. + */ + tx_info = IEEE80211_SKB_CB(skb); + rate_idx = tx_info->control.rates[0].idx; + rate_flags = tx_info->control.rates[0].flags; + skbdesc = get_skb_frame_desc(skb); + memset(skbdesc, 0, sizeof(*skbdesc)); + skbdesc->tx_rate_idx = rate_idx; + skbdesc->tx_rate_flags = rate_flags; + + if (local) + skbdesc->flags |= SKBDESC_NOT_MAC80211; + + /* + * When hardware encryption is supported, and this frame + * is to be encrypted, we should strip the IV/EIV data from + * the frame so we can provide it to the driver separately. + */ + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) { + if (test_bit(REQUIRE_COPY_IV, &queue->rt2x00dev->cap_flags)) + rt2x00crypto_tx_copy_iv(skb, &txdesc); + else + rt2x00crypto_tx_remove_iv(skb, &txdesc); + } + + /* + * When DMA allocation is required we should guarantee to the + * driver that the DMA is aligned to a 4-byte boundary. + * However some drivers require L2 padding to pad the payload + * rather then the header. This could be a requirement for + * PCI and USB devices, while header alignment only is valid + * for PCI devices. + */ + if (test_bit(REQUIRE_L2PAD, &queue->rt2x00dev->cap_flags)) + rt2x00queue_insert_l2pad(skb, txdesc.header_length); + else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags)) + rt2x00queue_align_frame(skb); + + /* + * That function must be called with bh disabled. + */ + spin_lock(&queue->tx_lock); + + if (unlikely(rt2x00queue_full(queue))) { + rt2x00_err(queue->rt2x00dev, "Dropping frame due to full tx queue %d\n", + queue->qid); + ret = -ENOBUFS; + goto out; + } + + entry = rt2x00queue_get_entry(queue, Q_INDEX); + + if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, + &entry->flags))) { + rt2x00_err(queue->rt2x00dev, + "Arrived at non-free entry in the non-full queue %d\n" + "Please file bug report to %s\n", + queue->qid, DRV_PROJECT); + ret = -EINVAL; + goto out; + } + + skbdesc->entry = entry; + entry->skb = skb; + + /* + * It could be possible that the queue was corrupted and this + * call failed. Since we always return NETDEV_TX_OK to mac80211, + * this frame will simply be dropped. + */ + if (unlikely(rt2x00queue_write_tx_data(entry, &txdesc))) { + clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); + entry->skb = NULL; + ret = -EIO; + goto out; + } + + /* + * Put BlockAckReqs into our check list for driver BA processing. + */ + rt2x00queue_bar_check(entry); + + set_bit(ENTRY_DATA_PENDING, &entry->flags); + + rt2x00queue_index_inc(entry, Q_INDEX); + rt2x00queue_write_tx_descriptor(entry, &txdesc); + rt2x00queue_kick_tx_queue(queue, &txdesc); + +out: + spin_unlock(&queue->tx_lock); + return ret; +} + +int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif) +{ + struct rt2x00_intf *intf = vif_to_intf(vif); + + if (unlikely(!intf->beacon)) + return -ENOBUFS; + + mutex_lock(&intf->beacon_skb_mutex); + + /* + * Clean up the beacon skb. + */ + rt2x00queue_free_skb(intf->beacon); + + /* + * Clear beacon (single bssid devices don't need to clear the beacon + * since the beacon queue will get stopped anyway). + */ + if (rt2x00dev->ops->lib->clear_beacon) + rt2x00dev->ops->lib->clear_beacon(intf->beacon); + + mutex_unlock(&intf->beacon_skb_mutex); + + return 0; +} + +int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif) +{ + struct rt2x00_intf *intf = vif_to_intf(vif); + struct skb_frame_desc *skbdesc; + struct txentry_desc txdesc; + + if (unlikely(!intf->beacon)) + return -ENOBUFS; + + /* + * Clean up the beacon skb. + */ + rt2x00queue_free_skb(intf->beacon); + + intf->beacon->skb = ieee80211_beacon_get(rt2x00dev->hw, vif); + if (!intf->beacon->skb) + return -ENOMEM; + + /* + * Copy all TX descriptor information into txdesc, + * after that we are free to use the skb->cb array + * for our information. + */ + rt2x00queue_create_tx_descriptor(rt2x00dev, intf->beacon->skb, &txdesc, NULL); + + /* + * Fill in skb descriptor + */ + skbdesc = get_skb_frame_desc(intf->beacon->skb); + memset(skbdesc, 0, sizeof(*skbdesc)); + skbdesc->entry = intf->beacon; + + /* + * Send beacon to hardware. + */ + rt2x00dev->ops->lib->write_beacon(intf->beacon, &txdesc); + + return 0; + +} + +int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, + struct ieee80211_vif *vif) +{ + struct rt2x00_intf *intf = vif_to_intf(vif); + int ret; + + mutex_lock(&intf->beacon_skb_mutex); + ret = rt2x00queue_update_beacon_locked(rt2x00dev, vif); + mutex_unlock(&intf->beacon_skb_mutex); + + return ret; +} + +bool rt2x00queue_for_each_entry(struct data_queue *queue, + enum queue_index start, + enum queue_index end, + void *data, + bool (*fn)(struct queue_entry *entry, + void *data)) +{ + unsigned long irqflags; + unsigned int index_start; + unsigned int index_end; + unsigned int i; + + if (unlikely(start >= Q_INDEX_MAX || end >= Q_INDEX_MAX)) { + rt2x00_err(queue->rt2x00dev, + "Entry requested from invalid index range (%d - %d)\n", + start, end); + return true; + } + + /* + * Only protect the range we are going to loop over, + * if during our loop a extra entry is set to pending + * it should not be kicked during this run, since it + * is part of another TX operation. + */ + spin_lock_irqsave(&queue->index_lock, irqflags); + index_start = queue->index[start]; + index_end = queue->index[end]; + spin_unlock_irqrestore(&queue->index_lock, irqflags); + + /* + * Start from the TX done pointer, this guarantees that we will + * send out all frames in the correct order. + */ + if (index_start < index_end) { + for (i = index_start; i < index_end; i++) { + if (fn(&queue->entries[i], data)) + return true; + } + } else { + for (i = index_start; i < queue->limit; i++) { + if (fn(&queue->entries[i], data)) + return true; + } + + for (i = 0; i < index_end; i++) { + if (fn(&queue->entries[i], data)) + return true; + } + } + + return false; +} +EXPORT_SYMBOL_GPL(rt2x00queue_for_each_entry); + +struct queue_entry *rt2x00queue_get_entry(struct data_queue *queue, + enum queue_index index) +{ + struct queue_entry *entry; + unsigned long irqflags; + + if (unlikely(index >= Q_INDEX_MAX)) { + rt2x00_err(queue->rt2x00dev, "Entry requested from invalid index type (%d)\n", + index); + return NULL; + } + + spin_lock_irqsave(&queue->index_lock, irqflags); + + entry = &queue->entries[queue->index[index]]; + + spin_unlock_irqrestore(&queue->index_lock, irqflags); + + return entry; +} +EXPORT_SYMBOL_GPL(rt2x00queue_get_entry); + +void rt2x00queue_index_inc(struct queue_entry *entry, enum queue_index index) +{ + struct data_queue *queue = entry->queue; + unsigned long irqflags; + + if (unlikely(index >= Q_INDEX_MAX)) { + rt2x00_err(queue->rt2x00dev, + "Index change on invalid index type (%d)\n", index); + return; + } + + spin_lock_irqsave(&queue->index_lock, irqflags); + + queue->index[index]++; + if (queue->index[index] >= queue->limit) + queue->index[index] = 0; + + entry->last_action = jiffies; + + if (index == Q_INDEX) { + queue->length++; + } else if (index == Q_INDEX_DONE) { + queue->length--; + queue->count++; + } + + spin_unlock_irqrestore(&queue->index_lock, irqflags); +} + +static void rt2x00queue_pause_queue_nocheck(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + /* + * For TX queues, we have to disable the queue + * inside mac80211. + */ + ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid); + break; + default: + break; + } +} +void rt2x00queue_pause_queue(struct data_queue *queue) +{ + if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || + !test_bit(QUEUE_STARTED, &queue->flags) || + test_and_set_bit(QUEUE_PAUSED, &queue->flags)) + return; + + rt2x00queue_pause_queue_nocheck(queue); +} +EXPORT_SYMBOL_GPL(rt2x00queue_pause_queue); + +void rt2x00queue_unpause_queue(struct data_queue *queue) +{ + if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || + !test_bit(QUEUE_STARTED, &queue->flags) || + !test_and_clear_bit(QUEUE_PAUSED, &queue->flags)) + return; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + /* + * For TX queues, we have to enable the queue + * inside mac80211. + */ + ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid); + break; + case QID_RX: + /* + * For RX we need to kick the queue now in order to + * receive frames. + */ + queue->rt2x00dev->ops->lib->kick_queue(queue); + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2x00queue_unpause_queue); + +void rt2x00queue_start_queue(struct data_queue *queue) +{ + mutex_lock(&queue->status_lock); + + if (!test_bit(DEVICE_STATE_PRESENT, &queue->rt2x00dev->flags) || + test_and_set_bit(QUEUE_STARTED, &queue->flags)) { + mutex_unlock(&queue->status_lock); + return; + } + + set_bit(QUEUE_PAUSED, &queue->flags); + + queue->rt2x00dev->ops->lib->start_queue(queue); + + rt2x00queue_unpause_queue(queue); + + mutex_unlock(&queue->status_lock); +} +EXPORT_SYMBOL_GPL(rt2x00queue_start_queue); + +void rt2x00queue_stop_queue(struct data_queue *queue) +{ + mutex_lock(&queue->status_lock); + + if (!test_and_clear_bit(QUEUE_STARTED, &queue->flags)) { + mutex_unlock(&queue->status_lock); + return; + } + + rt2x00queue_pause_queue_nocheck(queue); + + queue->rt2x00dev->ops->lib->stop_queue(queue); + + mutex_unlock(&queue->status_lock); +} +EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue); + +void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) +{ + bool tx_queue = + (queue->qid == QID_AC_VO) || + (queue->qid == QID_AC_VI) || + (queue->qid == QID_AC_BE) || + (queue->qid == QID_AC_BK); + + + /* + * If we are not supposed to drop any pending + * frames, this means we must force a start (=kick) + * to the queue to make sure the hardware will + * start transmitting. + */ + if (!drop && tx_queue) + queue->rt2x00dev->ops->lib->kick_queue(queue); + + /* + * Check if driver supports flushing, if that is the case we can + * defer the flushing to the driver. Otherwise we must use the + * alternative which just waits for the queue to become empty. + */ + if (likely(queue->rt2x00dev->ops->lib->flush_queue)) + queue->rt2x00dev->ops->lib->flush_queue(queue, drop); + + /* + * The queue flush has failed... + */ + if (unlikely(!rt2x00queue_empty(queue))) + rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n", + queue->qid); +} +EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue); + +void rt2x00queue_start_queues(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + /* + * rt2x00queue_start_queue will call ieee80211_wake_queue + * for each queue after is has been properly initialized. + */ + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_start_queue(queue); + + rt2x00queue_start_queue(rt2x00dev->rx); +} +EXPORT_SYMBOL_GPL(rt2x00queue_start_queues); + +void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + /* + * rt2x00queue_stop_queue will call ieee80211_stop_queue + * as well, but we are completely shutting doing everything + * now, so it is much safer to stop all TX queues at once, + * and use rt2x00queue_stop_queue for cleaning up. + */ + ieee80211_stop_queues(rt2x00dev->hw); + + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_stop_queue(queue); + + rt2x00queue_stop_queue(rt2x00dev->rx); +} +EXPORT_SYMBOL_GPL(rt2x00queue_stop_queues); + +void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop) +{ + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_flush_queue(queue, drop); + + rt2x00queue_flush_queue(rt2x00dev->rx, drop); +} +EXPORT_SYMBOL_GPL(rt2x00queue_flush_queues); + +static void rt2x00queue_reset(struct data_queue *queue) +{ + unsigned long irqflags; + unsigned int i; + + spin_lock_irqsave(&queue->index_lock, irqflags); + + queue->count = 0; + queue->length = 0; + + for (i = 0; i < Q_INDEX_MAX; i++) + queue->index[i] = 0; + + spin_unlock_irqrestore(&queue->index_lock, irqflags); +} + +void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + unsigned int i; + + queue_for_each(rt2x00dev, queue) { + rt2x00queue_reset(queue); + + for (i = 0; i < queue->limit; i++) + rt2x00dev->ops->lib->clear_entry(&queue->entries[i]); + } +} + +static int rt2x00queue_alloc_entries(struct data_queue *queue) +{ + struct queue_entry *entries; + unsigned int entry_size; + unsigned int i; + + rt2x00queue_reset(queue); + + /* + * Allocate all queue entries. + */ + entry_size = sizeof(*entries) + queue->priv_size; + entries = kcalloc(queue->limit, entry_size, GFP_KERNEL); + if (!entries) + return -ENOMEM; + +#define QUEUE_ENTRY_PRIV_OFFSET(__base, __index, __limit, __esize, __psize) \ + (((char *)(__base)) + ((__limit) * (__esize)) + \ + ((__index) * (__psize))) + + for (i = 0; i < queue->limit; i++) { + entries[i].flags = 0; + entries[i].queue = queue; + entries[i].skb = NULL; + entries[i].entry_idx = i; + entries[i].priv_data = + QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit, + sizeof(*entries), queue->priv_size); + } + +#undef QUEUE_ENTRY_PRIV_OFFSET + + queue->entries = entries; + + return 0; +} + +static void rt2x00queue_free_skbs(struct data_queue *queue) +{ + unsigned int i; + + if (!queue->entries) + return; + + for (i = 0; i < queue->limit; i++) { + rt2x00queue_free_skb(&queue->entries[i]); + } +} + +static int rt2x00queue_alloc_rxskbs(struct data_queue *queue) +{ + unsigned int i; + struct sk_buff *skb; + + for (i = 0; i < queue->limit; i++) { + skb = rt2x00queue_alloc_rxskb(&queue->entries[i], GFP_KERNEL); + if (!skb) + return -ENOMEM; + queue->entries[i].skb = skb; + } + + return 0; +} + +int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + int status; + + status = rt2x00queue_alloc_entries(rt2x00dev->rx); + if (status) + goto exit; + + tx_queue_for_each(rt2x00dev, queue) { + status = rt2x00queue_alloc_entries(queue); + if (status) + goto exit; + } + + status = rt2x00queue_alloc_entries(rt2x00dev->bcn); + if (status) + goto exit; + + if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) { + status = rt2x00queue_alloc_entries(rt2x00dev->atim); + if (status) + goto exit; + } + + status = rt2x00queue_alloc_rxskbs(rt2x00dev->rx); + if (status) + goto exit; + + return 0; + +exit: + rt2x00_err(rt2x00dev, "Queue entries allocation failed\n"); + + rt2x00queue_uninitialize(rt2x00dev); + + return status; +} + +void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + rt2x00queue_free_skbs(rt2x00dev->rx); + + queue_for_each(rt2x00dev, queue) { + kfree(queue->entries); + queue->entries = NULL; + } +} + +static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, + struct data_queue *queue, enum data_queue_qid qid) +{ + mutex_init(&queue->status_lock); + spin_lock_init(&queue->tx_lock); + spin_lock_init(&queue->index_lock); + + queue->rt2x00dev = rt2x00dev; + queue->qid = qid; + queue->txop = 0; + queue->aifs = 2; + queue->cw_min = 5; + queue->cw_max = 10; + + rt2x00dev->ops->queue_init(queue); + + queue->threshold = DIV_ROUND_UP(queue->limit, 10); +} + +int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + enum data_queue_qid qid; + unsigned int req_atim = + !!test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + + /* + * We need the following queues: + * RX: 1 + * TX: ops->tx_queues + * Beacon: 1 + * Atim: 1 (if required) + */ + rt2x00dev->data_queues = 2 + rt2x00dev->ops->tx_queues + req_atim; + + queue = kcalloc(rt2x00dev->data_queues, sizeof(*queue), GFP_KERNEL); + if (!queue) { + rt2x00_err(rt2x00dev, "Queue allocation failed\n"); + return -ENOMEM; + } + + /* + * Initialize pointers + */ + rt2x00dev->rx = queue; + rt2x00dev->tx = &queue[1]; + rt2x00dev->bcn = &queue[1 + rt2x00dev->ops->tx_queues]; + rt2x00dev->atim = req_atim ? &queue[2 + rt2x00dev->ops->tx_queues] : NULL; + + /* + * Initialize queue parameters. + * RX: qid = QID_RX + * TX: qid = QID_AC_VO + index + * TX: cw_min: 2^5 = 32. + * TX: cw_max: 2^10 = 1024. + * BCN: qid = QID_BEACON + * ATIM: qid = QID_ATIM + */ + rt2x00queue_init(rt2x00dev, rt2x00dev->rx, QID_RX); + + qid = QID_AC_VO; + tx_queue_for_each(rt2x00dev, queue) + rt2x00queue_init(rt2x00dev, queue, qid++); + + rt2x00queue_init(rt2x00dev, rt2x00dev->bcn, QID_BEACON); + if (req_atim) + rt2x00queue_init(rt2x00dev, rt2x00dev->atim, QID_ATIM); + + return 0; +} + +void rt2x00queue_free(struct rt2x00_dev *rt2x00dev) +{ + kfree(rt2x00dev->rx); + rt2x00dev->rx = NULL; + rt2x00dev->tx = NULL; + rt2x00dev->bcn = NULL; +} diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h new file mode 100644 index 00000000000..c48125be0e3 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -0,0 +1,685 @@ +/* + Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00 + Abstract: rt2x00 queue datastructures and routines + */ + +#ifndef RT2X00QUEUE_H +#define RT2X00QUEUE_H + +#include <linux/prefetch.h> + +/** + * DOC: Entry frame size + * + * Ralink PCI devices demand the Frame size to be a multiple of 128 bytes, + * for USB devices this restriction does not apply, but the value of + * 2432 makes sense since it is big enough to contain the maximum fragment + * size according to the ieee802.11 specs. + * The aggregation size depends on support from the driver, but should + * be something around 3840 bytes. + */ +#define DATA_FRAME_SIZE 2432 +#define MGMT_FRAME_SIZE 256 +#define AGGREGATION_SIZE 3840 + +/** + * enum data_queue_qid: Queue identification + * + * @QID_AC_VO: AC VO queue + * @QID_AC_VI: AC VI queue + * @QID_AC_BE: AC BE queue + * @QID_AC_BK: AC BK queue + * @QID_HCCA: HCCA queue + * @QID_MGMT: MGMT queue (prio queue) + * @QID_RX: RX queue + * @QID_OTHER: None of the above (don't use, only present for completeness) + * @QID_BEACON: Beacon queue (value unspecified, don't send it to device) + * @QID_ATIM: Atim queue (value unspecified, don't send it to device) + */ +enum data_queue_qid { + QID_AC_VO = 0, + QID_AC_VI = 1, + QID_AC_BE = 2, + QID_AC_BK = 3, + QID_HCCA = 4, + QID_MGMT = 13, + QID_RX = 14, + QID_OTHER = 15, + QID_BEACON, + QID_ATIM, +}; + +/** + * enum skb_frame_desc_flags: Flags for &struct skb_frame_desc + * + * @SKBDESC_DMA_MAPPED_RX: &skb_dma field has been mapped for RX + * @SKBDESC_DMA_MAPPED_TX: &skb_dma field has been mapped for TX + * @SKBDESC_IV_STRIPPED: Frame contained a IV/EIV provided by + * mac80211 but was stripped for processing by the driver. + * @SKBDESC_NOT_MAC80211: Frame didn't originate from mac80211, + * don't try to pass it back. + * @SKBDESC_DESC_IN_SKB: The descriptor is at the start of the + * skb, instead of in the desc field. + */ +enum skb_frame_desc_flags { + SKBDESC_DMA_MAPPED_RX = 1 << 0, + SKBDESC_DMA_MAPPED_TX = 1 << 1, + SKBDESC_IV_STRIPPED = 1 << 2, + SKBDESC_NOT_MAC80211 = 1 << 3, + SKBDESC_DESC_IN_SKB = 1 << 4, +}; + +/** + * struct skb_frame_desc: Descriptor information for the skb buffer + * + * This structure is placed over the driver_data array, this means that + * this structure should not exceed the size of that array (40 bytes). + * + * @flags: Frame flags, see &enum skb_frame_desc_flags. + * @desc_len: Length of the frame descriptor. + * @tx_rate_idx: the index of the TX rate, used for TX status reporting + * @tx_rate_flags: the TX rate flags, used for TX status reporting + * @desc: Pointer to descriptor part of the frame. + * Note that this pointer could point to something outside + * of the scope of the skb->data pointer. + * @iv: IV/EIV data used during encryption/decryption. + * @skb_dma: (PCI-only) the DMA address associated with the sk buffer. + * @entry: The entry to which this sk buffer belongs. + */ +struct skb_frame_desc { + u8 flags; + + u8 desc_len; + u8 tx_rate_idx; + u8 tx_rate_flags; + + void *desc; + + __le32 iv[2]; + + dma_addr_t skb_dma; + + struct queue_entry *entry; +}; + +/** + * get_skb_frame_desc - Obtain the rt2x00 frame descriptor from a sk_buff. + * @skb: &struct sk_buff from where we obtain the &struct skb_frame_desc + */ +static inline struct skb_frame_desc* get_skb_frame_desc(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct skb_frame_desc) > + IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + return (struct skb_frame_desc *)&IEEE80211_SKB_CB(skb)->driver_data; +} + +/** + * enum rxdone_entry_desc_flags: Flags for &struct rxdone_entry_desc + * + * @RXDONE_SIGNAL_PLCP: Signal field contains the plcp value. + * @RXDONE_SIGNAL_BITRATE: Signal field contains the bitrate value. + * @RXDONE_SIGNAL_MCS: Signal field contains the mcs value. + * @RXDONE_MY_BSS: Does this frame originate from device's BSS. + * @RXDONE_CRYPTO_IV: Driver provided IV/EIV data. + * @RXDONE_CRYPTO_ICV: Driver provided ICV data. + * @RXDONE_L2PAD: 802.11 payload has been padded to 4-byte boundary. + */ +enum rxdone_entry_desc_flags { + RXDONE_SIGNAL_PLCP = BIT(0), + RXDONE_SIGNAL_BITRATE = BIT(1), + RXDONE_SIGNAL_MCS = BIT(2), + RXDONE_MY_BSS = BIT(3), + RXDONE_CRYPTO_IV = BIT(4), + RXDONE_CRYPTO_ICV = BIT(5), + RXDONE_L2PAD = BIT(6), +}; + +/** + * RXDONE_SIGNAL_MASK - Define to mask off all &rxdone_entry_desc_flags flags + * except for the RXDONE_SIGNAL_* flags. This is useful to convert the dev_flags + * from &rxdone_entry_desc to a signal value type. + */ +#define RXDONE_SIGNAL_MASK \ + ( RXDONE_SIGNAL_PLCP | RXDONE_SIGNAL_BITRATE | RXDONE_SIGNAL_MCS ) + +/** + * struct rxdone_entry_desc: RX Entry descriptor + * + * Summary of information that has been read from the RX frame descriptor. + * + * @timestamp: RX Timestamp + * @signal: Signal of the received frame. + * @rssi: RSSI of the received frame. + * @size: Data size of the received frame. + * @flags: MAC80211 receive flags (See &enum mac80211_rx_flags). + * @dev_flags: Ralink receive flags (See &enum rxdone_entry_desc_flags). + * @rate_mode: Rate mode (See @enum rate_modulation). + * @cipher: Cipher type used during decryption. + * @cipher_status: Decryption status. + * @iv: IV/EIV data used during decryption. + * @icv: ICV data used during decryption. + */ +struct rxdone_entry_desc { + u64 timestamp; + int signal; + int rssi; + int size; + int flags; + int dev_flags; + u16 rate_mode; + u8 cipher; + u8 cipher_status; + + __le32 iv[2]; + __le32 icv; +}; + +/** + * enum txdone_entry_desc_flags: Flags for &struct txdone_entry_desc + * + * Every txdone report has to contain the basic result of the + * transmission, either &TXDONE_UNKNOWN, &TXDONE_SUCCESS or + * &TXDONE_FAILURE. The flag &TXDONE_FALLBACK can be used in + * conjunction with all of these flags but should only be set + * if retires > 0. The flag &TXDONE_EXCESSIVE_RETRY can only be used + * in conjunction with &TXDONE_FAILURE. + * + * @TXDONE_UNKNOWN: Hardware could not determine success of transmission. + * @TXDONE_SUCCESS: Frame was successfully send + * @TXDONE_FALLBACK: Hardware used fallback rates for retries + * @TXDONE_FAILURE: Frame was not successfully send + * @TXDONE_EXCESSIVE_RETRY: In addition to &TXDONE_FAILURE, the + * frame transmission failed due to excessive retries. + */ +enum txdone_entry_desc_flags { + TXDONE_UNKNOWN, + TXDONE_SUCCESS, + TXDONE_FALLBACK, + TXDONE_FAILURE, + TXDONE_EXCESSIVE_RETRY, + TXDONE_AMPDU, +}; + +/** + * struct txdone_entry_desc: TX done entry descriptor + * + * Summary of information that has been read from the TX frame descriptor + * after the device is done with transmission. + * + * @flags: TX done flags (See &enum txdone_entry_desc_flags). + * @retry: Retry count. + */ +struct txdone_entry_desc { + unsigned long flags; + int retry; +}; + +/** + * enum txentry_desc_flags: Status flags for TX entry descriptor + * + * @ENTRY_TXD_RTS_FRAME: This frame is a RTS frame. + * @ENTRY_TXD_CTS_FRAME: This frame is a CTS-to-self frame. + * @ENTRY_TXD_GENERATE_SEQ: This frame requires sequence counter. + * @ENTRY_TXD_FIRST_FRAGMENT: This is the first frame. + * @ENTRY_TXD_MORE_FRAG: This frame is followed by another fragment. + * @ENTRY_TXD_REQ_TIMESTAMP: Require timestamp to be inserted. + * @ENTRY_TXD_BURST: This frame belongs to the same burst event. + * @ENTRY_TXD_ACK: An ACK is required for this frame. + * @ENTRY_TXD_RETRY_MODE: When set, the long retry count is used. + * @ENTRY_TXD_ENCRYPT: This frame should be encrypted. + * @ENTRY_TXD_ENCRYPT_PAIRWISE: Use pairwise key table (instead of shared). + * @ENTRY_TXD_ENCRYPT_IV: Generate IV/EIV in hardware. + * @ENTRY_TXD_ENCRYPT_MMIC: Generate MIC in hardware. + * @ENTRY_TXD_HT_AMPDU: This frame is part of an AMPDU. + * @ENTRY_TXD_HT_BW_40: Use 40MHz Bandwidth. + * @ENTRY_TXD_HT_SHORT_GI: Use short GI. + * @ENTRY_TXD_HT_MIMO_PS: The receiving STA is in dynamic SM PS mode. + */ +enum txentry_desc_flags { + ENTRY_TXD_RTS_FRAME, + ENTRY_TXD_CTS_FRAME, + ENTRY_TXD_GENERATE_SEQ, + ENTRY_TXD_FIRST_FRAGMENT, + ENTRY_TXD_MORE_FRAG, + ENTRY_TXD_REQ_TIMESTAMP, + ENTRY_TXD_BURST, + ENTRY_TXD_ACK, + ENTRY_TXD_RETRY_MODE, + ENTRY_TXD_ENCRYPT, + ENTRY_TXD_ENCRYPT_PAIRWISE, + ENTRY_TXD_ENCRYPT_IV, + ENTRY_TXD_ENCRYPT_MMIC, + ENTRY_TXD_HT_AMPDU, + ENTRY_TXD_HT_BW_40, + ENTRY_TXD_HT_SHORT_GI, + ENTRY_TXD_HT_MIMO_PS, +}; + +/** + * struct txentry_desc: TX Entry descriptor + * + * Summary of information for the frame descriptor before sending a TX frame. + * + * @flags: Descriptor flags (See &enum queue_entry_flags). + * @length: Length of the entire frame. + * @header_length: Length of 802.11 header. + * @length_high: PLCP length high word. + * @length_low: PLCP length low word. + * @signal: PLCP signal. + * @service: PLCP service. + * @msc: MCS. + * @stbc: Use Space Time Block Coding (only available for MCS rates < 8). + * @ba_size: Size of the recepients RX reorder buffer - 1. + * @rate_mode: Rate mode (See @enum rate_modulation). + * @mpdu_density: MDPU density. + * @retry_limit: Max number of retries. + * @ifs: IFS value. + * @txop: IFS value for 11n capable chips. + * @cipher: Cipher type used for encryption. + * @key_idx: Key index used for encryption. + * @iv_offset: Position where IV should be inserted by hardware. + * @iv_len: Length of IV data. + */ +struct txentry_desc { + unsigned long flags; + + u16 length; + u16 header_length; + + union { + struct { + u16 length_high; + u16 length_low; + u16 signal; + u16 service; + enum ifs ifs; + } plcp; + + struct { + u16 mcs; + u8 stbc; + u8 ba_size; + u8 mpdu_density; + enum txop txop; + int wcid; + } ht; + } u; + + enum rate_modulation rate_mode; + + short retry_limit; + + enum cipher cipher; + u16 key_idx; + u16 iv_offset; + u16 iv_len; +}; + +/** + * enum queue_entry_flags: Status flags for queue entry + * + * @ENTRY_BCN_ASSIGNED: This entry has been assigned to an interface. + * As long as this bit is set, this entry may only be touched + * through the interface structure. + * @ENTRY_OWNER_DEVICE_DATA: This entry is owned by the device for data + * transfer (either TX or RX depending on the queue). The entry should + * only be touched after the device has signaled it is done with it. + * @ENTRY_DATA_PENDING: This entry contains a valid frame and is waiting + * for the signal to start sending. + * @ENTRY_DATA_IO_FAILED: Hardware indicated that an IO error occurred + * while transferring the data to the hardware. No TX status report will + * be expected from the hardware. + * @ENTRY_DATA_STATUS_PENDING: The entry has been send to the device and + * returned. It is now waiting for the status reporting before the + * entry can be reused again. + */ +enum queue_entry_flags { + ENTRY_BCN_ASSIGNED, + ENTRY_OWNER_DEVICE_DATA, + ENTRY_DATA_PENDING, + ENTRY_DATA_IO_FAILED, + ENTRY_DATA_STATUS_PENDING, + ENTRY_DATA_STATUS_SET, +}; + +/** + * struct queue_entry: Entry inside the &struct data_queue + * + * @flags: Entry flags, see &enum queue_entry_flags. + * @last_action: Timestamp of last change. + * @queue: The data queue (&struct data_queue) to which this entry belongs. + * @skb: The buffer which is currently being transmitted (for TX queue), + * or used to directly receive data in (for RX queue). + * @entry_idx: The entry index number. + * @priv_data: Private data belonging to this queue entry. The pointer + * points to data specific to a particular driver and queue type. + * @status: Device specific status + */ +struct queue_entry { + unsigned long flags; + unsigned long last_action; + + struct data_queue *queue; + + struct sk_buff *skb; + + unsigned int entry_idx; + + u32 status; + + void *priv_data; +}; + +/** + * enum queue_index: Queue index type + * + * @Q_INDEX: Index pointer to the current entry in the queue, if this entry is + * owned by the hardware then the queue is considered to be full. + * @Q_INDEX_DMA_DONE: Index pointer for the next entry which will have been + * transferred to the hardware. + * @Q_INDEX_DONE: Index pointer to the next entry which will be completed by + * the hardware and for which we need to run the txdone handler. If this + * entry is not owned by the hardware the queue is considered to be empty. + * @Q_INDEX_MAX: Keep last, used in &struct data_queue to determine the size + * of the index array. + */ +enum queue_index { + Q_INDEX, + Q_INDEX_DMA_DONE, + Q_INDEX_DONE, + Q_INDEX_MAX, +}; + +/** + * enum data_queue_flags: Status flags for data queues + * + * @QUEUE_STARTED: The queue has been started. Fox RX queues this means the + * device might be DMA'ing skbuffers. TX queues will accept skbuffers to + * be transmitted and beacon queues will start beaconing the configured + * beacons. + * @QUEUE_PAUSED: The queue has been started but is currently paused. + * When this bit is set, the queue has been stopped in mac80211, + * preventing new frames to be enqueued. However, a few frames + * might still appear shortly after the pausing... + */ +enum data_queue_flags { + QUEUE_STARTED, + QUEUE_PAUSED, +}; + +/** + * struct data_queue: Data queue + * + * @rt2x00dev: Pointer to main &struct rt2x00dev where this queue belongs to. + * @entries: Base address of the &struct queue_entry which are + * part of this queue. + * @qid: The queue identification, see &enum data_queue_qid. + * @flags: Entry flags, see &enum queue_entry_flags. + * @status_lock: The mutex for protecting the start/stop/flush + * handling on this queue. + * @tx_lock: Spinlock to serialize tx operations on this queue. + * @index_lock: Spinlock to protect index handling. Whenever @index, @index_done or + * @index_crypt needs to be changed this lock should be grabbed to prevent + * index corruption due to concurrency. + * @count: Number of frames handled in the queue. + * @limit: Maximum number of entries in the queue. + * @threshold: Minimum number of free entries before queue is kicked by force. + * @length: Number of frames in queue. + * @index: Index pointers to entry positions in the queue, + * use &enum queue_index to get a specific index field. + * @txop: maximum burst time. + * @aifs: The aifs value for outgoing frames (field ignored in RX queue). + * @cw_min: The cw min value for outgoing frames (field ignored in RX queue). + * @cw_max: The cw max value for outgoing frames (field ignored in RX queue). + * @data_size: Maximum data size for the frames in this queue. + * @desc_size: Hardware descriptor size for the data in this queue. + * @priv_size: Size of per-queue_entry private data. + * @usb_endpoint: Device endpoint used for communication (USB only) + * @usb_maxpacket: Max packet size for given endpoint (USB only) + */ +struct data_queue { + struct rt2x00_dev *rt2x00dev; + struct queue_entry *entries; + + enum data_queue_qid qid; + unsigned long flags; + + struct mutex status_lock; + spinlock_t tx_lock; + spinlock_t index_lock; + + unsigned int count; + unsigned short limit; + unsigned short threshold; + unsigned short length; + unsigned short index[Q_INDEX_MAX]; + + unsigned short txop; + unsigned short aifs; + unsigned short cw_min; + unsigned short cw_max; + + unsigned short data_size; + unsigned char desc_size; + unsigned char winfo_size; + unsigned short priv_size; + + unsigned short usb_endpoint; + unsigned short usb_maxpacket; +}; + +/** + * queue_end - Return pointer to the last queue (HELPER MACRO). + * @__dev: Pointer to &struct rt2x00_dev + * + * Using the base rx pointer and the maximum number of available queues, + * this macro will return the address of 1 position beyond the end of the + * queues array. + */ +#define queue_end(__dev) \ + &(__dev)->rx[(__dev)->data_queues] + +/** + * tx_queue_end - Return pointer to the last TX queue (HELPER MACRO). + * @__dev: Pointer to &struct rt2x00_dev + * + * Using the base tx pointer and the maximum number of available TX + * queues, this macro will return the address of 1 position beyond + * the end of the TX queue array. + */ +#define tx_queue_end(__dev) \ + &(__dev)->tx[(__dev)->ops->tx_queues] + +/** + * queue_next - Return pointer to next queue in list (HELPER MACRO). + * @__queue: Current queue for which we need the next queue + * + * Using the current queue address we take the address directly + * after the queue to take the next queue. Note that this macro + * should be used carefully since it does not protect against + * moving past the end of the list. (See macros &queue_end and + * &tx_queue_end for determining the end of the queue). + */ +#define queue_next(__queue) \ + &(__queue)[1] + +/** + * queue_loop - Loop through the queues within a specific range (HELPER MACRO). + * @__entry: Pointer where the current queue entry will be stored in. + * @__start: Start queue pointer. + * @__end: End queue pointer. + * + * This macro will loop through all queues between &__start and &__end. + */ +#define queue_loop(__entry, __start, __end) \ + for ((__entry) = (__start); \ + prefetch(queue_next(__entry)), (__entry) != (__end);\ + (__entry) = queue_next(__entry)) + +/** + * queue_for_each - Loop through all queues + * @__dev: Pointer to &struct rt2x00_dev + * @__entry: Pointer where the current queue entry will be stored in. + * + * This macro will loop through all available queues. + */ +#define queue_for_each(__dev, __entry) \ + queue_loop(__entry, (__dev)->rx, queue_end(__dev)) + +/** + * tx_queue_for_each - Loop through the TX queues + * @__dev: Pointer to &struct rt2x00_dev + * @__entry: Pointer where the current queue entry will be stored in. + * + * This macro will loop through all TX related queues excluding + * the Beacon and Atim queues. + */ +#define tx_queue_for_each(__dev, __entry) \ + queue_loop(__entry, (__dev)->tx, tx_queue_end(__dev)) + +/** + * txall_queue_for_each - Loop through all TX related queues + * @__dev: Pointer to &struct rt2x00_dev + * @__entry: Pointer where the current queue entry will be stored in. + * + * This macro will loop through all TX related queues including + * the Beacon and Atim queues. + */ +#define txall_queue_for_each(__dev, __entry) \ + queue_loop(__entry, (__dev)->tx, queue_end(__dev)) + +/** + * rt2x00queue_for_each_entry - Loop through all entries in the queue + * @queue: Pointer to @data_queue + * @start: &enum queue_index Pointer to start index + * @end: &enum queue_index Pointer to end index + * @data: Data to pass to the callback function + * @fn: The function to call for each &struct queue_entry + * + * This will walk through all entries in the queue, in chronological + * order. This means it will start at the current @start pointer + * and will walk through the queue until it reaches the @end pointer. + * + * If fn returns true for an entry rt2x00queue_for_each_entry will stop + * processing and return true as well. + */ +bool rt2x00queue_for_each_entry(struct data_queue *queue, + enum queue_index start, + enum queue_index end, + void *data, + bool (*fn)(struct queue_entry *entry, + void *data)); + +/** + * rt2x00queue_empty - Check if the queue is empty. + * @queue: Queue to check if empty. + */ +static inline int rt2x00queue_empty(struct data_queue *queue) +{ + return queue->length == 0; +} + +/** + * rt2x00queue_full - Check if the queue is full. + * @queue: Queue to check if full. + */ +static inline int rt2x00queue_full(struct data_queue *queue) +{ + return queue->length == queue->limit; +} + +/** + * rt2x00queue_free - Check the number of available entries in queue. + * @queue: Queue to check. + */ +static inline int rt2x00queue_available(struct data_queue *queue) +{ + return queue->limit - queue->length; +} + +/** + * rt2x00queue_threshold - Check if the queue is below threshold + * @queue: Queue to check. + */ +static inline int rt2x00queue_threshold(struct data_queue *queue) +{ + return rt2x00queue_available(queue) < queue->threshold; +} +/** + * rt2x00queue_dma_timeout - Check if a timeout occurred for DMA transfers + * @entry: Queue entry to check. + */ +static inline int rt2x00queue_dma_timeout(struct queue_entry *entry) +{ + if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return false; + return time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); +} + +/** + * _rt2x00_desc_read - Read a word from the hardware descriptor. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be read. + * @value: Address where the descriptor value should be written into. + */ +static inline void _rt2x00_desc_read(__le32 *desc, const u8 word, __le32 *value) +{ + *value = desc[word]; +} + +/** + * rt2x00_desc_read - Read a word from the hardware descriptor, this + * function will take care of the byte ordering. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be read. + * @value: Address where the descriptor value should be written into. + */ +static inline void rt2x00_desc_read(__le32 *desc, const u8 word, u32 *value) +{ + __le32 tmp; + _rt2x00_desc_read(desc, word, &tmp); + *value = le32_to_cpu(tmp); +} + +/** + * rt2x00_desc_write - write a word to the hardware descriptor, this + * function will take care of the byte ordering. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be written. + * @value: Value that should be written into the descriptor. + */ +static inline void _rt2x00_desc_write(__le32 *desc, const u8 word, __le32 value) +{ + desc[word] = value; +} + +/** + * rt2x00_desc_write - write a word to the hardware descriptor. + * @desc: Base descriptor address + * @word: Word index from where the descriptor should be written. + * @value: Value that should be written into the descriptor. + */ +static inline void rt2x00_desc_write(__le32 *desc, const u8 word, u32 value) +{ + _rt2x00_desc_write(desc, word, cpu_to_le32(value)); +} + +#endif /* RT2X00QUEUE_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h index b1915dc7dda..3cc541d13d6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00reg.h +++ b/drivers/net/wireless/rt2x00/rt2x00reg.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -27,14 +25,13 @@ #define RT2X00REG_H /* - * TX result flags. + * RX crypto status */ -enum TX_STATUS { - TX_SUCCESS = 0, - TX_SUCCESS_RETRY = 1, - TX_FAIL_RETRY = 2, - TX_FAIL_INVALID = 3, - TX_FAIL_OTHER = 4, +enum rx_crypto { + RX_CRYPTO_SUCCESS = 0, + RX_CRYPTO_FAIL_ICV = 1, + RX_CRYPTO_FAIL_MIC = 2, + RX_CRYPTO_FAIL_KEY = 3, }; /* @@ -64,7 +61,8 @@ enum led_mode { enum tsf_sync { TSF_SYNC_NONE = 0, TSF_SYNC_INFRA = 1, - TSF_SYNC_BEACON = 2, + TSF_SYNC_ADHOC = 2, + TSF_SYNC_AP_NONE = 3, }; /* @@ -83,10 +81,6 @@ enum dev_state { */ STATE_RADIO_ON, STATE_RADIO_OFF, - STATE_RADIO_RX_ON, - STATE_RADIO_RX_OFF, - STATE_RADIO_RX_ON_LINK, - STATE_RADIO_RX_OFF_LINK, STATE_RADIO_IRQ_ON, STATE_RADIO_IRQ_OFF, }; @@ -102,6 +96,16 @@ enum ifs { }; /* + * IFS backoff values for HT devices + */ +enum txop { + TXOP_HTTXOP = 0, + TXOP_PIFS = 1, + TXOP_SIFS = 2, + TXOP_BACKOFF = 3, +}; + +/* * Cipher types for hardware encryption */ enum cipher { @@ -115,7 +119,34 @@ enum cipher { */ CIPHER_CKIP64 = 5, CIPHER_CKIP128 = 6, - CIPHER_TKIP_NO_MIC = 7, + CIPHER_TKIP_NO_MIC = 7, /* Don't send to device */ + +/* + * Max cipher type. + * Note that CIPHER_NONE isn't counted, and CKIP64 and CKIP128 + * are excluded due to limitations in mac80211. + */ + CIPHER_MAX = 4, +}; + +/* + * Rate modulations + */ +enum rate_modulation { + RATE_MODE_CCK = 0, + RATE_MODE_OFDM = 1, + RATE_MODE_HT_MIX = 2, + RATE_MODE_HT_GREENFIELD = 3, +}; + +/* + * Firmware validation error codes + */ +enum firmware_errors { + FW_OK, + FW_BAD_CRC, + FW_BAD_LENGTH, + FW_BAD_VERSION, }; /* @@ -141,154 +172,106 @@ struct rt2x00_field32 { /* * Power of two check, this will check - * if the mask that has been given contains - * and contiguous set of bits. + * if the mask that has been given contains and contiguous set of bits. + * Note that we cannot use the is_power_of_2() function since this + * check must be done at compile-time. */ #define is_power_of_two(x) ( !((x) & ((x)-1)) ) #define low_bit_mask(x) ( ((x)-1) & ~(x) ) -#define is_valid_mask(x) is_power_of_two(1 + (x) + low_bit_mask(x)) +#define is_valid_mask(x) is_power_of_two(1LU + (x) + low_bit_mask(x)) + +/* + * Macros to find first set bit in a variable. + * These macros behave the same as the __ffs() functions but + * the most important difference that this is done during + * compile-time rather then run-time. + */ +#define compile_ffs2(__x) \ + __builtin_choose_expr(((__x) & 0x1), 0, 1) + +#define compile_ffs4(__x) \ + __builtin_choose_expr(((__x) & 0x3), \ + (compile_ffs2((__x))), \ + (compile_ffs2((__x) >> 2) + 2)) + +#define compile_ffs8(__x) \ + __builtin_choose_expr(((__x) & 0xf), \ + (compile_ffs4((__x))), \ + (compile_ffs4((__x) >> 4) + 4)) + +#define compile_ffs16(__x) \ + __builtin_choose_expr(((__x) & 0xff), \ + (compile_ffs8((__x))), \ + (compile_ffs8((__x) >> 8) + 8)) + +#define compile_ffs32(__x) \ + __builtin_choose_expr(((__x) & 0xffff), \ + (compile_ffs16((__x))), \ + (compile_ffs16((__x) >> 16) + 16)) + +/* + * This macro will check the requirements for the FIELD{8,16,32} macros + * The mask should be a constant non-zero contiguous set of bits which + * does not exceed the given typelimit. + */ +#define FIELD_CHECK(__mask, __type) \ + BUILD_BUG_ON(!(__mask) || \ + !is_valid_mask(__mask) || \ + (__mask) != (__type)(__mask)) \ #define FIELD8(__mask) \ ({ \ - BUILD_BUG_ON(!(__mask) || \ - !is_valid_mask(__mask) || \ - (__mask) != (u8)(__mask)); \ + FIELD_CHECK(__mask, u8); \ (struct rt2x00_field8) { \ - __ffs(__mask), (__mask) \ + compile_ffs8(__mask), (__mask) \ }; \ }) #define FIELD16(__mask) \ ({ \ - BUILD_BUG_ON(!(__mask) || \ - !is_valid_mask(__mask) || \ - (__mask) != (u16)(__mask));\ + FIELD_CHECK(__mask, u16); \ (struct rt2x00_field16) { \ - __ffs(__mask), (__mask) \ + compile_ffs16(__mask), (__mask) \ }; \ }) #define FIELD32(__mask) \ ({ \ - BUILD_BUG_ON(!(__mask) || \ - !is_valid_mask(__mask) || \ - (__mask) != (u32)(__mask));\ + FIELD_CHECK(__mask, u32); \ (struct rt2x00_field32) { \ - __ffs(__mask), (__mask) \ + compile_ffs32(__mask), (__mask) \ }; \ }) -static inline void rt2x00_set_field32(u32 *reg, - const struct rt2x00_field32 field, - const u32 value) -{ - *reg &= ~(field.bit_mask); - *reg |= (value << field.bit_offset) & field.bit_mask; -} - -static inline u32 rt2x00_get_field32(const u32 reg, - const struct rt2x00_field32 field) -{ - return (reg & field.bit_mask) >> field.bit_offset; -} - -static inline void rt2x00_set_field16(u16 *reg, - const struct rt2x00_field16 field, - const u16 value) -{ - *reg &= ~(field.bit_mask); - *reg |= (value << field.bit_offset) & field.bit_mask; -} - -static inline u16 rt2x00_get_field16(const u16 reg, - const struct rt2x00_field16 field) -{ - return (reg & field.bit_mask) >> field.bit_offset; -} - -static inline void rt2x00_set_field8(u8 *reg, - const struct rt2x00_field8 field, - const u8 value) -{ - *reg &= ~(field.bit_mask); - *reg |= (value << field.bit_offset) & field.bit_mask; -} - -static inline u8 rt2x00_get_field8(const u8 reg, - const struct rt2x00_field8 field) -{ - return (reg & field.bit_mask) >> field.bit_offset; -} - -/* - * Device specific rate value. - * We will have to create the device specific rate value - * passed to the ieee80211 kernel. We need to make it a consist of - * multiple fields because we want to store more then 1 device specific - * values inside the value. - * 1 - rate, stored as 100 kbit/s. - * 2 - preamble, short_preamble enabled flag. - * 3 - MASK_RATE, which rates are enabled in this mode, this mask - * corresponds with the TX register format for the current device. - * 4 - plcp, 802.11b rates are device specific, - * 802.11g rates are set according to the ieee802.11a-1999 p.14. - * The bit to enable preamble is set in a seperate define. - */ -#define DEV_RATE FIELD32(0x000007ff) -#define DEV_PREAMBLE FIELD32(0x00000800) -#define DEV_RATEMASK FIELD32(0x00fff000) -#define DEV_PLCP FIELD32(0xff000000) - -/* - * Bitfields - */ -#define DEV_RATEBIT_1MB ( 1 << 0 ) -#define DEV_RATEBIT_2MB ( 1 << 1 ) -#define DEV_RATEBIT_5_5MB ( 1 << 2 ) -#define DEV_RATEBIT_11MB ( 1 << 3 ) -#define DEV_RATEBIT_6MB ( 1 << 4 ) -#define DEV_RATEBIT_9MB ( 1 << 5 ) -#define DEV_RATEBIT_12MB ( 1 << 6 ) -#define DEV_RATEBIT_18MB ( 1 << 7 ) -#define DEV_RATEBIT_24MB ( 1 << 8 ) -#define DEV_RATEBIT_36MB ( 1 << 9 ) -#define DEV_RATEBIT_48MB ( 1 << 10 ) -#define DEV_RATEBIT_54MB ( 1 << 11 ) - -/* - * Bitmasks for DEV_RATEMASK - */ -#define DEV_RATEMASK_1MB ( (DEV_RATEBIT_1MB << 1) -1 ) -#define DEV_RATEMASK_2MB ( (DEV_RATEBIT_2MB << 1) -1 ) -#define DEV_RATEMASK_5_5MB ( (DEV_RATEBIT_5_5MB << 1) -1 ) -#define DEV_RATEMASK_11MB ( (DEV_RATEBIT_11MB << 1) -1 ) -#define DEV_RATEMASK_6MB ( (DEV_RATEBIT_6MB << 1) -1 ) -#define DEV_RATEMASK_9MB ( (DEV_RATEBIT_9MB << 1) -1 ) -#define DEV_RATEMASK_12MB ( (DEV_RATEBIT_12MB << 1) -1 ) -#define DEV_RATEMASK_18MB ( (DEV_RATEBIT_18MB << 1) -1 ) -#define DEV_RATEMASK_24MB ( (DEV_RATEBIT_24MB << 1) -1 ) -#define DEV_RATEMASK_36MB ( (DEV_RATEBIT_36MB << 1) -1 ) -#define DEV_RATEMASK_48MB ( (DEV_RATEBIT_48MB << 1) -1 ) -#define DEV_RATEMASK_54MB ( (DEV_RATEBIT_54MB << 1) -1 ) +#define SET_FIELD(__reg, __type, __field, __value)\ +({ \ + typecheck(__type, __field); \ + *(__reg) &= ~((__field).bit_mask); \ + *(__reg) |= ((__value) << \ + ((__field).bit_offset)) & \ + ((__field).bit_mask); \ +}) -/* - * Bitmask groups of bitrates - */ -#define DEV_BASIC_RATEMASK \ - ( DEV_RATEMASK_11MB | \ - DEV_RATEBIT_6MB | DEV_RATEBIT_12MB | DEV_RATEBIT_24MB ) +#define GET_FIELD(__reg, __type, __field) \ +({ \ + typecheck(__type, __field); \ + ((__reg) & ((__field).bit_mask)) >> \ + ((__field).bit_offset); \ +}) -#define DEV_CCK_RATEMASK ( DEV_RATEMASK_11MB ) -#define DEV_OFDM_RATEMASK ( DEV_RATEMASK_54MB & ~DEV_CCK_RATEMASK ) +#define rt2x00_set_field32(__reg, __field, __value) \ + SET_FIELD(__reg, struct rt2x00_field32, __field, __value) +#define rt2x00_get_field32(__reg, __field) \ + GET_FIELD(__reg, struct rt2x00_field32, __field) -/* - * Macro's to set and get specific fields from the device specific val and val2 - * fields inside the ieee80211_rate entry. - */ -#define DEVICE_SET_RATE_FIELD(__value, __mask) \ - (int)( ((__value) << DEV_##__mask.bit_offset) & DEV_##__mask.bit_mask ) +#define rt2x00_set_field16(__reg, __field, __value) \ + SET_FIELD(__reg, struct rt2x00_field16, __field, __value) +#define rt2x00_get_field16(__reg, __field) \ + GET_FIELD(__reg, struct rt2x00_field16, __field) -#define DEVICE_GET_RATE_FIELD(__value, __mask) \ - (int)( ((__value) & DEV_##__mask.bit_mask) >> DEV_##__mask.bit_offset ) +#define rt2x00_set_field8(__reg, __field, __value) \ + SET_FIELD(__reg, struct rt2x00_field8, __field, __value) +#define rt2x00_get_field8(__reg, __field) \ + GET_FIELD(__reg, struct rt2x00_field8, __field) #endif /* RT2X00REG_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00rfkill.c b/drivers/net/wireless/rt2x00/rt2x00rfkill.c deleted file mode 100644 index f9557759620..00000000000 --- a/drivers/net/wireless/rt2x00/rt2x00rfkill.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project - <http://rt2x00.serialmonkey.com> - - 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; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - Module: rt2x00rfkill - Abstract: rt2x00 rfkill routines. - */ - -#include <linux/input-polldev.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/rfkill.h> - -#include "rt2x00.h" -#include "rt2x00lib.h" - -static int rt2x00rfkill_toggle_radio(void *data, enum rfkill_state state) -{ - struct rt2x00_dev *rt2x00dev = data; - int retval = 0; - - if (unlikely(!rt2x00dev)) - return 0; - - /* - * Only continue if there are enabled interfaces. - */ - if (!test_bit(DEVICE_STARTED, &rt2x00dev->flags)) - return 0; - - if (state == RFKILL_STATE_ON) { - INFO(rt2x00dev, "Hardware button pressed, enabling radio.\n"); - __clear_bit(DEVICE_DISABLED_RADIO_HW, &rt2x00dev->flags); - retval = rt2x00lib_enable_radio(rt2x00dev); - } else if (state == RFKILL_STATE_OFF) { - INFO(rt2x00dev, "Hardware button pressed, disabling radio.\n"); - __set_bit(DEVICE_DISABLED_RADIO_HW, &rt2x00dev->flags); - rt2x00lib_disable_radio(rt2x00dev); - } - - return retval; -} - -static void rt2x00rfkill_poll(struct input_polled_dev *poll_dev) -{ - struct rt2x00_dev *rt2x00dev = poll_dev->private; - int state = rt2x00dev->ops->lib->rfkill_poll(rt2x00dev); - - if (rt2x00dev->rfkill->state != state) { - input_report_key(poll_dev->input, KEY_WLAN, 1); - input_report_key(poll_dev->input, KEY_WLAN, 0); - } -} - -void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) || - !test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state)) - return; - - if (rfkill_register(rt2x00dev->rfkill)) { - ERROR(rt2x00dev, "Failed to register rfkill handler.\n"); - return; - } - - if (input_register_polled_device(rt2x00dev->poll_dev)) { - ERROR(rt2x00dev, "Failed to register polled device.\n"); - rfkill_unregister(rt2x00dev->rfkill); - return; - } - - __set_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state); - - /* - * Force initial poll which will detect the initial device state, - * and correctly sends the signal to the rfkill layer about this - * state. - */ - rt2x00rfkill_poll(rt2x00dev->poll_dev); -} - -void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) || - !test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state)) - return; - - input_unregister_polled_device(rt2x00dev->poll_dev); - rfkill_unregister(rt2x00dev->rfkill); - - __clear_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state); -} - -static struct input_polled_dev * -rt2x00rfkill_allocate_polldev(struct rt2x00_dev *rt2x00dev) -{ - struct input_polled_dev *poll_dev; - - poll_dev = input_allocate_polled_device(); - if (!poll_dev) - return NULL; - - poll_dev->private = rt2x00dev; - poll_dev->poll = rt2x00rfkill_poll; - poll_dev->poll_interval = RFKILL_POLL_INTERVAL; - - poll_dev->input->name = rt2x00dev->ops->name; - poll_dev->input->phys = wiphy_name(rt2x00dev->hw->wiphy); - poll_dev->input->id.bustype = BUS_HOST; - poll_dev->input->id.vendor = 0x1814; - poll_dev->input->id.product = rt2x00dev->chip.rt; - poll_dev->input->id.version = rt2x00dev->chip.rev; - poll_dev->input->dev.parent = wiphy_dev(rt2x00dev->hw->wiphy); - poll_dev->input->evbit[0] = BIT(EV_KEY); - set_bit(KEY_WLAN, poll_dev->input->keybit); - - return poll_dev; -} - -void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags)) - return; - - rt2x00dev->rfkill = - rfkill_allocate(wiphy_dev(rt2x00dev->hw->wiphy), RFKILL_TYPE_WLAN); - if (!rt2x00dev->rfkill) { - ERROR(rt2x00dev, "Failed to allocate rfkill handler.\n"); - return; - } - - rt2x00dev->rfkill->name = rt2x00dev->ops->name; - rt2x00dev->rfkill->data = rt2x00dev; - rt2x00dev->rfkill->state = -1; - rt2x00dev->rfkill->toggle_radio = rt2x00rfkill_toggle_radio; - - rt2x00dev->poll_dev = rt2x00rfkill_allocate_polldev(rt2x00dev); - if (!rt2x00dev->poll_dev) { - ERROR(rt2x00dev, "Failed to allocate polled device.\n"); - rfkill_free(rt2x00dev->rfkill); - rt2x00dev->rfkill = NULL; - return; - } - - return; -} - -void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) || - !test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state)) - return; - - input_free_polled_device(rt2x00dev->poll_dev); - rt2x00dev->poll_dev = NULL; - - rfkill_free(rt2x00dev->rfkill); - rt2x00dev->rfkill = NULL; -} - -void rt2x00rfkill_suspend(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) || - !test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state)) - return; - - input_free_polled_device(rt2x00dev->poll_dev); - rt2x00dev->poll_dev = NULL; -} - -void rt2x00rfkill_resume(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags) || - !test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state)) - return; - - rt2x00dev->poll_dev = rt2x00rfkill_allocate_polldev(rt2x00dev); - if (!rt2x00dev->poll_dev) { - ERROR(rt2x00dev, "Failed to allocate polled device.\n"); - return; - } -} diff --git a/drivers/net/wireless/rt2x00/rt2x00ring.h b/drivers/net/wireless/rt2x00/rt2x00ring.h deleted file mode 100644 index 1caa6d688c4..00000000000 --- a/drivers/net/wireless/rt2x00/rt2x00ring.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project - <http://rt2x00.serialmonkey.com> - - 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; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - Module: rt2x00 - Abstract: rt2x00 ring datastructures and routines - */ - -#ifndef RT2X00RING_H -#define RT2X00RING_H - -/* - * skb_desc - * Descriptor information for the skb buffer - */ -struct skb_desc { - unsigned int frame_type; - - unsigned int desc_len; - unsigned int data_len; - - void *desc; - void *data; - - struct data_ring *ring; - struct data_entry *entry; -}; - -static inline struct skb_desc* get_skb_desc(struct sk_buff *skb) -{ - return (struct skb_desc*)&skb->cb[0]; -} - -/* - * rxdata_entry_desc - * Summary of information that has been read from the - * RX frame descriptor. - */ -struct rxdata_entry_desc { - int signal; - int rssi; - int ofdm; - int size; - int flags; - int my_bss; -}; - -/* - * txdata_entry_desc - * Summary of information that should be written into the - * descriptor for sending a TX frame. - */ -struct txdata_entry_desc { - unsigned long flags; -#define ENTRY_TXDONE 1 -#define ENTRY_TXD_RTS_FRAME 2 -#define ENTRY_TXD_OFDM_RATE 3 -#define ENTRY_TXD_MORE_FRAG 4 -#define ENTRY_TXD_REQ_TIMESTAMP 5 -#define ENTRY_TXD_BURST 6 -#define ENTRY_TXD_ACK 7 - -/* - * Queue ID. ID's 0-4 are data TX rings - */ - int queue; -#define QUEUE_MGMT 13 -#define QUEUE_RX 14 -#define QUEUE_OTHER 15 - - /* - * PLCP values. - */ - u16 length_high; - u16 length_low; - u16 signal; - u16 service; - - /* - * Timing information - */ - int aifs; - int ifs; - int cw_min; - int cw_max; -}; - -/* - * data_entry - * The data ring is a list of data entries. - * Each entry holds a reference to the descriptor - * and the data buffer. For TX rings the reference to the - * sk_buff of the packet being transmitted is also stored here. - */ -struct data_entry { - /* - * Status flags - */ - unsigned long flags; -#define ENTRY_OWNER_NIC 1 - - /* - * Ring we belong to. - */ - struct data_ring *ring; - - /* - * sk_buff for the packet which is being transmitted - * in this entry (Only used with TX related rings). - */ - struct sk_buff *skb; - - /* - * Store a ieee80211_tx_status structure in each - * ring entry, this will optimize the txdone - * handler. - */ - struct ieee80211_tx_status tx_status; - - /* - * private pointer specific to driver. - */ - void *priv; - - /* - * Data address for this entry. - */ - void *data_addr; - dma_addr_t data_dma; - - /* - * Entry identification number (index). - */ - unsigned int entry_idx; -}; - -/* - * data_ring - * Data rings are used by the device to send and receive packets. - * The data_addr is the base address of the data memory. - * To determine at which point in the ring we are, - * have to use the rt2x00_ring_index_*() functions. - */ -struct data_ring { - /* - * Pointer to main rt2x00dev structure where this - * ring belongs to. - */ - struct rt2x00_dev *rt2x00dev; - - /* - * Base address for the device specific data entries. - */ - struct data_entry *entry; - - /* - * TX queue statistic info. - */ - struct ieee80211_tx_queue_stats_data stats; - - /* - * TX Queue parameters. - */ - struct ieee80211_tx_queue_params tx_params; - - /* - * Base address for data ring. - */ - dma_addr_t data_dma; - void *data_addr; - - /* - * Queue identification number: - * RX: 0 - * TX: IEEE80211_TX_* - */ - unsigned int queue_idx; - - /* - * Index variables. - */ - u16 index; - u16 index_done; - - /* - * Size of packet and descriptor in bytes. - */ - u16 data_size; - u16 desc_size; -}; - -/* - * Handlers to determine the address of the current device specific - * data entry, where either index or index_done points to. - */ -static inline struct data_entry *rt2x00_get_data_entry(struct data_ring *ring) -{ - return &ring->entry[ring->index]; -} - -static inline struct data_entry *rt2x00_get_data_entry_done(struct data_ring - *ring) -{ - return &ring->entry[ring->index_done]; -} - -/* - * Total ring memory - */ -static inline int rt2x00_get_ring_size(struct data_ring *ring) -{ - return ring->stats.limit * (ring->desc_size + ring->data_size); -} - -/* - * Ring index manipulation functions. - */ -static inline void rt2x00_ring_index_inc(struct data_ring *ring) -{ - ring->index++; - if (ring->index >= ring->stats.limit) - ring->index = 0; - ring->stats.len++; -} - -static inline void rt2x00_ring_index_done_inc(struct data_ring *ring) -{ - ring->index_done++; - if (ring->index_done >= ring->stats.limit) - ring->index_done = 0; - ring->stats.len--; - ring->stats.count++; -} - -static inline void rt2x00_ring_index_clear(struct data_ring *ring) -{ - ring->index = 0; - ring->index_done = 0; - ring->stats.len = 0; - ring->stats.count = 0; -} - -static inline int rt2x00_ring_empty(struct data_ring *ring) -{ - return ring->stats.len == 0; -} - -static inline int rt2x00_ring_full(struct data_ring *ring) -{ - return ring->stats.len == ring->stats.limit; -} - -static inline int rt2x00_ring_free(struct data_ring *ring) -{ - return ring->stats.limit - ring->stats.len; -} - -/* - * TX/RX Descriptor access functions. - */ -static inline void rt2x00_desc_read(__le32 *desc, - const u8 word, u32 *value) -{ - *value = le32_to_cpu(desc[word]); -} - -static inline void rt2x00_desc_write(__le32 *desc, - const u8 word, const u32 value) -{ - desc[word] = cpu_to_le32(value); -} - -#endif /* RT2X00RING_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.c b/drivers/net/wireless/rt2x00/rt2x00soc.c new file mode 100644 index 00000000000..69a0cdadb07 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00soc.c @@ -0,0 +1,160 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + Copyright (C) 2004 - 2009 Felix Fietkau <nbd@openwrt.org> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00soc + Abstract: rt2x00 generic soc device routines. + */ + +#include <linux/bug.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "rt2x00.h" +#include "rt2x00soc.h" + +static void rt2x00soc_free_reg(struct rt2x00_dev *rt2x00dev) +{ + kfree(rt2x00dev->rf); + rt2x00dev->rf = NULL; + + kfree(rt2x00dev->eeprom); + rt2x00dev->eeprom = NULL; + + iounmap(rt2x00dev->csr.base); +} + +static int rt2x00soc_alloc_reg(struct rt2x00_dev *rt2x00dev) +{ + struct platform_device *pdev = to_platform_device(rt2x00dev->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + rt2x00dev->csr.base = ioremap(res->start, resource_size(res)); + if (!rt2x00dev->csr.base) + return -ENOMEM; + + rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); + if (!rt2x00dev->eeprom) + goto exit; + + rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); + if (!rt2x00dev->rf) + goto exit; + + return 0; + +exit: + rt2x00_probe_err("Failed to allocate registers\n"); + rt2x00soc_free_reg(rt2x00dev); + + return -ENOMEM; +} + +int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops) +{ + struct ieee80211_hw *hw; + struct rt2x00_dev *rt2x00dev; + int retval; + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); + if (!hw) { + rt2x00_probe_err("Failed to allocate hardware\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, hw); + + rt2x00dev = hw->priv; + rt2x00dev->dev = &pdev->dev; + rt2x00dev->ops = ops; + rt2x00dev->hw = hw; + rt2x00dev->irq = platform_get_irq(pdev, 0); + rt2x00dev->name = pdev->dev.driver->name; + + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); + + retval = rt2x00soc_alloc_reg(rt2x00dev); + if (retval) + goto exit_free_device; + + retval = rt2x00lib_probe_dev(rt2x00dev); + if (retval) + goto exit_free_reg; + + return 0; + +exit_free_reg: + rt2x00soc_free_reg(rt2x00dev); + +exit_free_device: + ieee80211_free_hw(hw); + + return retval; +} +EXPORT_SYMBOL_GPL(rt2x00soc_probe); + +int rt2x00soc_remove(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Free all allocated data. + */ + rt2x00lib_remove_dev(rt2x00dev); + rt2x00soc_free_reg(rt2x00dev); + ieee80211_free_hw(hw); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00soc_remove); + +#ifdef CONFIG_PM +int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00lib_suspend(rt2x00dev, state); +} +EXPORT_SYMBOL_GPL(rt2x00soc_suspend); + +int rt2x00soc_resume(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + return rt2x00lib_resume(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00soc_resume); +#endif /* CONFIG_PM */ + +/* + * rt2x00soc module information. + */ +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 soc library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.h b/drivers/net/wireless/rt2x00/rt2x00soc.h new file mode 100644 index 00000000000..9948d355e9a --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00soc.h @@ -0,0 +1,40 @@ +/* + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> + <http://rt2x00.serialmonkey.com> + + 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; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + Module: rt2x00soc + Abstract: Data structures for the rt2x00soc module. + */ + +#ifndef RT2X00SOC_H +#define RT2X00SOC_H + +/* + * SoC driver handlers. + */ +int rt2x00soc_probe(struct platform_device *pdev, const struct rt2x00_ops *ops); +int rt2x00soc_remove(struct platform_device *pdev); +#ifdef CONFIG_PM +int rt2x00soc_suspend(struct platform_device *pdev, pm_message_t state); +int rt2x00soc_resume(struct platform_device *pdev); +#else +#define rt2x00soc_suspend NULL +#define rt2x00soc_resume NULL +#endif /* CONFIG_PM */ + +#endif /* RT2X00SOC_H */ diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 84e9bdb7391..86c43d112a4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -1,5 +1,6 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2010 Willow Garage <http://www.willowgarage.com> + Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +14,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -25,6 +24,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/usb.h> #include <linux/bug.h> @@ -40,14 +40,15 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, void *buffer, const u16 buffer_length, const int timeout) { - struct usb_device *usb_dev = - interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); int status; unsigned int i; unsigned int pipe = (requesttype == USB_VENDOR_REQUEST_IN) ? usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return -ENODEV; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { status = usb_control_msg(usb_dev, pipe, request, requesttype, @@ -61,13 +62,21 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, * -ENODEV: Device has disappeared, no point continuing. * All other errors: Try again. */ - else if (status == -ENODEV) + else if (status == -ENODEV) { + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); break; + } } - ERROR(rt2x00dev, - "Vendor Request 0x%02x failed for offset 0x%04x with error %d.\n", - request, offset, status); + /* If the port is powered down, we get a -EPROTO error, and this + * leads to a endless loop. So just say that the device is gone. + */ + if (status == -EPROTO) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + + rt2x00_err(rt2x00dev, + "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n", + request, offset, status); return status; } @@ -80,25 +89,25 @@ int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, { int status; - BUG_ON(!mutex_is_locked(&rt2x00dev->usb_cache_mutex)); + BUG_ON(!mutex_is_locked(&rt2x00dev->csr_mutex)); /* * Check for Cache availability. */ - if (unlikely(!rt2x00dev->csr_cache || buffer_length > CSR_CACHE_SIZE)) { - ERROR(rt2x00dev, "CSR cache not available.\n"); + if (unlikely(!rt2x00dev->csr.cache || buffer_length > CSR_CACHE_SIZE)) { + rt2x00_err(rt2x00dev, "CSR cache not available\n"); return -ENOMEM; } if (requesttype == USB_VENDOR_REQUEST_OUT) - memcpy(rt2x00dev->csr_cache, buffer, buffer_length); + memcpy(rt2x00dev->csr.cache, buffer, buffer_length); status = rt2x00usb_vendor_request(rt2x00dev, request, requesttype, - offset, 0, rt2x00dev->csr_cache, + offset, 0, rt2x00dev->csr.cache, buffer_length, timeout); if (!status && requesttype == USB_VENDOR_REQUEST_IN) - memcpy(buffer, rt2x00dev->csr_cache, buffer_length); + memcpy(buffer, rt2x00dev->csr.cache, buffer_length); return status; } @@ -109,340 +118,617 @@ int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, const u16 offset, void *buffer, const u16 buffer_length, const int timeout) { - int status; - - mutex_lock(&rt2x00dev->usb_cache_mutex); - - status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request, - requesttype, offset, buffer, - buffer_length, timeout); + int status = 0; + unsigned char *tb; + u16 off, len, bsize; + + mutex_lock(&rt2x00dev->csr_mutex); + + tb = (char *)buffer; + off = offset; + len = buffer_length; + while (len && !status) { + bsize = min_t(u16, CSR_CACHE_SIZE, len); + status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request, + requesttype, off, tb, + bsize, timeout); + + tb += bsize; + len -= bsize; + off += bsize; + } - mutex_unlock(&rt2x00dev->usb_cache_mutex); + mutex_unlock(&rt2x00dev->csr_mutex); return status; } EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff); -/* - * TX data handlers. - */ -static void rt2x00usb_interrupt_txdone(struct urb *urb) +int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg) { - struct data_entry *entry = (struct data_entry *)urb->context; - struct data_ring *ring = entry->ring; - struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; - __le32 *txd = (__le32 *)entry->skb->data; - u32 word; - int tx_status; + unsigned int i; - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags) || - !__test_and_clear_bit(ENTRY_OWNER_NIC, &entry->flags)) - return; + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return -ENODEV; - rt2x00_desc_read(txd, 0, &word); + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00usb_register_read_lock(rt2x00dev, offset, reg); + if (!rt2x00_get_field32(*reg, field)) + return 1; + udelay(REGISTER_BUSY_DELAY); + } - /* - * Remove the descriptor data from the buffer. - */ - skb_pull(entry->skb, ring->desc_size); + rt2x00_err(rt2x00dev, "Indirect register access failed: offset=0x%.08x, value=0x%.08x\n", + offset, *reg); + *reg = ~0; - /* - * Obtain the status about this packet. - */ - tx_status = !urb->status ? TX_SUCCESS : TX_FAIL_RETRY; + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read); - rt2x00lib_txdone(entry, tx_status, 0); - /* - * Make this entry available for reuse. - */ - entry->flags = 0; - rt2x00_ring_index_done_inc(entry->ring); +struct rt2x00_async_read_data { + __le32 reg; + struct usb_ctrlrequest cr; + struct rt2x00_dev *rt2x00dev; + bool (*callback)(struct rt2x00_dev *, int, u32); +}; +static void rt2x00usb_register_read_async_cb(struct urb *urb) +{ + struct rt2x00_async_read_data *rd = urb->context; + if (rd->callback(rd->rt2x00dev, urb->status, le32_to_cpu(rd->reg))) { + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) + kfree(rd); + } else + kfree(rd); +} + +void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + bool (*callback)(struct rt2x00_dev*, int, u32)) +{ + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct urb *urb; + struct rt2x00_async_read_data *rd; + + rd = kmalloc(sizeof(*rd), GFP_ATOMIC); + if (!rd) + return; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + kfree(rd); + return; + } + + rd->rt2x00dev = rt2x00dev; + rd->callback = callback; + rd->cr.bRequestType = USB_VENDOR_REQUEST_IN; + rd->cr.bRequest = USB_MULTI_READ; + rd->cr.wValue = 0; + rd->cr.wIndex = cpu_to_le16(offset); + rd->cr.wLength = cpu_to_le16(sizeof(u32)); + + usb_fill_control_urb(urb, usb_dev, usb_rcvctrlpipe(usb_dev, 0), + (unsigned char *)(&rd->cr), &rd->reg, sizeof(rd->reg), + rt2x00usb_register_read_async_cb, rd); + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) + kfree(rd); + usb_free_urb(urb); +} +EXPORT_SYMBOL_GPL(rt2x00usb_register_read_async); + +/* + * TX data handlers. + */ +static void rt2x00usb_work_txdone_entry(struct queue_entry *entry) +{ /* - * If the data ring was full before the txdone handler - * we must make sure the packet queue in the mac80211 stack - * is reenabled when the txdone handler has finished. + * If the transfer to hardware succeeded, it does not mean the + * frame was send out correctly. It only means the frame + * was successfully pushed to the hardware, we have no + * way to determine the transmission status right now. + * (Only indirectly by looking at the failed TX counters + * in the register). */ - if (!rt2x00_ring_full(ring)) - ieee80211_wake_queue(rt2x00dev->hw, - entry->tx_status.control.queue); + if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) + rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE); + else + rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN); } -int rt2x00usb_write_tx_data(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring, struct sk_buff *skb, - struct ieee80211_tx_control *control) +static void rt2x00usb_work_txdone(struct work_struct *work) { - struct usb_device *usb_dev = - interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); - struct data_entry *entry = rt2x00_get_data_entry(ring); - struct skb_desc *desc; - u32 length; + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, txdone_work); + struct data_queue *queue; + struct queue_entry *entry; + + tx_queue_for_each(rt2x00dev, queue) { + while (!rt2x00queue_empty(queue)) { + entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE); - if (rt2x00_ring_full(ring)) - return -EINVAL; + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + break; - if (test_bit(ENTRY_OWNER_NIC, &entry->flags)) { - ERROR(rt2x00dev, - "Arrived at non-free entry in the non-full queue %d.\n" - "Please file bug report to %s.\n", - control->queue, DRV_PROJECT); - return -EINVAL; + rt2x00usb_work_txdone_entry(entry); + } } +} + +static void rt2x00usb_interrupt_txdone(struct urb *urb) +{ + struct queue_entry *entry = (struct queue_entry *)urb->context; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return; /* - * Add the descriptor in front of the skb. + * Check if the frame was correctly uploaded */ - skb_push(skb, ring->desc_size); - memset(skb->data, 0, ring->desc_size); - + if (urb->status) + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); /* - * Fill in skb descriptor + * Report the frame as DMA done */ - desc = get_skb_desc(skb); - desc->desc_len = ring->desc_size; - desc->data_len = skb->len - ring->desc_size; - desc->desc = skb->data; - desc->data = skb->data + ring->desc_size; - desc->ring = ring; - desc->entry = entry; - - rt2x00lib_write_tx_desc(rt2x00dev, skb, control); + rt2x00lib_dmadone(entry); + if (rt2x00dev->ops->lib->tx_dma_done) + rt2x00dev->ops->lib->tx_dma_done(entry); /* - * USB devices cannot blindly pass the skb->len as the - * length of the data to usb_fill_bulk_urb. Pass the skb - * to the driver to determine what the length should be. + * Schedule the delayed work for reading the TX status + * from the device. */ - length = rt2x00dev->ops->lib->get_tx_data_len(rt2x00dev, skb); + if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) || + !kfifo_is_empty(&rt2x00dev->txstatus_fifo)) + queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); +} + +static bool rt2x00usb_kick_tx_entry(struct queue_entry *entry, void *data) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + u32 length; + int status; + + if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags) || + test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + return false; /* - * Initialize URB and send the frame to the device. + * USB devices require certain padding at the end of each frame + * and urb. Those paddings are not included in skbs. Pass entry + * to the driver to determine what the overall length should be. */ - __set_bit(ENTRY_OWNER_NIC, &entry->flags); - usb_fill_bulk_urb(entry->priv, usb_dev, usb_sndbulkpipe(usb_dev, 1), - skb->data, length, rt2x00usb_interrupt_txdone, entry); - usb_submit_urb(entry->priv, GFP_ATOMIC); + length = rt2x00dev->ops->lib->get_tx_data_len(entry); - rt2x00_ring_index_inc(ring); + status = skb_padto(entry->skb, length); + if (unlikely(status)) { + /* TODO: report something more appropriate than IO_FAILED. */ + rt2x00_warn(rt2x00dev, "TX SKB padding error, out of memory\n"); + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); - return 0; + return false; + } + + usb_fill_bulk_urb(entry_priv->urb, usb_dev, + usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint), + entry->skb->data, length, + rt2x00usb_interrupt_txdone, entry); + + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); + } + + return false; } -EXPORT_SYMBOL_GPL(rt2x00usb_write_tx_data); /* * RX data handlers. */ +static void rt2x00usb_work_rxdone(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, rxdone_work); + struct queue_entry *entry; + struct skb_frame_desc *skbdesc; + u8 rxd[32]; + + while (!rt2x00queue_empty(rt2x00dev->rx)) { + entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE); + + if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + break; + + /* + * Fill in desc fields of the skb descriptor + */ + skbdesc = get_skb_frame_desc(entry->skb); + skbdesc->desc = rxd; + skbdesc->desc_len = entry->queue->desc_size; + + /* + * Send the frame to rt2x00lib for further processing. + */ + rt2x00lib_rxdone(entry, GFP_KERNEL); + } +} + static void rt2x00usb_interrupt_rxdone(struct urb *urb) { - struct data_entry *entry = (struct data_entry *)urb->context; - struct data_ring *ring = entry->ring; - struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; - struct sk_buff *skb; - struct ieee80211_hdr *hdr; - struct skb_desc *skbdesc; - struct rxdata_entry_desc desc; - int header_size; - int frame_size; - - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags) || - !test_and_clear_bit(ENTRY_OWNER_NIC, &entry->flags)) + struct queue_entry *entry = (struct queue_entry *)urb->context; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + + if (!test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return; /* + * Report the frame as DMA done + */ + rt2x00lib_dmadone(entry); + + /* * Check if the received data is simply too small * to be actually valid, or if the urb is signaling * a problem. */ - if (urb->actual_length < entry->ring->desc_size || urb->status) - goto skip_entry; + if (urb->actual_length < entry->queue->desc_size || urb->status) + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); /* - * Fill in skb descriptor + * Schedule the delayed work for reading the RX status + * from the device. */ - skbdesc = get_skb_desc(entry->skb); - skbdesc->ring = ring; - skbdesc->entry = entry; + queue_work(rt2x00dev->workqueue, &rt2x00dev->rxdone_work); +} - memset(&desc, 0, sizeof(desc)); - rt2x00dev->ops->lib->fill_rxdone(entry, &desc); +static bool rt2x00usb_kick_rx_entry(struct queue_entry *entry, void *data) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev); + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + int status; - /* - * Allocate a new sk buffer to replace the current one. - * If allocation fails, we should drop the current frame - * so we can recycle the existing sk buffer for the new frame. - * As alignment we use 2 and not NET_IP_ALIGN because we need - * to be sure we have 2 bytes room in the head. (NET_IP_ALIGN - * can be 0 on some hardware). We use these 2 bytes for frame - * alignment later, we assume that the chance that - * header_size % 4 == 2 is bigger then header_size % 2 == 0 - * and thus optimize alignment by reserving the 2 bytes in - * advance. - */ - frame_size = entry->ring->data_size + entry->ring->desc_size; - skb = dev_alloc_skb(frame_size + 2); - if (!skb) - goto skip_entry; + if (test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) || + test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags)) + return false; - skb_reserve(skb, 2); - skb_put(skb, frame_size); + rt2x00lib_dmastart(entry); - /* - * The data behind the ieee80211 header must be - * aligned on a 4 byte boundary. - */ - hdr = (struct ieee80211_hdr *)entry->skb->data; - header_size = - ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + usb_fill_bulk_urb(entry_priv->urb, usb_dev, + usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint), + entry->skb->data, entry->skb->len, + rt2x00usb_interrupt_rxdone, entry); - if (header_size % 4 == 0) { - skb_push(entry->skb, 2); - memmove(entry->skb->data, entry->skb->data + 2, skb->len - 2); + status = usb_submit_urb(entry_priv->urb, GFP_ATOMIC); + if (status) { + if (status == -ENODEV) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + set_bit(ENTRY_DATA_IO_FAILED, &entry->flags); + rt2x00lib_dmadone(entry); } - /* - * Trim the entire buffer down to only contain the valid frame data - * excluding the device descriptor. The position of the descriptor - * varies. This means that we should check where the descriptor is - * and decide if we need to pull the data pointer to exclude the - * device descriptor. - */ - if (skbdesc->data > skbdesc->desc) - skb_pull(entry->skb, skbdesc->desc_len); - skb_trim(entry->skb, desc.size); + return false; +} + +void rt2x00usb_kick_queue(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + if (!rt2x00queue_empty(queue)) + rt2x00queue_for_each_entry(queue, + Q_INDEX_DONE, + Q_INDEX, + NULL, + rt2x00usb_kick_tx_entry); + break; + case QID_RX: + if (!rt2x00queue_full(queue)) + rt2x00queue_for_each_entry(queue, + Q_INDEX, + Q_INDEX_DONE, + NULL, + rt2x00usb_kick_rx_entry); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2x00usb_kick_queue); + +static bool rt2x00usb_flush_entry(struct queue_entry *entry, void *data) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_usb *entry_priv = entry->priv_data; + struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data; + + if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) + return false; + + usb_kill_urb(entry_priv->urb); /* - * Send the frame to rt2x00lib for further processing. + * Kill guardian urb (if required by driver). */ - rt2x00lib_rxdone(entry, entry->skb, &desc); + if ((entry->queue->qid == QID_BEACON) && + (test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags))) + usb_kill_urb(bcn_priv->guardian_urb); + + return false; +} + +void rt2x00usb_flush_queue(struct data_queue *queue, bool drop) +{ + struct work_struct *completion; + unsigned int i; + + if (drop) + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX, NULL, + rt2x00usb_flush_entry); /* - * Replace current entry's skb with the newly allocated one, - * and reinitialize the urb. + * Obtain the queue completion handler */ - entry->skb = skb; - urb->transfer_buffer = entry->skb->data; - urb->transfer_buffer_length = entry->skb->len; - -skip_entry: - if (test_bit(DEVICE_ENABLED_RADIO, &ring->rt2x00dev->flags)) { - __set_bit(ENTRY_OWNER_NIC, &entry->flags); - usb_submit_urb(urb, GFP_ATOMIC); + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + completion = &queue->rt2x00dev->txdone_work; + break; + case QID_RX: + completion = &queue->rt2x00dev->rxdone_work; + break; + default: + return; + } + + for (i = 0; i < 10; i++) { + /* + * Check if the driver is already done, otherwise we + * have to sleep a little while to give the driver/hw + * the oppurtunity to complete interrupt process itself. + */ + if (rt2x00queue_empty(queue)) + break; + + /* + * Schedule the completion handler manually, when this + * worker function runs, it should cleanup the queue. + */ + queue_work(queue->rt2x00dev->workqueue, completion); + + /* + * Wait for a little while to give the driver + * the oppurtunity to recover itself. + */ + msleep(10); } +} +EXPORT_SYMBOL_GPL(rt2x00usb_flush_queue); + +static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) +{ + rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n", + queue->qid); + + rt2x00queue_stop_queue(queue); + rt2x00queue_flush_queue(queue, true); + rt2x00queue_start_queue(queue); +} + +static int rt2x00usb_dma_timeout(struct data_queue *queue) +{ + struct queue_entry *entry; - rt2x00_ring_index_inc(ring); + entry = rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE); + return rt2x00queue_dma_timeout(entry); } +void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + + tx_queue_for_each(rt2x00dev, queue) { + if (!rt2x00queue_empty(queue)) { + if (rt2x00usb_dma_timeout(queue)) + rt2x00usb_watchdog_tx_dma(queue); + } + } +} +EXPORT_SYMBOL_GPL(rt2x00usb_watchdog); + /* * Radio handlers */ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev) { - struct data_ring *ring; - unsigned int i; - - rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0x0000, 0x0000, + rt2x00usb_vendor_request_sw(rt2x00dev, USB_RX_CONTROL, 0, 0, REGISTER_TIMEOUT); - - /* - * Cancel all rings. - */ - ring_for_each(rt2x00dev, ring) { - for (i = 0; i < ring->stats.limit; i++) - usb_kill_urb(ring->entry[i].priv); - } } EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio); /* * Device initialization handlers. */ -void rt2x00usb_init_rxentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry) +void rt2x00usb_clear_entry(struct queue_entry *entry) { - struct usb_device *usb_dev = - interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); + entry->flags = 0; - usb_fill_bulk_urb(entry->priv, usb_dev, - usb_rcvbulkpipe(usb_dev, 1), - entry->skb->data, entry->skb->len, - rt2x00usb_interrupt_rxdone, entry); + if (entry->queue->qid == QID_RX) + rt2x00usb_kick_rx_entry(entry, NULL); +} +EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry); + +static void rt2x00usb_assign_endpoint(struct data_queue *queue, + struct usb_endpoint_descriptor *ep_desc) +{ + struct usb_device *usb_dev = to_usb_device_intf(queue->rt2x00dev->dev); + int pipe; + + queue->usb_endpoint = usb_endpoint_num(ep_desc); - __set_bit(ENTRY_OWNER_NIC, &entry->flags); - usb_submit_urb(entry->priv, GFP_ATOMIC); + if (queue->qid == QID_RX) { + pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint); + queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0); + } else { + pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint); + queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1); + } + + if (!queue->usb_maxpacket) + queue->usb_maxpacket = 1; } -EXPORT_SYMBOL_GPL(rt2x00usb_init_rxentry); -void rt2x00usb_init_txentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry) +static int rt2x00usb_find_endpoints(struct rt2x00_dev *rt2x00dev) { - entry->flags = 0; + struct usb_interface *intf = to_usb_interface(rt2x00dev->dev); + struct usb_host_interface *intf_desc = intf->cur_altsetting; + struct usb_endpoint_descriptor *ep_desc; + struct data_queue *queue = rt2x00dev->tx; + struct usb_endpoint_descriptor *tx_ep_desc = NULL; + unsigned int i; + + /* + * Walk through all available endpoints to search for "bulk in" + * and "bulk out" endpoints. When we find such endpoints collect + * the information we need from the descriptor and assign it + * to the queue. + */ + for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { + ep_desc = &intf_desc->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(ep_desc)) { + rt2x00usb_assign_endpoint(rt2x00dev->rx, ep_desc); + } else if (usb_endpoint_is_bulk_out(ep_desc) && + (queue != queue_end(rt2x00dev))) { + rt2x00usb_assign_endpoint(queue, ep_desc); + queue = queue_next(queue); + + tx_ep_desc = ep_desc; + } + } + + /* + * At least 1 endpoint for RX and 1 endpoint for TX must be available. + */ + if (!rt2x00dev->rx->usb_endpoint || !rt2x00dev->tx->usb_endpoint) { + rt2x00_err(rt2x00dev, "Bulk-in/Bulk-out endpoints not found\n"); + return -EPIPE; + } + + /* + * It might be possible not all queues have a dedicated endpoint. + * Loop through all TX queues and copy the endpoint information + * which we have gathered from already assigned endpoints. + */ + txall_queue_for_each(rt2x00dev, queue) { + if (!queue->usb_endpoint) + rt2x00usb_assign_endpoint(queue, tx_ep_desc); + } + + return 0; } -EXPORT_SYMBOL_GPL(rt2x00usb_init_txentry); -static int rt2x00usb_alloc_urb(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring) +static int rt2x00usb_alloc_entries(struct data_queue *queue) { + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct queue_entry_priv_usb *entry_priv; + struct queue_entry_priv_usb_bcn *bcn_priv; unsigned int i; + for (i = 0; i < queue->limit; i++) { + entry_priv = queue->entries[i].priv_data; + entry_priv->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!entry_priv->urb) + return -ENOMEM; + } + /* - * Allocate the URB's + * If this is not the beacon queue or + * no guardian byte was required for the beacon, + * then we are done. */ - for (i = 0; i < ring->stats.limit; i++) { - ring->entry[i].priv = usb_alloc_urb(0, GFP_KERNEL); - if (!ring->entry[i].priv) + if (queue->qid != QID_BEACON || + !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) + return 0; + + for (i = 0; i < queue->limit; i++) { + bcn_priv = queue->entries[i].priv_data; + bcn_priv->guardian_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!bcn_priv->guardian_urb) return -ENOMEM; } return 0; } -static void rt2x00usb_free_urb(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring) +static void rt2x00usb_free_entries(struct data_queue *queue) { + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct queue_entry_priv_usb *entry_priv; + struct queue_entry_priv_usb_bcn *bcn_priv; unsigned int i; - if (!ring->entry) + if (!queue->entries) return; - for (i = 0; i < ring->stats.limit; i++) { - usb_kill_urb(ring->entry[i].priv); - usb_free_urb(ring->entry[i].priv); - if (ring->entry[i].skb) - kfree_skb(ring->entry[i].skb); + for (i = 0; i < queue->limit; i++) { + entry_priv = queue->entries[i].priv_data; + usb_kill_urb(entry_priv->urb); + usb_free_urb(entry_priv->urb); + } + + /* + * If this is not the beacon queue or + * no guardian byte was required for the beacon, + * then we are done. + */ + if (queue->qid != QID_BEACON || + !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) + return; + + for (i = 0; i < queue->limit; i++) { + bcn_priv = queue->entries[i].priv_data; + usb_kill_urb(bcn_priv->guardian_urb); + usb_free_urb(bcn_priv->guardian_urb); } } int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) { - struct data_ring *ring; - struct sk_buff *skb; - unsigned int entry_size; - unsigned int i; - int uninitialized_var(status); + struct data_queue *queue; + int status; /* - * Allocate DMA + * Find endpoints for each queue */ - ring_for_each(rt2x00dev, ring) { - status = rt2x00usb_alloc_urb(rt2x00dev, ring); - if (status) - goto exit; - } + status = rt2x00usb_find_endpoints(rt2x00dev); + if (status) + goto exit; /* - * For the RX ring, skb's should be allocated. + * Allocate DMA */ - entry_size = rt2x00dev->rx->data_size + rt2x00dev->rx->desc_size; - for (i = 0; i < rt2x00dev->rx->stats.limit; i++) { - skb = dev_alloc_skb(NET_IP_ALIGN + entry_size); - if (!skb) + queue_for_each(rt2x00dev, queue) { + status = rt2x00usb_alloc_entries(queue); + if (status) goto exit; - - skb_reserve(skb, NET_IP_ALIGN); - skb_put(skb, entry_size); - - rt2x00dev->rx->entry[i].skb = skb; } return 0; @@ -456,10 +742,10 @@ EXPORT_SYMBOL_GPL(rt2x00usb_initialize); void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) { - struct data_ring *ring; + struct data_queue *queue; - ring_for_each(rt2x00dev, ring) - rt2x00usb_free_urb(rt2x00dev, ring); + queue_for_each(rt2x00dev, queue) + rt2x00usb_free_entries(queue); } EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize); @@ -474,14 +760,14 @@ static void rt2x00usb_free_reg(struct rt2x00_dev *rt2x00dev) kfree(rt2x00dev->eeprom); rt2x00dev->eeprom = NULL; - kfree(rt2x00dev->csr_cache); - rt2x00dev->csr_cache = NULL; + kfree(rt2x00dev->csr.cache); + rt2x00dev->csr.cache = NULL; } static int rt2x00usb_alloc_reg(struct rt2x00_dev *rt2x00dev) { - rt2x00dev->csr_cache = kzalloc(CSR_CACHE_SIZE, GFP_KERNEL); - if (!rt2x00dev->csr_cache) + rt2x00dev->csr.cache = kzalloc(CSR_CACHE_SIZE, GFP_KERNEL); + if (!rt2x00dev->csr.cache) goto exit; rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); @@ -495,7 +781,7 @@ static int rt2x00usb_alloc_reg(struct rt2x00_dev *rt2x00dev) return 0; exit: - ERROR_PROBE("Failed to allocate registers.\n"); + rt2x00_probe_err("Failed to allocate registers\n"); rt2x00usb_free_reg(rt2x00dev); @@ -503,19 +789,19 @@ exit: } int rt2x00usb_probe(struct usb_interface *usb_intf, - const struct usb_device_id *id) + const struct rt2x00_ops *ops) { struct usb_device *usb_dev = interface_to_usbdev(usb_intf); - struct rt2x00_ops *ops = (struct rt2x00_ops *)id->driver_info; struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; int retval; usb_dev = usb_get_dev(usb_dev); + usb_reset_device(usb_dev); hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { - ERROR_PROBE("Failed to allocate hardware.\n"); + rt2x00_probe_err("Failed to allocate hardware\n"); retval = -ENOMEM; goto exit_put_device; } @@ -523,15 +809,16 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, usb_set_intfdata(usb_intf, hw); rt2x00dev = hw->priv; - rt2x00dev->dev = usb_intf; + rt2x00dev->dev = &usb_intf->dev; rt2x00dev->ops = ops; rt2x00dev->hw = hw; - mutex_init(&rt2x00dev->usb_cache_mutex); - rt2x00dev->usb_maxpacket = - usb_maxpacket(usb_dev, usb_sndbulkpipe(usb_dev, 1), 1); - if (!rt2x00dev->usb_maxpacket) - rt2x00dev->usb_maxpacket = 1; + rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); + + INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); + INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); + hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); retval = rt2x00usb_alloc_reg(rt2x00dev); if (retval) @@ -583,20 +870,8 @@ int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state) { struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); struct rt2x00_dev *rt2x00dev = hw->priv; - int retval; - - retval = rt2x00lib_suspend(rt2x00dev, state); - if (retval) - return retval; - rt2x00usb_free_reg(rt2x00dev); - - /* - * Decrease usbdev refcount. - */ - usb_put_dev(interface_to_usbdev(usb_intf)); - - return 0; + return rt2x00lib_suspend(rt2x00dev, state); } EXPORT_SYMBOL_GPL(rt2x00usb_suspend); @@ -604,32 +879,16 @@ int rt2x00usb_resume(struct usb_interface *usb_intf) { struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); struct rt2x00_dev *rt2x00dev = hw->priv; - int retval; - - usb_get_dev(interface_to_usbdev(usb_intf)); - retval = rt2x00usb_alloc_reg(rt2x00dev); - if (retval) - return retval; - - retval = rt2x00lib_resume(rt2x00dev); - if (retval) - goto exit_free_reg; - - return 0; - -exit_free_reg: - rt2x00usb_free_reg(rt2x00dev); - - return retval; + return rt2x00lib_resume(rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00usb_resume); #endif /* CONFIG_PM */ /* - * rt2x00pci module information. + * rt2x00usb module information. */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); -MODULE_DESCRIPTION("rt2x00 library"); +MODULE_DESCRIPTION("rt2x00 usb library"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index e40df4050cd..831b65f93fe 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -26,32 +24,41 @@ #ifndef RT2X00USB_H #define RT2X00USB_H -/* - * This variable should be used with the - * usb_driver structure initialization. - */ -#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) +#include <linux/usb.h> + +#define to_usb_device_intf(d) \ +({ \ + struct usb_interface *intf = to_usb_interface(d); \ + interface_to_usbdev(intf); \ +}) /* - * Register defines. - * Some registers require multiple attempts before success, - * in those cases REGISTER_BUSY_COUNT attempts should be - * taken with a REGISTER_BUSY_DELAY interval. * For USB vendor requests we need to pass a timeout * time in ms, for this we use the REGISTER_TIMEOUT, * however when loading firmware a higher value is * required. In that case we use the REGISTER_TIMEOUT_FIRMWARE. */ -#define REGISTER_BUSY_COUNT 5 -#define REGISTER_BUSY_DELAY 100 #define REGISTER_TIMEOUT 500 #define REGISTER_TIMEOUT_FIRMWARE 1000 +/** + * REGISTER_TIMEOUT16 - Determine the timeout for 16bit register access + * @__datalen: Data length + */ +#define REGISTER_TIMEOUT16(__datalen) \ + ( REGISTER_TIMEOUT * ((__datalen) / sizeof(u16)) ) + +/** + * REGISTER_TIMEOUT32 - Determine the timeout for 32bit register access + * @__datalen: Data length + */ +#define REGISTER_TIMEOUT32(__datalen) \ + ( REGISTER_TIMEOUT * ((__datalen) / sizeof(u32)) ) + /* * Cache size */ -#define CSR_CACHE_SIZE 8 -#define CSR_CACHE_SIZE_FIRMWARE 64 +#define CSR_CACHE_SIZE 64 /* * USB request types. @@ -60,34 +67,48 @@ #define USB_VENDOR_REQUEST_IN ( USB_DIR_IN | USB_VENDOR_REQUEST ) #define USB_VENDOR_REQUEST_OUT ( USB_DIR_OUT | USB_VENDOR_REQUEST ) -/* - * USB vendor commands. - */ -#define USB_DEVICE_MODE 0x01 -#define USB_SINGLE_WRITE 0x02 -#define USB_SINGLE_READ 0x03 -#define USB_MULTI_WRITE 0x06 -#define USB_MULTI_READ 0x07 -#define USB_EEPROM_WRITE 0x08 -#define USB_EEPROM_READ 0x09 -#define USB_LED_CONTROL 0x0a /* RT73USB */ -#define USB_RX_CONTROL 0x0c +/** + * enum rt2x00usb_vendor_request: USB vendor commands. + */ +enum rt2x00usb_vendor_request { + USB_DEVICE_MODE = 1, + USB_SINGLE_WRITE = 2, + USB_SINGLE_READ = 3, + USB_MULTI_WRITE = 6, + USB_MULTI_READ = 7, + USB_EEPROM_WRITE = 8, + USB_EEPROM_READ = 9, + USB_LED_CONTROL = 10, /* RT73USB */ + USB_RX_CONTROL = 12, +}; -/* - * Device modes offset +/** + * enum rt2x00usb_mode_offset: Device modes offset. */ -#define USB_MODE_RESET 0x01 -#define USB_MODE_UNPLUG 0x02 -#define USB_MODE_FUNCTION 0x03 -#define USB_MODE_TEST 0x04 -#define USB_MODE_SLEEP 0x07 /* RT73USB */ -#define USB_MODE_FIRMWARE 0x08 /* RT73USB */ -#define USB_MODE_WAKEUP 0x09 /* RT73USB */ +enum rt2x00usb_mode_offset { + USB_MODE_RESET = 1, + USB_MODE_UNPLUG = 2, + USB_MODE_FUNCTION = 3, + USB_MODE_TEST = 4, + USB_MODE_SLEEP = 7, /* RT73USB */ + USB_MODE_FIRMWARE = 8, /* RT73USB */ + USB_MODE_WAKEUP = 9, /* RT73USB */ + USB_MODE_AUTORUN = 17, /* RT2800USB */ +}; -/* - * Used to read/write from/to the device. +/** + * rt2x00usb_vendor_request - Send register command to device + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @requesttype: Request type &USB_VENDOR_REQUEST_* + * @offset: Register offset to perform action on + * @value: Value to write to device + * @buffer: Buffer where information will be read/written to by device + * @buffer_length: Size of &buffer + * @timeout: Operation timeout + * * This is the main function to communicate with the device, - * the buffer argument _must_ either be NULL or point to + * the &buffer argument _must_ either be NULL or point to * a buffer allocated by kmalloc. Failure to do so can lead * to unexpected behavior depending on the architecture. */ @@ -97,13 +118,21 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, void *buffer, const u16 buffer_length, const int timeout); -/* - * Used to read/write from/to the device. +/** + * rt2x00usb_vendor_request_buff - Send register command to device (buffered) + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @requesttype: Request type &USB_VENDOR_REQUEST_* + * @offset: Register offset to perform action on + * @buffer: Buffer where information will be read/written to by device + * @buffer_length: Size of &buffer + * @timeout: Operation timeout + * * This function will use a previously with kmalloc allocated cache * to communicate with the device. The contents of the buffer pointer * will be copied to this cache when writing, or read from the cache * when reading. - * Buffers send to rt2x00usb_vendor_request _must_ be allocated with + * Buffers send to &rt2x00usb_vendor_request _must_ be allocated with * kmalloc. Hence the reason for using a previously allocated cache * which has been allocated properly. */ @@ -112,15 +141,32 @@ int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, const u16 offset, void *buffer, const u16 buffer_length, const int timeout); -/* - * A version of rt2x00usb_vendor_request_buff which must be called - * if the usb_cache_mutex is already held. */ +/** + * rt2x00usb_vendor_request_buff - Send register command to device (buffered) + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @requesttype: Request type &USB_VENDOR_REQUEST_* + * @offset: Register offset to perform action on + * @buffer: Buffer where information will be read/written to by device + * @buffer_length: Size of &buffer + * @timeout: Operation timeout + * + * A version of &rt2x00usb_vendor_request_buff which must be called + * if the usb_cache_mutex is already held. + */ int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, const u16 offset, void *buffer, const u16 buffer_length, const int timeout); -/* +/** + * rt2x00usb_vendor_request_sw - Send single register command to device + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @offset: Register offset to perform action on + * @value: Value to write to device + * @timeout: Operation timeout + * * Simple wrapper around rt2x00usb_vendor_request to write a single * command to the device. Since we don't use the buffer argument we * don't have to worry about kmalloc here. @@ -136,40 +182,245 @@ static inline int rt2x00usb_vendor_request_sw(struct rt2x00_dev *rt2x00dev, value, NULL, 0, timeout); } -/* +/** + * rt2x00usb_eeprom_read - Read eeprom from device + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @eeprom: Pointer to eeprom array to store the information in + * @length: Number of bytes to read from the eeprom + * * Simple wrapper around rt2x00usb_vendor_request to read the eeprom * from the device. Note that the eeprom argument _must_ be allocated using * kmalloc for correct handling inside the kernel USB layer. */ static inline int rt2x00usb_eeprom_read(struct rt2x00_dev *rt2x00dev, - __le16 *eeprom, const u16 lenght) + __le16 *eeprom, const u16 length) { - int timeout = REGISTER_TIMEOUT * (lenght / sizeof(u16)); - return rt2x00usb_vendor_request(rt2x00dev, USB_EEPROM_READ, - USB_VENDOR_REQUEST_IN, 0x0000, - 0x0000, eeprom, lenght, timeout); + USB_VENDOR_REQUEST_IN, 0, 0, + eeprom, length, + REGISTER_TIMEOUT16(length)); +} + +/** + * rt2x00usb_register_read - Read 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Pointer to where register contents should be stored + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_request_buff(). + */ +static inline void rt2x00usb_register_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + __le32 reg; + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + ®, sizeof(reg), REGISTER_TIMEOUT); + *value = le32_to_cpu(reg); } +/** + * rt2x00usb_register_read_lock - Read 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Pointer to where register contents should be stored + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_req_buff_lock(). + */ +static inline void rt2x00usb_register_read_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 *value) +{ + __le32 reg; + rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + ®, sizeof(reg), REGISTER_TIMEOUT); + *value = le32_to_cpu(reg); +} + +/** + * rt2x00usb_register_multiread - Read 32bit register words + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Pointer to where register contents should be stored + * @length: Length of the data + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_request_buff(). + */ +static inline void rt2x00usb_register_multiread(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + void *value, const u32 length) +{ + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, + USB_VENDOR_REQUEST_IN, offset, + value, length, + REGISTER_TIMEOUT32(length)); +} + +/** + * rt2x00usb_register_write - Write 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Data which should be written + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_request_buff(). + */ +static inline void rt2x00usb_register_write(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + __le32 reg = cpu_to_le32(value); + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + ®, sizeof(reg), REGISTER_TIMEOUT); +} + +/** + * rt2x00usb_register_write_lock - Write 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Data which should be written + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_req_buff_lock(). + */ +static inline void rt2x00usb_register_write_lock(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + u32 value) +{ + __le32 reg = cpu_to_le32(value); + rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + ®, sizeof(reg), REGISTER_TIMEOUT); +} + +/** + * rt2x00usb_register_multiwrite - Write 32bit register words + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @value: Data which should be written + * @length: Length of the data + * + * This function is a simple wrapper for 32bit register access + * through rt2x00usb_vendor_request_buff(). + */ +static inline void rt2x00usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const void *value, + const u32 length) +{ + rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, offset, + (void *)value, length, + REGISTER_TIMEOUT32(length)); +} + +/** + * rt2x00usb_regbusy_read - Read from register with busy check + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @field: Field to check if register is busy + * @reg: Pointer to where register contents should be stored + * + * This function will read the given register, and checks if the + * register is busy. If it is, it will sleep for a couple of + * microseconds before reading the register again. If the register + * is not read after a certain timeout, this function will return + * FALSE. + */ +int rt2x00usb_regbusy_read(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + const struct rt2x00_field32 field, + u32 *reg); + +/** + * rt2x00usb_register_read_async - Asynchronously read 32bit register word + * @rt2x00dev: Device pointer, see &struct rt2x00_dev. + * @offset: Register offset + * @callback: Functon to call when read completes. + * + * Submit a control URB to read a 32bit register. This safe to + * be called from atomic context. The callback will be called + * when the URB completes. Otherwise the function is similar + * to rt2x00usb_register_read(). + * When the callback function returns false, the memory will be cleaned up, + * when it returns true, the urb will be fired again. + */ +void rt2x00usb_register_read_async(struct rt2x00_dev *rt2x00dev, + const unsigned int offset, + bool (*callback)(struct rt2x00_dev*, int, u32)); + /* * Radio handlers */ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev); -/* - * TX data handlers. +/** + * struct queue_entry_priv_usb: Per entry USB specific information + * + * @urb: Urb structure used for device communication. + */ +struct queue_entry_priv_usb { + struct urb *urb; +}; + +/** + * struct queue_entry_priv_usb_bcn: Per TX entry USB specific information + * + * The first section should match &struct queue_entry_priv_usb exactly. + * rt2500usb can use this structure to send a guardian byte when working + * with beacons. + * + * @urb: Urb structure used for device communication. + * @guardian_data: Set to 0, used for sending the guardian data. + * @guardian_urb: Urb structure used to send the guardian data. + */ +struct queue_entry_priv_usb_bcn { + struct urb *urb; + + unsigned int guardian_data; + struct urb *guardian_urb; +}; + +/** + * rt2x00usb_kick_queue - Kick data queue + * @queue: Data queue to kick + * + * This will walk through all entries of the queue and push all pending + * frames to the hardware as a single burst. + */ +void rt2x00usb_kick_queue(struct data_queue *queue); + +/** + * rt2x00usb_flush_queue - Flush data queue + * @queue: Data queue to stop + * @drop: True to drop all pending frames. + * + * This will walk through all entries of the queue and will optionally + * kill all URB's which were send to the device, or at least wait until + * they have been returned from the device.. + */ +void rt2x00usb_flush_queue(struct data_queue *queue, bool drop); + +/** + * rt2x00usb_watchdog - Watchdog for USB communication + * @rt2x00dev: Pointer to &struct rt2x00_dev + * + * Check the health of the USB communication and determine + * if timeouts have occurred. If this is the case, this function + * will reset all communication to restore functionality again. */ -int rt2x00usb_write_tx_data(struct rt2x00_dev *rt2x00dev, - struct data_ring *ring, struct sk_buff *skb, - struct ieee80211_tx_control *control); +void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev); /* * Device initialization handlers. */ -void rt2x00usb_init_rxentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry); -void rt2x00usb_init_txentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry); +void rt2x00usb_clear_entry(struct queue_entry *entry); int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev); void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev); @@ -177,7 +428,7 @@ void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev); * USB driver handlers. */ int rt2x00usb_probe(struct usb_interface *usb_intf, - const struct usb_device_id *id); + const struct rt2x00_ops *ops); void rt2x00usb_disconnect(struct usb_interface *usb_intf); #ifdef CONFIG_PM int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state); diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 93ea212fedd..9048a9cbe52 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -24,68 +22,68 @@ Supported chipsets: RT2561, RT2561s, RT2661. */ +#include <linux/crc-itu-t.h> #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/pci.h> #include <linux/eeprom_93cx6.h> #include "rt2x00.h" +#include "rt2x00mmio.h" #include "rt2x00pci.h" #include "rt61pci.h" /* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt = false; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +/* * Register access. * BBP and RF register require indirect register access, * and use the CSR registers PHY_CSR3 and PHY_CSR4 to achieve this. * These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay - * between each attampt. When the busy bit is still set at that time, + * between each attempt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. */ -static u32 rt61pci_bbp_check(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - unsigned int i; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, PHY_CSR3, ®); - if (!rt2x00_get_field32(reg, PHY_CSR3_BUSY)) - break; - udelay(REGISTER_BUSY_DELAY); - } - - return reg; -} +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg)) +#define WAIT_FOR_MCU(__dev, __reg) \ + rt2x00mmio_regbusy_read((__dev), H2M_MAILBOX_CSR, \ + H2M_MAILBOX_CSR_OWNER, (__reg)) static void rt61pci_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; - /* - * Wait until the BBP becomes ready. - */ - reg = rt61pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n"); - return; - } + mutex_lock(&rt2x00dev->csr_mutex); /* - * Write the data into the BBP. + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. */ - reg = 0; - rt2x00_set_field32(®, PHY_CSR3_VALUE, value); - rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); - rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); - rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_VALUE, value); + rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); - rt2x00pci_register_write(rt2x00dev, PHY_CSR3, reg); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR3, reg); + } + + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt61pci_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -93,66 +91,55 @@ static void rt61pci_bbp_read(struct rt2x00_dev *rt2x00dev, { u32 reg; - /* - * Wait until the BBP becomes ready. - */ - reg = rt61pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); - return; - } + mutex_lock(&rt2x00dev->csr_mutex); /* - * Write the request into the BBP. + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. */ - reg = 0; - rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); - rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); - rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); - rt2x00pci_register_write(rt2x00dev, PHY_CSR3, reg); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR3, reg); - /* - * Wait until the BBP becomes ready. - */ - reg = rt61pci_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); - *value = 0xff; - return; + WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); + + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt61pci_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; - unsigned int i; - - if (!word) - return; - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, PHY_CSR4, ®); - if (!rt2x00_get_field32(reg, PHY_CSR4_BUSY)) - goto rf_write; - udelay(REGISTER_BUSY_DELAY); - } + mutex_lock(&rt2x00dev->csr_mutex); - ERROR(rt2x00dev, "PHY_CSR4 register busy. Write failed.\n"); - return; + /* + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR4_VALUE, value); + rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, 21); + rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); + rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); -rf_write: - reg = 0; - rt2x00_set_field32(®, PHY_CSR4_VALUE, value); - rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, 21); - rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); - rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR4, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } - rt2x00pci_register_write(rt2x00dev, PHY_CSR4, reg); - rt2x00_rf_write(rt2x00dev, word, value); + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt61pci_mcu_request(struct rt2x00_dev *rt2x00dev, @@ -161,25 +148,27 @@ static void rt61pci_mcu_request(struct rt2x00_dev *rt2x00dev, { u32 reg; - rt2x00pci_register_read(rt2x00dev, H2M_MAILBOX_CSR, ®); + mutex_lock(&rt2x00dev->csr_mutex); - if (rt2x00_get_field32(reg, H2M_MAILBOX_CSR_OWNER)) { - ERROR(rt2x00dev, "mcu request error. " - "Request 0x%02x failed for token 0x%02x.\n", - command, token); - return; + /* + * Wait until the MCU becomes available, afterwards we + * can safely write the new data into the register. + */ + if (WAIT_FOR_MCU(rt2x00dev, ®)) { + rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, HOST_CMD_CSR, ®); + rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); + rt2x00_set_field32(®, HOST_CMD_CSR_INTERRUPT_MCU, 1); + rt2x00mmio_register_write(rt2x00dev, HOST_CMD_CSR, reg); } - rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); - rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); - rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CSR, reg); + mutex_unlock(&rt2x00dev->csr_mutex); - rt2x00pci_register_read(rt2x00dev, HOST_CMD_CSR, ®); - rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); - rt2x00_set_field32(®, HOST_CMD_CSR_INTERRUPT_MCU, 1); - rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, reg); } static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom) @@ -187,7 +176,7 @@ static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom) struct rt2x00_dev *rt2x00dev = eeprom->data; u32 reg; - rt2x00pci_register_read(rt2x00dev, E2PROM_CSR, ®); + rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); eeprom->reg_data_in = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_IN); eeprom->reg_data_out = !!rt2x00_get_field32(reg, E2PROM_CSR_DATA_OUT); @@ -209,201 +198,429 @@ static void rt61pci_eepromregister_write(struct eeprom_93cx6 *eeprom) rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, !!eeprom->reg_chip_select); - rt2x00pci_register_write(rt2x00dev, E2PROM_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, E2PROM_CSR, reg); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS -#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) ) - -static void rt61pci_read_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 *data) -{ - rt2x00pci_register_read(rt2x00dev, CSR_OFFSET(word), data); -} - -static void rt61pci_write_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 data) -{ - rt2x00pci_register_write(rt2x00dev, CSR_OFFSET(word), data); -} - static const struct rt2x00debug rt61pci_rt2x00debug = { .owner = THIS_MODULE, .csr = { - .read = rt61pci_read_csr, - .write = rt61pci_write_csr, + .read = rt2x00mmio_register_read, + .write = rt2x00mmio_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt61pci_bbp_read, .write = rt61pci_bbp_write, + .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt61pci_rf_write, + .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT61PCI_RFKILL static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt2x00pci_register_read(rt2x00dev, MAC_CSR13, ®); - return rt2x00_get_field32(reg, MAC_CSR13_BIT5);; + rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); + return rt2x00_get_field32(reg, MAC_CSR13_VAL5); } -#else -#define rt61pci_rfkill_poll NULL -#endif /* CONFIG_RT61PCI_RFKILL */ -/* - * Configuration handlers. - */ -static void rt61pci_config_mac_addr(struct rt2x00_dev *rt2x00dev, __le32 *mac) +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt61pci_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) { - u32 tmp; - - tmp = le32_to_cpu(mac[1]); - rt2x00_set_field32(&tmp, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); - mac[1] = cpu_to_le32(tmp); - - rt2x00pci_register_multiwrite(rt2x00dev, MAC_CSR2, mac, - (2 * sizeof(__le32))); + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + unsigned int a_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); + unsigned int bg_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); + + if (led->type == LED_TYPE_RADIO) { + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_RADIO_STATUS, enabled); + + rt61pci_mcu_request(led->rt2x00dev, MCU_LED, 0xff, + (led->rt2x00dev->led_mcu_reg & 0xff), + ((led->rt2x00dev->led_mcu_reg >> 8))); + } else if (led->type == LED_TYPE_ASSOC) { + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_LINK_BG_STATUS, bg_mode); + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_LINK_A_STATUS, a_mode); + + rt61pci_mcu_request(led->rt2x00dev, MCU_LED, 0xff, + (led->rt2x00dev->led_mcu_reg & 0xff), + ((led->rt2x00dev->led_mcu_reg >> 8))); + } else if (led->type == LED_TYPE_QUALITY) { + /* + * The brightness is divided into 6 levels (0 - 5), + * this means we need to convert the brightness + * argument into the matching level within that range. + */ + rt61pci_mcu_request(led->rt2x00dev, MCU_LED_STRENGTH, 0xff, + brightness / (LED_FULL / 6), 0); + } } -static void rt61pci_config_bssid(struct rt2x00_dev *rt2x00dev, __le32 *bssid) +static int rt61pci_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) { - u32 tmp; + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; - tmp = le32_to_cpu(bssid[1]); - rt2x00_set_field32(&tmp, MAC_CSR5_BSS_ID_MASK, 3); - bssid[1] = cpu_to_le32(tmp); + rt2x00mmio_register_read(led->rt2x00dev, MAC_CSR14, ®); + rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, *delay_off); + rt2x00mmio_register_write(led->rt2x00dev, MAC_CSR14, reg); - rt2x00pci_register_multiwrite(rt2x00dev, MAC_CSR4, bssid, - (2 * sizeof(__le32))); + return 0; } -static void rt61pci_config_type(struct rt2x00_dev *rt2x00dev, const int type, - const int tsf_sync) +static void rt61pci_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) { + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt61pci_brightness_set; + led->led_dev.blink_set = rt61pci_blink_set; + led->flags = LED_INITIALIZED; +} +#endif /* CONFIG_RT2X00_LIB_LEDS */ + +/* + * Configuration handlers. + */ +static int rt61pci_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) +{ + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + u32 mask; u32 reg; - /* - * Clear current synchronisation setup. - * For the Beacon base registers we only need to clear - * the first byte since that byte contains the VALID and OWNER - * bits which (when set to 0) will invalidate the entire beacon. - */ - rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, 0); - rt2x00pci_register_write(rt2x00dev, HW_BEACON_BASE0, 0); - rt2x00pci_register_write(rt2x00dev, HW_BEACON_BASE1, 0); - rt2x00pci_register_write(rt2x00dev, HW_BEACON_BASE2, 0); - rt2x00pci_register_write(rt2x00dev, HW_BEACON_BASE3, 0); + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for shared keys. We have 1 register + * with key valid bits. The goal is simple, read + * the register, if that is full we have no slots + * left. + * Note that each BSS is allowed to have up to 4 + * shared keys, so put a mask over the allowed + * entries. + */ + mask = (0xf << crypto->bssidx); + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR0, ®); + reg &= mask; + + if (reg && reg == mask) + return -ENOSPC; + + key->hw_key_idx += reg ? ffz(reg) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = SHARED_KEY_ENTRY(key->hw_key_idx); + rt2x00mmio_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + /* + * The cipher types are stored over 2 registers. + * bssidx 0 and 1 keys are stored in SEC_CSR1 and + * bssidx 1 and 2 keys are stored in SEC_CSR5. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + if (key->hw_key_idx < 8) { + field.bit_offset = (3 * key->hw_key_idx); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR1, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR1, reg); + } else { + field.bit_offset = (3 * (key->hw_key_idx - 8)); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR5, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR5, reg); + } + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided separately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } /* - * Enable synchronisation. + * SEC_CSR0 contains only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead, we use + * a calculation to determine the correct bit directly. */ - rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, - (tsf_sync == TSF_SYNC_BEACON)); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, tsf_sync); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); + mask = 1 << key->hw_key_idx; + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR0, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00mmio_register_write(rt2x00dev, SEC_CSR0, reg); + + return 0; } -static void rt61pci_config_preamble(struct rt2x00_dev *rt2x00dev, - const int short_preamble, - const int ack_timeout, - const int ack_consume_time) +static int rt61pci_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) { + struct hw_pairwise_ta_entry addr_entry; + struct hw_key_entry key_entry; + u32 mask; u32 reg; - rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, ack_timeout); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for pairwise keys. We have 2 registers + * with key valid bits. The goal is simple: read + * the first register. If that is full, move to + * the next register. + * When both registers are full, we drop the key. + * Otherwise, we use the first invalid entry. + */ + rt2x00mmio_register_read(rt2x00dev, SEC_CSR2, ®); + if (reg && reg == ~0) { + key->hw_key_idx = 32; + rt2x00mmio_register_read(rt2x00dev, SEC_CSR3, ®); + if (reg && reg == ~0) + return -ENOSPC; + } - rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, - !!short_preamble); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg); -} + key->hw_key_idx += reg ? ffz(reg) : 0; -static void rt61pci_config_phymode(struct rt2x00_dev *rt2x00dev, - const int basic_rate_mask) -{ - rt2x00pci_register_write(rt2x00dev, TXRX_CSR5, basic_rate_mask); -} + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); -static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev, - struct rf_channel *rf, const int txpower) -{ - u8 r3; - u8 r94; - u8 smart; + memset(&addr_entry, 0, sizeof(addr_entry)); + memcpy(&addr_entry, crypto->address, ETH_ALEN); + addr_entry.cipher = crypto->cipher; - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + rt2x00mmio_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); - smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF2527)); + reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); + rt2x00mmio_register_multiwrite(rt2x00dev, reg, + &addr_entry, sizeof(addr_entry)); - rt61pci_bbp_read(rt2x00dev, 3, &r3); - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); - rt61pci_bbp_write(rt2x00dev, 3, r3); + /* + * Enable pairwise lookup table for given BSS idx. + * Without this, received frames will not be decrypted + * by the hardware. + */ + rt2x00mmio_register_read(rt2x00dev, SEC_CSR4, ®); + reg |= (1 << crypto->bssidx); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR4, reg); - r94 = 6; - if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) - r94 += txpower - MAX_TXPOWER; - else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) - r94 += txpower; - rt61pci_bbp_write(rt2x00dev, 94, r94); + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided separately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } - rt61pci_rf_write(rt2x00dev, 1, rf->rf1); - rt61pci_rf_write(rt2x00dev, 2, rf->rf2); - rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt61pci_rf_write(rt2x00dev, 4, rf->rf4); + /* + * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead, we use + * a calculation to determine the correct bit directly. + */ + if (key->hw_key_idx < 32) { + mask = 1 << key->hw_key_idx; - udelay(200); + rt2x00mmio_register_read(rt2x00dev, SEC_CSR2, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00mmio_register_write(rt2x00dev, SEC_CSR2, reg); + } else { + mask = 1 << (key->hw_key_idx - 32); + + rt2x00mmio_register_read(rt2x00dev, SEC_CSR3, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00mmio_register_write(rt2x00dev, SEC_CSR3, reg); + } - rt61pci_rf_write(rt2x00dev, 1, rf->rf1); - rt61pci_rf_write(rt2x00dev, 2, rf->rf2); - rt61pci_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); - rt61pci_rf_write(rt2x00dev, 4, rf->rf4); + return 0; +} - udelay(200); +static void rt61pci_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) +{ + u32 reg; - rt61pci_rf_write(rt2x00dev, 1, rf->rf1); - rt61pci_rf_write(rt2x00dev, 2, rf->rf2); - rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt61pci_rf_write(rt2x00dev, 4, rf->rf4); + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, + !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); + rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, + !(filter_flags & FIF_PROMISC_IN_BSS)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, + !(filter_flags & FIF_PROMISC_IN_BSS) && + !rt2x00dev->intf_ap_count); + rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, + !(filter_flags & FIF_CONTROL)); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); +} - msleep(1); +static void rt61pci_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) +{ + u32 reg; + + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable synchronisation. + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, conf->sync); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + } + + if (flags & CONFIG_UPDATE_MAC) { + reg = le32_to_cpu(conf->mac[1]); + rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); + conf->mac[1] = cpu_to_le32(reg); + + rt2x00mmio_register_multiwrite(rt2x00dev, MAC_CSR2, + conf->mac, sizeof(conf->mac)); + } + + if (flags & CONFIG_UPDATE_BSSID) { + reg = le32_to_cpu(conf->bssid[1]); + rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); + conf->bssid[1] = cpu_to_le32(reg); + + rt2x00mmio_register_multiwrite(rt2x00dev, MAC_CSR4, + conf->bssid, + sizeof(conf->bssid)); + } } -static void rt61pci_config_txpower(struct rt2x00_dev *rt2x00dev, - const int txpower) +static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) { - struct rf_channel rf; + u32 reg; - rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); - rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); - rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); - rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, 0x32); + rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, + !!erp->short_preamble); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR4, reg); + } - rt61pci_config_channel(rt2x00dev, &rf, txpower); + if (changed & BSS_CHANGED_BASIC_RATES) + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR5, + erp->basic_rates); + + if (changed & BSS_CHANGED_BEACON_INT) { + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2x00mmio_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR9, reg); + + rt2x00mmio_register_read(rt2x00dev, MAC_CSR8, ®); + rt2x00_set_field32(®, MAC_CSR8_SIFS, erp->sifs); + rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); + rt2x00_set_field32(®, MAC_CSR8_EIFS, erp->eifs); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR8, reg); + } } static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev, @@ -417,8 +634,7 @@ static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev, rt61pci_bbp_read(rt2x00dev, 4, &r4); rt61pci_bbp_read(rt2x00dev, 77, &r77); - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, - rt2x00_rf(&rt2x00dev->chip, RF5325)); + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF5325)); /* * Configure the RX antenna. @@ -427,27 +643,21 @@ static void rt61pci_config_antenna_5x(struct rt2x00_dev *rt2x00dev, case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - (rt2x00dev->curr_hwmode != HWMODE_A)); + (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ)); break; case ANTENNA_A: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); - if (rt2x00dev->curr_hwmode == HWMODE_A) + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); else rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); break; - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ case ANTENNA_B: + default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); - if (rt2x00dev->curr_hwmode == HWMODE_A) + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); else rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); @@ -470,10 +680,9 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt61pci_bbp_read(rt2x00dev, 4, &r4); rt61pci_bbp_read(rt2x00dev, 77, &r77); - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, - rt2x00_rf(&rt2x00dev->chip, RF2529)); + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529)); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - !test_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags)); + !rt2x00_has_cap_frame_type(rt2x00dev)); /* * Configure the RX antenna. @@ -486,14 +695,8 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); break; - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ case ANTENNA_B: + default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); break; @@ -509,15 +712,15 @@ static void rt61pci_config_antenna_2529_rx(struct rt2x00_dev *rt2x00dev, { u32 reg; - rt2x00pci_register_read(rt2x00dev, MAC_CSR13, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); - rt2x00_set_field32(®, MAC_CSR13_BIT4, p1); - rt2x00_set_field32(®, MAC_CSR13_BIT12, 0); + rt2x00_set_field32(®, MAC_CSR13_DIR4, 0); + rt2x00_set_field32(®, MAC_CSR13_VAL4, p1); - rt2x00_set_field32(®, MAC_CSR13_BIT3, !p2); - rt2x00_set_field32(®, MAC_CSR13_BIT11, 0); + rt2x00_set_field32(®, MAC_CSR13_DIR3, 0); + rt2x00_set_field32(®, MAC_CSR13_VAL3, !p2); - rt2x00pci_register_write(rt2x00dev, MAC_CSR13, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, reg); } static void rt61pci_config_antenna_2529(struct rt2x00_dev *rt2x00dev, @@ -531,10 +734,6 @@ static void rt61pci_config_antenna_2529(struct rt2x00_dev *rt2x00dev, rt61pci_bbp_read(rt2x00dev, 4, &r4); rt61pci_bbp_read(rt2x00dev, 77, &r77); - /* FIXME: Antenna selection for the rf 2529 is very confusing in the - * legacy driver. The code below should be ok for non-diversity setups. - */ - /* * Configure the RX antenna. */ @@ -544,15 +743,14 @@ static void rt61pci_config_antenna_2529(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); rt61pci_config_antenna_2529_rx(rt2x00dev, 0, 0); break; - case ANTENNA_SW_DIVERSITY: case ANTENNA_HW_DIVERSITY: /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. + * FIXME: Antenna selection for the rf 2529 is very confusing + * in the legacy driver. Just default to antenna B until the + * legacy code can be properly translated into rt2x00 code. */ case ANTENNA_B: + default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); rt61pci_config_antenna_2529_rx(rt2x00dev, 1, 1); @@ -595,164 +793,213 @@ static const struct antenna_sel antenna_sel_bg[] = { { 98, { 0x48, 0x48 } }, }; -static void rt61pci_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) +static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) { const struct antenna_sel *sel; unsigned int lna; unsigned int i; u32 reg; - if (rt2x00dev->curr_hwmode == HWMODE_A) { + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { sel = antenna_sel_a; - lna = test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags); + lna = rt2x00_has_cap_external_lna_a(rt2x00dev); } else { sel = antenna_sel_bg; - lna = test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags); + lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); } for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) rt61pci_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]); - rt2x00pci_register_read(rt2x00dev, PHY_CSR0, ®); + rt2x00mmio_register_read(rt2x00dev, PHY_CSR0, ®); rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, - (rt2x00dev->curr_hwmode == HWMODE_B || - rt2x00dev->curr_hwmode == HWMODE_G)); + rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, - (rt2x00dev->curr_hwmode == HWMODE_A)); + rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); - rt2x00pci_register_write(rt2x00dev, PHY_CSR0, reg); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR0, reg); - if (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF5325)) + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) rt61pci_config_antenna_5x(rt2x00dev, ant); - else if (rt2x00_rf(&rt2x00dev->chip, RF2527)) + else if (rt2x00_rf(rt2x00dev, RF2527)) rt61pci_config_antenna_2x(rt2x00dev, ant); - else if (rt2x00_rf(&rt2x00dev->chip, RF2529)) { - if (test_bit(CONFIG_DOUBLE_ANTENNA, &rt2x00dev->flags)) + else if (rt2x00_rf(rt2x00dev, RF2529)) { + if (rt2x00_has_cap_double_antenna(rt2x00dev)) rt61pci_config_antenna_2x(rt2x00dev, ant); else rt61pci_config_antenna_2529(rt2x00dev, ant); } } -static void rt61pci_config_duration(struct rt2x00_dev *rt2x00dev, +static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { - u32 reg; - - rt2x00pci_register_read(rt2x00dev, MAC_CSR9, ®); - rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, libconf->slot_time); - rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg); + u16 eeprom; + short lna_gain = 0; - rt2x00pci_register_read(rt2x00dev, MAC_CSR8, ®); - rt2x00_set_field32(®, MAC_CSR8_SIFS, libconf->sifs); - rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); - rt2x00_set_field32(®, MAC_CSR8_EIFS, libconf->eifs); - rt2x00pci_register_write(rt2x00dev, MAC_CSR8, reg); + if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) + lna_gain += 14; - rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); + } else { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) + lna_gain += 14; - rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg); + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); + } - rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, - libconf->conf->beacon_int * 16); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); + rt2x00dev->lna_gain = lna_gain; } -static void rt61pci_config(struct rt2x00_dev *rt2x00dev, - const unsigned int flags, - struct rt2x00lib_conf *libconf) +static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev, + struct rf_channel *rf, const int txpower) { - if (flags & CONFIG_UPDATE_PHYMODE) - rt61pci_config_phymode(rt2x00dev, libconf->basic_rates); - if (flags & CONFIG_UPDATE_CHANNEL) - rt61pci_config_channel(rt2x00dev, &libconf->rf, - libconf->conf->power_level); - if ((flags & CONFIG_UPDATE_TXPOWER) && !(flags & CONFIG_UPDATE_CHANNEL)) - rt61pci_config_txpower(rt2x00dev, libconf->conf->power_level); - if (flags & CONFIG_UPDATE_ANTENNA) - rt61pci_config_antenna(rt2x00dev, &libconf->ant); - if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT)) - rt61pci_config_duration(rt2x00dev, libconf); -} + u8 r3; + u8 r94; + u8 smart; -/* - * LED functions. - */ -static void rt61pci_enable_led(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - u8 arg0; - u8 arg1; + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); - rt2x00pci_register_read(rt2x00dev, MAC_CSR14, ®); - rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, 70); - rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, 30); - rt2x00pci_register_write(rt2x00dev, MAC_CSR14, reg); + rt61pci_bbp_read(rt2x00dev, 3, &r3); + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); + rt61pci_bbp_write(rt2x00dev, 3, r3); + + r94 = 6; + if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) + r94 += txpower - MAX_TXPOWER; + else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) + r94 += txpower; + rt61pci_bbp_write(rt2x00dev, 94, r94); + + rt61pci_rf_write(rt2x00dev, 1, rf->rf1); + rt61pci_rf_write(rt2x00dev, 2, rf->rf2); + rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt61pci_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(200); + + rt61pci_rf_write(rt2x00dev, 1, rf->rf1); + rt61pci_rf_write(rt2x00dev, 2, rf->rf2); + rt61pci_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); + rt61pci_rf_write(rt2x00dev, 4, rf->rf4); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_RADIO_STATUS, 1); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LINK_A_STATUS, - (rt2x00dev->rx_status.phymode == MODE_IEEE80211A)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LINK_BG_STATUS, - (rt2x00dev->rx_status.phymode != MODE_IEEE80211A)); + udelay(200); - arg0 = rt2x00dev->led_reg & 0xff; - arg1 = (rt2x00dev->led_reg >> 8) & 0xff; + rt61pci_rf_write(rt2x00dev, 1, rf->rf1); + rt61pci_rf_write(rt2x00dev, 2, rf->rf2); + rt61pci_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt61pci_rf_write(rt2x00dev, 4, rf->rf4); - rt61pci_mcu_request(rt2x00dev, MCU_LED, 0xff, arg0, arg1); + msleep(1); } -static void rt61pci_disable_led(struct rt2x00_dev *rt2x00dev) +static void rt61pci_config_txpower(struct rt2x00_dev *rt2x00dev, + const int txpower) { - u16 led_reg; - u8 arg0; - u8 arg1; + struct rf_channel rf; - led_reg = rt2x00dev->led_reg; - rt2x00_set_field16(&led_reg, MCU_LEDCS_RADIO_STATUS, 0); - rt2x00_set_field16(&led_reg, MCU_LEDCS_LINK_BG_STATUS, 0); - rt2x00_set_field16(&led_reg, MCU_LEDCS_LINK_A_STATUS, 0); + rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); + rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); + rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); + rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); + + rt61pci_config_channel(rt2x00dev, &rf, txpower); +} - arg0 = led_reg & 0xff; - arg1 = (led_reg >> 8) & 0xff; +static void rt61pci_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) +{ + u32 reg; - rt61pci_mcu_request(rt2x00dev, MCU_LED, 0xff, arg0, arg1); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_DOWN, 1); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_STEP, 0); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_FALLBACK_CCK, 0); + rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, + libconf->conf->short_frame_max_tx_count); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR4, reg); } -static void rt61pci_activity_led(struct rt2x00_dev *rt2x00dev, int rssi) +static void rt61pci_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) { - u8 led; + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; - if (rt2x00dev->led_mode != LED_MODE_SIGNAL_STRENGTH) - return; + if (state == STATE_SLEEP) { + rt2x00mmio_register_read(rt2x00dev, MAC_CSR11, ®); + rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, + rt2x00dev->beacon_int - 10); + rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 5); - /* - * Led handling requires a positive value for the rssi, - * to do that correctly we need to add the correction. - */ - rssi += rt2x00dev->rssi_offset; + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); - if (rssi <= 30) - led = 0; - else if (rssi <= 39) - led = 1; - else if (rssi <= 49) - led = 2; - else if (rssi <= 53) - led = 3; - else if (rssi <= 63) - led = 4; - else - led = 5; + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, + 0x00000005); + rt2x00mmio_register_write(rt2x00dev, IO_CNTL_CSR, 0x0000001c); + rt2x00mmio_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000060); + + rt61pci_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0, 0); + } else { + rt2x00mmio_register_read(rt2x00dev, MAC_CSR11, ®); + rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, 0); + rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, 0); + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); + rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, + 0x00000007); + rt2x00mmio_register_write(rt2x00dev, IO_CNTL_CSR, 0x00000018); + rt2x00mmio_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000020); + + rt61pci_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0, 0); + } +} + +static void rt61pci_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + /* Always recalculate LNA gain before changing configuration */ + rt61pci_config_lna_gain(rt2x00dev, libconf); - rt61pci_mcu_request(rt2x00dev, MCU_LED_STRENGTH, 0xff, led, 0); + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt61pci_config_channel(rt2x00dev, &libconf->rf, + libconf->conf->power_level); + if ((flags & IEEE80211_CONF_CHANGE_POWER) && + !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) + rt61pci_config_txpower(rt2x00dev, libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt61pci_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt61pci_config_ps(rt2x00dev, libconf); } /* @@ -766,90 +1013,93 @@ static void rt61pci_link_stats(struct rt2x00_dev *rt2x00dev, /* * Update FCS error count from register. */ - rt2x00pci_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00mmio_register_read(rt2x00dev, STA_CSR0, ®); qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); /* * Update False CCA count from register. */ - rt2x00pci_register_read(rt2x00dev, STA_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, STA_CSR1, ®); qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); } -static void rt61pci_reset_tuner(struct rt2x00_dev *rt2x00dev) +static inline void rt61pci_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) +{ + if (qual->vgc_level != vgc_level) { + rt61pci_bbp_write(rt2x00dev, 17, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } +} + +static void rt61pci_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) { - rt61pci_bbp_write(rt2x00dev, 17, 0x20); - rt2x00dev->link.vgc_level = 0x20; + rt61pci_set_vgc(rt2x00dev, qual, 0x20); } -static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev) +static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count) { - int rssi = rt2x00_get_link_rssi(&rt2x00dev->link); - u8 r17; u8 up_bound; u8 low_bound; /* - * Update Led strength - */ - rt61pci_activity_led(rt2x00dev, rssi); - - rt61pci_bbp_read(rt2x00dev, 17, &r17); - - /* * Determine r17 bounds. */ - if (rt2x00dev->rx_status.phymode == MODE_IEEE80211A) { + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { low_bound = 0x28; up_bound = 0x48; - if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } } else { low_bound = 0x20; up_bound = 0x40; - if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } } /* + * If we are not associated, we should go straight to the + * dynamic CCA tuning. + */ + if (!rt2x00dev->intf_associated) + goto dynamic_cca_tune; + + /* * Special big-R17 for very short distance */ - if (rssi >= -35) { - if (r17 != 0x60) - rt61pci_bbp_write(rt2x00dev, 17, 0x60); + if (qual->rssi >= -35) { + rt61pci_set_vgc(rt2x00dev, qual, 0x60); return; } /* * Special big-R17 for short distance */ - if (rssi >= -58) { - if (r17 != up_bound) - rt61pci_bbp_write(rt2x00dev, 17, up_bound); + if (qual->rssi >= -58) { + rt61pci_set_vgc(rt2x00dev, qual, up_bound); return; } /* * Special big-R17 for middle-short distance */ - if (rssi >= -66) { - low_bound += 0x10; - if (r17 != low_bound) - rt61pci_bbp_write(rt2x00dev, 17, low_bound); + if (qual->rssi >= -66) { + rt61pci_set_vgc(rt2x00dev, qual, low_bound + 0x10); return; } /* * Special mid-R17 for middle distance */ - if (rssi >= -74) { - low_bound += 0x08; - if (r17 != low_bound) - rt61pci_bbp_write(rt2x00dev, 17, low_bound); + if (qual->rssi >= -74) { + rt61pci_set_vgc(rt2x00dev, qual, low_bound + 0x08); return; } @@ -857,45 +1107,149 @@ static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev) * Special case: Change up_bound based on the rssi. * Lower up_bound when rssi is weaker then -74 dBm. */ - up_bound -= 2 * (-74 - rssi); + up_bound -= 2 * (-74 - qual->rssi); if (low_bound > up_bound) up_bound = low_bound; - if (r17 > up_bound) { - rt61pci_bbp_write(rt2x00dev, 17, up_bound); + if (qual->vgc_level > up_bound) { + rt61pci_set_vgc(rt2x00dev, qual, up_bound); return; } +dynamic_cca_tune: + /* * r17 does not yet exceed upper limit, continue and base * the r17 tuning on the false CCA count. */ - if (rt2x00dev->link.qual.false_cca > 512 && r17 < up_bound) { - if (++r17 > up_bound) - r17 = up_bound; - rt61pci_bbp_write(rt2x00dev, 17, r17); - } else if (rt2x00dev->link.qual.false_cca < 100 && r17 > low_bound) { - if (--r17 < low_bound) - r17 = low_bound; - rt61pci_bbp_write(rt2x00dev, 17, r17); + if ((qual->false_cca > 512) && (qual->vgc_level < up_bound)) + rt61pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level); + else if ((qual->false_cca < 100) && (qual->vgc_level > low_bound)) + rt61pci_set_vgc(rt2x00dev, qual, --qual->vgc_level); +} + +/* + * Queue handlers. + */ +static void rt61pci_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + break; + default: + break; + } +} + +static void rt61pci_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC0, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_VI: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC1, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_BE: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC2, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_BK: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC3, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + default: + break; + } +} + +static void rt61pci_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_AC_VO: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC0, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_VI: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC1, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_BE: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC2, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_AC_BK: + rt2x00mmio_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC3, 1); + rt2x00mmio_register_write(rt2x00dev, TX_CNTL_CSR, reg); + break; + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Wait for possibly running tbtt tasklets. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + break; + default: + break; } } /* - * Firmware name function. + * Firmware functions */ static char *rt61pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) { + u16 chip; char *fw_name; - switch (rt2x00dev->chip.rt) { - case RT2561: + pci_read_config_word(to_pci_dev(rt2x00dev->dev), PCI_DEVICE_ID, &chip); + switch (chip) { + case RT2561_PCI_ID: fw_name = FIRMWARE_RT2561; break; - case RT2561s: + case RT2561s_PCI_ID: fw_name = FIRMWARE_RT2561s; break; - case RT2661: + case RT2661_PCI_ID: fw_name = FIRMWARE_RT2661; break; default: @@ -906,11 +1260,37 @@ static char *rt61pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) return fw_name; } -/* - * Initialization functions. - */ -static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, void *data, - const size_t len) +static int rt61pci_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + u16 fw_crc; + u16 crc; + + /* + * Only support 8kb firmware files. + */ + if (len != 8192) + return FW_BAD_LENGTH; + + /* + * The last 2 bytes in the firmware array are the crc checksum itself. + * This means that we should never pass those 2 bytes to the crc + * algorithm. + */ + fw_crc = (data[len - 2] << 8 | data[len - 1]); + + /* + * Use the crc itu-t algorithm. + */ + crc = crc_itu_t(0, data, len - 2); + crc = crc_itu_t_byte(crc, 0); + crc = crc_itu_t_byte(crc, 0); + + return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; +} + +static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) { int i; u32 reg; @@ -919,14 +1299,14 @@ static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, void *data, * Wait for stable hardware. */ for (i = 0; i < 100; i++) { - rt2x00pci_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR0, ®); if (reg) break; msleep(1); } if (!reg) { - ERROR(rt2x00dev, "Unstable hardware.\n"); + rt2x00_err(rt2x00dev, "Unstable hardware\n"); return -EBUSY; } @@ -935,10 +1315,10 @@ static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, void *data, */ reg = 0; rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); - rt2x00pci_register_write(rt2x00dev, MCU_CNTL_CSR, reg); - rt2x00pci_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); - rt2x00pci_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - rt2x00pci_register_write(rt2x00dev, HOST_CMD_CSR, 0); + rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); + rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + rt2x00mmio_register_write(rt2x00dev, HOST_CMD_CSR, 0); /* * Write firmware to device. @@ -946,174 +1326,175 @@ static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, void *data, reg = 0; rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 1); - rt2x00pci_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); - rt2x00pci_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, - data, len); + rt2x00mmio_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data, len); rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 0); - rt2x00pci_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 0); - rt2x00pci_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, MCU_CNTL_CSR, reg); for (i = 0; i < 100; i++) { - rt2x00pci_register_read(rt2x00dev, MCU_CNTL_CSR, ®); + rt2x00mmio_register_read(rt2x00dev, MCU_CNTL_CSR, ®); if (rt2x00_get_field32(reg, MCU_CNTL_CSR_READY)) break; msleep(1); } if (i == 100) { - ERROR(rt2x00dev, "MCU Control register not ready.\n"); + rt2x00_err(rt2x00dev, "MCU Control register not ready\n"); return -EBUSY; } /* + * Hardware needs another millisecond before it is ready. + */ + msleep(1); + + /* * Reset MAC and BBP registers. */ reg = 0; rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); - rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); - rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); - rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); return 0; } -static void rt61pci_init_rxentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry) +/* + * Initialization functions. + */ +static bool rt61pci_get_entry_state(struct queue_entry *entry) { - __le32 *rxd = entry->priv; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; u32 word; - rt2x00_desc_read(rxd, 5, &word); - rt2x00_set_field32(&word, RXD_W5_BUFFER_PHYSICAL_ADDRESS, - entry->data_dma); - rt2x00_desc_write(rxd, 5, word); + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_desc_read(rxd, 0, &word); - rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); - rt2x00_desc_write(rxd, 0, word); + return rt2x00_get_field32(word, RXD_W0_OWNER_NIC); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + + return (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)); + } } -static void rt61pci_init_txentry(struct rt2x00_dev *rt2x00dev, - struct data_entry *entry) +static void rt61pci_clear_entry(struct queue_entry *entry) { - __le32 *txd = entry->priv; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); u32 word; - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_BUFFER_COUNT, 1); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 5, &word); - rt2x00_set_field32(&word, TXD_W5_PID_TYPE, entry->ring->queue_idx); - rt2x00_set_field32(&word, TXD_W5_PID_SUBTYPE, entry->entry_idx); - rt2x00_desc_write(txd, 5, word); - - rt2x00_desc_read(txd, 6, &word); - rt2x00_set_field32(&word, TXD_W6_BUFFER_PHYSICAL_ADDRESS, - entry->data_dma); - rt2x00_desc_write(txd, 6, word); + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 5, &word); + rt2x00_set_field32(&word, RXD_W5_BUFFER_PHYSICAL_ADDRESS, + skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 5, word); - rt2x00_desc_read(txd, 0, &word); - rt2x00_set_field32(&word, TXD_W0_VALID, 0); - rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); - rt2x00_desc_write(txd, 0, word); + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(entry_priv->desc, 0, word); + } else { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(entry_priv->desc, 0, word); + } } -static int rt61pci_init_rings(struct rt2x00_dev *rt2x00dev) +static int rt61pci_init_queues(struct rt2x00_dev *rt2x00dev) { + struct queue_entry_priv_mmio *entry_priv; u32 reg; /* * Initialize registers. */ - rt2x00pci_register_read(rt2x00dev, TX_RING_CSR0, ®); + rt2x00mmio_register_read(rt2x00dev, TX_RING_CSR0, ®); rt2x00_set_field32(®, TX_RING_CSR0_AC0_RING_SIZE, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].stats.limit); + rt2x00dev->tx[0].limit); rt2x00_set_field32(®, TX_RING_CSR0_AC1_RING_SIZE, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA1].stats.limit); + rt2x00dev->tx[1].limit); rt2x00_set_field32(®, TX_RING_CSR0_AC2_RING_SIZE, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA2].stats.limit); + rt2x00dev->tx[2].limit); rt2x00_set_field32(®, TX_RING_CSR0_AC3_RING_SIZE, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA3].stats.limit); - rt2x00pci_register_write(rt2x00dev, TX_RING_CSR0, reg); + rt2x00dev->tx[3].limit); + rt2x00mmio_register_write(rt2x00dev, TX_RING_CSR0, reg); - rt2x00pci_register_read(rt2x00dev, TX_RING_CSR1, ®); - rt2x00_set_field32(®, TX_RING_CSR1_MGMT_RING_SIZE, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA4].stats.limit); + rt2x00mmio_register_read(rt2x00dev, TX_RING_CSR1, ®); rt2x00_set_field32(®, TX_RING_CSR1_TXD_SIZE, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].desc_size / - 4); - rt2x00pci_register_write(rt2x00dev, TX_RING_CSR1, reg); + rt2x00dev->tx[0].desc_size / 4); + rt2x00mmio_register_write(rt2x00dev, TX_RING_CSR1, reg); - rt2x00pci_register_read(rt2x00dev, AC0_BASE_CSR, ®); + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, AC0_BASE_CSR, ®); rt2x00_set_field32(®, AC0_BASE_CSR_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA0].data_dma); - rt2x00pci_register_write(rt2x00dev, AC0_BASE_CSR, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, AC0_BASE_CSR, reg); - rt2x00pci_register_read(rt2x00dev, AC1_BASE_CSR, ®); + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, AC1_BASE_CSR, ®); rt2x00_set_field32(®, AC1_BASE_CSR_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA1].data_dma); - rt2x00pci_register_write(rt2x00dev, AC1_BASE_CSR, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, AC1_BASE_CSR, reg); - rt2x00pci_register_read(rt2x00dev, AC2_BASE_CSR, ®); + entry_priv = rt2x00dev->tx[2].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, AC2_BASE_CSR, ®); rt2x00_set_field32(®, AC2_BASE_CSR_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA2].data_dma); - rt2x00pci_register_write(rt2x00dev, AC2_BASE_CSR, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, AC2_BASE_CSR, reg); - rt2x00pci_register_read(rt2x00dev, AC3_BASE_CSR, ®); + entry_priv = rt2x00dev->tx[3].entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, AC3_BASE_CSR, ®); rt2x00_set_field32(®, AC3_BASE_CSR_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA3].data_dma); - rt2x00pci_register_write(rt2x00dev, AC3_BASE_CSR, reg); - - rt2x00pci_register_read(rt2x00dev, MGMT_BASE_CSR, ®); - rt2x00_set_field32(®, MGMT_BASE_CSR_RING_REGISTER, - rt2x00dev->tx[IEEE80211_TX_QUEUE_DATA4].data_dma); - rt2x00pci_register_write(rt2x00dev, MGMT_BASE_CSR, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, AC3_BASE_CSR, reg); - rt2x00pci_register_read(rt2x00dev, RX_RING_CSR, ®); - rt2x00_set_field32(®, RX_RING_CSR_RING_SIZE, - rt2x00dev->rx->stats.limit); + rt2x00mmio_register_read(rt2x00dev, RX_RING_CSR, ®); + rt2x00_set_field32(®, RX_RING_CSR_RING_SIZE, rt2x00dev->rx->limit); rt2x00_set_field32(®, RX_RING_CSR_RXD_SIZE, rt2x00dev->rx->desc_size / 4); rt2x00_set_field32(®, RX_RING_CSR_RXD_WRITEBACK_SIZE, 4); - rt2x00pci_register_write(rt2x00dev, RX_RING_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, RX_RING_CSR, reg); - rt2x00pci_register_read(rt2x00dev, RX_BASE_CSR, ®); + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_read(rt2x00dev, RX_BASE_CSR, ®); rt2x00_set_field32(®, RX_BASE_CSR_RING_REGISTER, - rt2x00dev->rx->data_dma); - rt2x00pci_register_write(rt2x00dev, RX_BASE_CSR, reg); + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RX_BASE_CSR, reg); - rt2x00pci_register_read(rt2x00dev, TX_DMA_DST_CSR, ®); + rt2x00mmio_register_read(rt2x00dev, TX_DMA_DST_CSR, ®); rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC0, 2); rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC1, 2); rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC2, 2); rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_AC3, 2); - rt2x00_set_field32(®, TX_DMA_DST_CSR_DEST_MGMT, 0); - rt2x00pci_register_write(rt2x00dev, TX_DMA_DST_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, TX_DMA_DST_CSR, reg); - rt2x00pci_register_read(rt2x00dev, LOAD_TX_RING_CSR, ®); + rt2x00mmio_register_read(rt2x00dev, LOAD_TX_RING_CSR, ®); rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC0, 1); rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC1, 1); rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC2, 1); rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_AC3, 1); - rt2x00_set_field32(®, LOAD_TX_RING_CSR_LOAD_TXD_MGMT, 1); - rt2x00pci_register_write(rt2x00dev, LOAD_TX_RING_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, LOAD_TX_RING_CSR, reg); - rt2x00pci_register_read(rt2x00dev, RX_CNTL_CSR, ®); + rt2x00mmio_register_read(rt2x00dev, RX_CNTL_CSR, ®); rt2x00_set_field32(®, RX_CNTL_CSR_LOAD_RXD, 1); - rt2x00pci_register_write(rt2x00dev, RX_CNTL_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, RX_CNTL_CSR, reg); return 0; } @@ -1122,13 +1503,13 @@ static int rt61pci_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); rt2x00_set_field32(®, TXRX_CSR0_TX_WITHOUT_WAITING, 0); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR0, reg); - rt2x00pci_register_read(rt2x00dev, TXRX_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR1, ®); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1, 30); /* Rssi */ @@ -1137,12 +1518,12 @@ static int rt61pci_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3, 30); /* Rssi */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3_VALID, 1); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR1, reg); /* * CCK TXD BBP registers */ - rt2x00pci_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR2, ®); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0, 13); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1, 12); @@ -1151,126 +1532,143 @@ static int rt61pci_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3, 10); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3_VALID, 1); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR2, reg); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR2, reg); /* * OFDM TXD BBP registers */ - rt2x00pci_register_read(rt2x00dev, TXRX_CSR3, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR3, ®); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0, 7); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1, 6); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2, 5); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2_VALID, 1); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR3, reg); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR3, reg); - rt2x00pci_register_read(rt2x00dev, TXRX_CSR7, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR7, ®); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_6MBS, 59); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_9MBS, 53); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_12MBS, 49); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_18MBS, 46); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR7, reg); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR7, reg); - rt2x00pci_register_read(rt2x00dev, TXRX_CSR8, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR8, ®); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_24MBS, 44); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_36MBS, 42); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_48MBS, 42); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_54MBS, 42); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR8, reg); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR8, reg); + + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 0); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00_set_field32(®, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); - rt2x00pci_register_write(rt2x00dev, MAC_CSR6, 0x00000fff); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR6, 0x00000fff); - rt2x00pci_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR9, ®); rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); - rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR9, reg); - rt2x00pci_register_write(rt2x00dev, MAC_CSR10, 0x0000071c); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR10, 0x0000071c); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; - rt2x00pci_register_write(rt2x00dev, MAC_CSR13, 0x0000e000); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, 0x0000e000); /* * Invalidate all Shared Keys (SEC_CSR0), * and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5) */ - rt2x00pci_register_write(rt2x00dev, SEC_CSR0, 0x00000000); - rt2x00pci_register_write(rt2x00dev, SEC_CSR1, 0x00000000); - rt2x00pci_register_write(rt2x00dev, SEC_CSR5, 0x00000000); - - rt2x00pci_register_write(rt2x00dev, PHY_CSR1, 0x000023b0); - rt2x00pci_register_write(rt2x00dev, PHY_CSR5, 0x060a100c); - rt2x00pci_register_write(rt2x00dev, PHY_CSR6, 0x00080606); - rt2x00pci_register_write(rt2x00dev, PHY_CSR7, 0x00000a08); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR0, 0x00000000); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR1, 0x00000000); + rt2x00mmio_register_write(rt2x00dev, SEC_CSR5, 0x00000000); - rt2x00pci_register_write(rt2x00dev, PCI_CFG_CSR, 0x28ca4404); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR1, 0x000023b0); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR5, 0x060a100c); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR6, 0x00080606); + rt2x00mmio_register_write(rt2x00dev, PHY_CSR7, 0x00000a08); - rt2x00pci_register_write(rt2x00dev, TEST_MODE_CSR, 0x00000200); + rt2x00mmio_register_write(rt2x00dev, PCI_CFG_CSR, 0x28ca4404); - rt2x00pci_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); + rt2x00mmio_register_write(rt2x00dev, TEST_MODE_CSR, 0x00000200); - rt2x00pci_register_read(rt2x00dev, AC_TXOP_CSR0, ®); - rt2x00_set_field32(®, AC_TXOP_CSR0_AC0_TX_OP, 0); - rt2x00_set_field32(®, AC_TXOP_CSR0_AC1_TX_OP, 0); - rt2x00pci_register_write(rt2x00dev, AC_TXOP_CSR0, reg); + rt2x00mmio_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); - rt2x00pci_register_read(rt2x00dev, AC_TXOP_CSR1, ®); - rt2x00_set_field32(®, AC_TXOP_CSR1_AC2_TX_OP, 192); - rt2x00_set_field32(®, AC_TXOP_CSR1_AC3_TX_OP, 48); - rt2x00pci_register_write(rt2x00dev, AC_TXOP_CSR1, reg); + /* + * Clear all beacons + * For the Beacon base registers we only need to clear + * the first byte since that byte contains the VALID and OWNER + * bits which (when set to 0) will invalidate the entire beacon. + */ + rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE0, 0); + rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE1, 0); + rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE2, 0); + rt2x00mmio_register_write(rt2x00dev, HW_BEACON_BASE3, 0); /* * We must clear the error counters. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ - rt2x00pci_register_read(rt2x00dev, STA_CSR0, ®); - rt2x00pci_register_read(rt2x00dev, STA_CSR1, ®); - rt2x00pci_register_read(rt2x00dev, STA_CSR2, ®); + rt2x00mmio_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00mmio_register_read(rt2x00dev, STA_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, STA_CSR2, ®); /* * Reset MAC and BBP registers. */ - rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); - rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); - rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); - rt2x00pci_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); - rt2x00pci_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR1, reg); return 0; } -static int rt61pci_init_bbp(struct rt2x00_dev *rt2x00dev) +static int rt61pci_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; - u16 eeprom; - u8 reg_id; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt61pci_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) - goto continue_csr_init; - NOTICE(rt2x00dev, "Waiting for BBP register.\n"); + return 0; udelay(REGISTER_BUSY_DELAY); } - ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); return -EACCES; +} + +static int rt61pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt61pci_wait_bbp_ready(rt2x00dev))) + return -EACCES; -continue_csr_init: rt61pci_bbp_write(rt2x00dev, 3, 0x00); rt61pci_bbp_write(rt2x00dev, 15, 0x30); rt61pci_bbp_write(rt2x00dev, 21, 0xc8); @@ -1296,19 +1694,15 @@ continue_csr_init: rt61pci_bbp_write(rt2x00dev, 102, 0x16); rt61pci_bbp_write(rt2x00dev, 107, 0x04); - DEBUG(rt2x00dev, "Start initialization from EEPROM...\n"); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - DEBUG(rt2x00dev, "BBP: 0x%02x, value: 0x%02x.\n", - reg_id, value); rt61pci_bbp_write(rt2x00dev, reg_id, value); } } - DEBUG(rt2x00dev, "...End initialization from EEPROM.\n"); return 0; } @@ -1316,47 +1710,40 @@ continue_csr_init: /* * Device state switch handlers. */ -static void rt61pci_toggle_rx(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg; - - rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, - state == STATE_RADIO_RX_OFF); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); -} - static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, enum dev_state state) { int mask = (state == STATE_RADIO_IRQ_OFF); u32 reg; + unsigned long flags; /* * When interrupts are being enabled, the interrupt registers * should clear the register to assure a clean state. */ if (state == STATE_RADIO_IRQ_ON) { - rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); - rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); + rt2x00mmio_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); } /* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ - rt2x00pci_register_read(rt2x00dev, INT_MASK_CSR, ®); + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); rt2x00_set_field32(®, INT_MASK_CSR_TXDONE, mask); rt2x00_set_field32(®, INT_MASK_CSR_RXDONE, mask); + rt2x00_set_field32(®, INT_MASK_CSR_BEACON_DONE, mask); rt2x00_set_field32(®, INT_MASK_CSR_ENABLE_MITIGATION, mask); rt2x00_set_field32(®, INT_MASK_CSR_MITIGATION_PERIOD, 0xff); - rt2x00pci_register_write(rt2x00dev, INT_MASK_CSR, reg); + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - rt2x00pci_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); + rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); rt2x00_set_field32(®, MCU_INT_MASK_CSR_0, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_1, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_2, mask); @@ -1365,7 +1752,20 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(®, MCU_INT_MASK_CSR_5, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_6, mask); rt2x00_set_field32(®, MCU_INT_MASK_CSR_7, mask); - rt2x00pci_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); + rt2x00_set_field32(®, MCU_INT_MASK_CSR_TWAKEUP, mask); + rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); + + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Ensure that all tasklets are finished. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->autowake_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + } } static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev) @@ -1375,79 +1775,41 @@ static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev) /* * Initialize all registers. */ - if (rt61pci_init_rings(rt2x00dev) || - rt61pci_init_registers(rt2x00dev) || - rt61pci_init_bbp(rt2x00dev)) { - ERROR(rt2x00dev, "Register initialization failed.\n"); + if (unlikely(rt61pci_init_queues(rt2x00dev) || + rt61pci_init_registers(rt2x00dev) || + rt61pci_init_bbp(rt2x00dev))) return -EIO; - } - - /* - * Enable interrupts. - */ - rt61pci_toggle_irq(rt2x00dev, STATE_RADIO_IRQ_ON); /* * Enable RX. */ - rt2x00pci_register_read(rt2x00dev, RX_CNTL_CSR, ®); + rt2x00mmio_register_read(rt2x00dev, RX_CNTL_CSR, ®); rt2x00_set_field32(®, RX_CNTL_CSR_ENABLE_RX_DMA, 1); - rt2x00pci_register_write(rt2x00dev, RX_CNTL_CSR, reg); - - /* - * Enable LED - */ - rt61pci_enable_led(rt2x00dev); + rt2x00mmio_register_write(rt2x00dev, RX_CNTL_CSR, reg); return 0; } static void rt61pci_disable_radio(struct rt2x00_dev *rt2x00dev) { - u32 reg; - - /* - * Disable LED - */ - rt61pci_disable_led(rt2x00dev); - - rt2x00pci_register_write(rt2x00dev, MAC_CSR10, 0x00001818); - /* - * Disable synchronisation. + * Disable power */ - rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, 0); - - /* - * Cancel RX and TX. - */ - rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC0, 1); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC1, 1); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC2, 1); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC3, 1); - rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_MGMT, 1); - rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); - - /* - * Disable interrupts. - */ - rt61pci_toggle_irq(rt2x00dev, STATE_RADIO_IRQ_OFF); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR10, 0x00001818); } static int rt61pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - u32 reg; + u32 reg, reg2; unsigned int i; char put_to_sleep; - char current_state; put_to_sleep = (state != STATE_AWAKE); - rt2x00pci_register_read(rt2x00dev, MAC_CSR12, ®); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR12, ®); rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); - rt2x00pci_register_write(rt2x00dev, MAC_CSR12, reg); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR12, reg); /* * Device is not guaranteed to be in the requested state yet. @@ -1455,17 +1817,14 @@ static int rt61pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2x00pci_register_read(rt2x00dev, MAC_CSR12, ®); - current_state = - rt2x00_get_field32(reg, MAC_CSR12_BBP_CURRENT_STATE); - if (current_state == !put_to_sleep) + rt2x00mmio_register_read(rt2x00dev, MAC_CSR12, ®2); + state = rt2x00_get_field32(reg2, MAC_CSR12_BBP_CURRENT_STATE); + if (state == !put_to_sleep) return 0; + rt2x00mmio_register_write(rt2x00dev, MAC_CSR12, reg); msleep(10); } - NOTICE(rt2x00dev, "Device failed to enter state %d, " - "current device state %d.\n", !put_to_sleep, current_state); - return -EBUSY; } @@ -1481,13 +1840,9 @@ static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev, case STATE_RADIO_OFF: rt61pci_disable_radio(rt2x00dev); break; - case STATE_RADIO_RX_ON: - case STATE_RADIO_RX_ON_LINK: - rt61pci_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); - break; - case STATE_RADIO_RX_OFF: - case STATE_RADIO_RX_OFF_LINK: - rt61pci_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt61pci_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: @@ -1500,108 +1855,203 @@ static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev, break; } + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + return retval; } /* * TX descriptor initialization */ -static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txdata_entry_desc *desc, - struct ieee80211_tx_control *control) +static void rt61pci_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) { - struct skb_desc *skbdesc = get_skb_desc(skb); - __le32 *txd = skbdesc->desc; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; u32 word; /* * Start writing the descriptor words. */ rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, desc->queue); - rt2x00_set_field32(&word, TXD_W1_AIFSN, desc->aifs); - rt2x00_set_field32(&word, TXD_W1_CWMIN, desc->cw_min); - rt2x00_set_field32(&word, TXD_W1_CWMAX, desc->cw_max); - rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, 1); + rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, entry->queue->qid); + rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->queue->aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); + rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_BUFFER_COUNT, 1); rt2x00_desc_write(txd, 1, word); rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, desc->signal); - rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, desc->service); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, desc->length_low); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, desc->length_high); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); rt2x00_desc_write(txd, 2, word); + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { + _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); + _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); + } + rt2x00_desc_read(txd, 5, &word); + rt2x00_set_field32(&word, TXD_W5_PID_TYPE, entry->queue->qid); + rt2x00_set_field32(&word, TXD_W5_PID_SUBTYPE, + skbdesc->entry->entry_idx); rt2x00_set_field32(&word, TXD_W5_TX_POWER, - TXPOWER_TO_DEV(control->power_level)); + TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power)); rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); rt2x00_desc_write(txd, 5, word); - rt2x00_desc_read(txd, 11, &word); - rt2x00_set_field32(&word, TXD_W11_BUFFER_LENGTH0, skbdesc->data_len); - rt2x00_desc_write(txd, 11, word); + if (entry->queue->qid != QID_BEACON) { + rt2x00_desc_read(txd, 6, &word); + rt2x00_set_field32(&word, TXD_W6_BUFFER_PHYSICAL_ADDRESS, + skbdesc->skb_dma); + rt2x00_desc_write(txd, 6, word); + + rt2x00_desc_read(txd, 11, &word); + rt2x00_set_field32(&word, TXD_W11_BUFFER_LENGTH0, + txdesc->length); + rt2x00_desc_write(txd, 11, word); + } + /* + * Writing TXD word 0 must the last to prevent a race condition with + * the device, whereby the device may take hold of the TXD before we + * finished updating it. + */ rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); rt2x00_set_field32(&word, TXD_W0_VALID, 1); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &desc->flags)); + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &desc->flags)); + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &desc->flags)); + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_OFDM, - test_bit(ENTRY_TXD_OFDM_RATE, &desc->flags)); - rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + (txdesc->rate_mode == RATE_MODE_OFDM)); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, - !!(control->flags & - IEEE80211_TXCTL_LONG_RETRY_LIMIT)); - rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, 0); - rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skbdesc->data_len); + test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, + test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, + test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); rt2x00_set_field32(&word, TXD_W0_BURST, - test_bit(ENTRY_TXD_BURST, &desc->flags)); - rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); rt2x00_desc_write(txd, 0, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = (entry->queue->qid == QID_BEACON) ? TXINFO_SIZE : + TXD_DESC_SIZE; } /* * TX data initialization */ -static void rt61pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, - unsigned int queue) +static void rt61pci_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) { - u32 reg; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + unsigned int beacon_base; + unsigned int padding_len; + u32 orig_reg, reg; - if (queue == IEEE80211_TX_QUEUE_BEACON) { - /* - * For Wi-Fi faily generated beacons between participating - * stations. Set TBTT phase adaptive adjustment step to 8us. - */ - rt2x00pci_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + orig_reg = reg; + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); - rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); - if (!rt2x00_get_field32(reg, TXRX_CSR9_BEACON_GEN)) { - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); - } + /* + * Write the TX descriptor for the beacon. + */ + rt61pci_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); + + /* + * Write entire beacon with descriptor and padding to register. + */ + padding_len = roundup(entry->skb->len, 4) - entry->skb->len; + if (padding_len && skb_pad(entry->skb, padding_len)) { + rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); + /* skb freed by skb_pad() on failure */ + entry->skb = NULL; + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg); return; } - rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, ®); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC0, - (queue == IEEE80211_TX_QUEUE_DATA0)); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC1, - (queue == IEEE80211_TX_QUEUE_DATA1)); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC2, - (queue == IEEE80211_TX_QUEUE_DATA2)); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC3, - (queue == IEEE80211_TX_QUEUE_DATA3)); - rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_MGMT, - (queue == IEEE80211_TX_QUEUE_DATA4)); - rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg); + beacon_base = HW_BEACON_OFFSET(entry->entry_idx); + rt2x00mmio_register_multiwrite(rt2x00dev, beacon_base, + entry_priv->desc, TXINFO_SIZE); + rt2x00mmio_register_multiwrite(rt2x00dev, beacon_base + TXINFO_SIZE, + entry->skb->data, + entry->skb->len + padding_len); + + /* + * Enable beaconing again. + * + * For Wi-Fi faily generated beacons between participating + * stations. Set TBTT phase adaptive adjustment step to 8us. + */ + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); + + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Clean up beacon skb. + */ + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; +} + +static void rt61pci_clear_beacon(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, &orig_reg); + reg = orig_reg; + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Clear beacon. + */ + rt2x00mmio_register_write(rt2x00dev, + HW_BEACON_OFFSET(entry->entry_idx), 0); + + /* + * Restore global beaconing state. + */ + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg); } /* @@ -1609,67 +2059,92 @@ static void rt61pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, */ static int rt61pci_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) { - u16 eeprom; - u8 offset; + u8 offset = rt2x00dev->lna_gain; u8 lna; lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); switch (lna) { case 3: - offset = 90; + offset += 90; break; case 2: - offset = 74; + offset += 74; break; case 1: - offset = 64; + offset += 64; break; default: return 0; } - if (rt2x00dev->rx_status.phymode == MODE_IEEE80211A) { - if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) - offset += 14; - + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { if (lna == 3 || lna == 2) offset += 10; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); - offset -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); - } else { - if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) - offset += 14; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); - offset -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); } return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; } -static void rt61pci_fill_rxdone(struct data_entry *entry, - struct rxdata_entry_desc *desc) +static void rt61pci_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) { - __le32 *rxd = entry->priv; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; u32 word0; u32 word1; - rt2x00_desc_read(rxd, 0, &word0); - rt2x00_desc_read(rxd, 1, &word1); + rt2x00_desc_read(entry_priv->desc, 0, &word0); + rt2x00_desc_read(entry_priv->desc, 1, &word1); - desc->flags = 0; if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - desc->flags |= RX_FLAG_FAILED_FCS_CRC; + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); + rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); + + if (rxdesc->cipher != CIPHER_NONE) { + _rt2x00_desc_read(entry_priv->desc, 2, &rxdesc->iv[0]); + _rt2x00_desc_read(entry_priv->desc, 3, &rxdesc->iv[1]); + rxdesc->dev_flags |= RXDONE_CRYPTO_IV; + + _rt2x00_desc_read(entry_priv->desc, 4, &rxdesc->icv); + rxdesc->dev_flags |= RXDONE_CRYPTO_ICV; + + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. It has provided the data separately but rt2x00lib + * should decide if it should be reinserted. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } /* * Obtain the status about this packet. + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 100kbit/s. */ - desc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); - desc->rssi = rt61pci_agc_to_rssi(entry->ring->rt2x00dev, word1); - desc->ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); - desc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - desc->my_bss = !!rt2x00_get_field32(word0, RXD_W0_MY_BSS); + rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rxdesc->rssi = rt61pci_agc_to_rssi(rt2x00dev, word1); + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + if (rt2x00_get_field32(word0, RXD_W0_OFDM)) + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + else + rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; } /* @@ -1677,44 +2152,38 @@ static void rt61pci_fill_rxdone(struct data_entry *entry, */ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) { - struct data_ring *ring; - struct data_entry *entry; - struct data_entry *entry_done; - __le32 *txd; + struct data_queue *queue; + struct queue_entry *entry; + struct queue_entry *entry_done; + struct queue_entry_priv_mmio *entry_priv; + struct txdone_entry_desc txdesc; u32 word; u32 reg; - u32 old_reg; int type; int index; - int tx_status; - int retry; + int i; /* - * During each loop we will compare the freshly read - * STA_CSR4 register value with the value read from - * the previous loop. If the 2 values are equal then - * we should stop processing because the chance it - * quite big that the device has been unplugged and - * we risk going into an endless loop. + * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO + * at most X times and also stop processing once the TX_STA_FIFO_VALID + * flag is not set anymore. + * + * The legacy drivers use X=TX_RING_SIZE but state in a comment + * that the TX_STA_FIFO stack has a size of 16. We stick to our + * tx ring size for now. */ - old_reg = 0; - - while (1) { - rt2x00pci_register_read(rt2x00dev, STA_CSR4, ®); + for (i = 0; i < rt2x00dev->tx->limit; i++) { + rt2x00mmio_register_read(rt2x00dev, STA_CSR4, ®); if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) break; - if (old_reg == reg) - break; - old_reg = reg; - /* * Skip this entry when it contains an invalid - * ring identication number. + * queue identication number. */ type = rt2x00_get_field32(reg, STA_CSR4_PID_TYPE); - ring = rt2x00lib_get_ring(rt2x00dev, type); - if (unlikely(!ring)) + queue = rt2x00queue_get_tx_queue(rt2x00dev, type); + if (unlikely(!queue)) continue; /* @@ -1722,85 +2191,194 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) * index number. */ index = rt2x00_get_field32(reg, STA_CSR4_PID_SUBTYPE); - if (unlikely(index >= ring->stats.limit)) + if (unlikely(index >= queue->limit)) continue; - entry = &ring->entry[index]; - txd = entry->priv; - rt2x00_desc_read(txd, 0, &word); + entry = &queue->entries[index]; + entry_priv = entry->priv_data; + rt2x00_desc_read(entry_priv->desc, 0, &word); if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || !rt2x00_get_field32(word, TXD_W0_VALID)) return; - entry_done = rt2x00_get_data_entry_done(ring); + entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); while (entry != entry_done) { - /* Catch up. Just report any entries we missed as - * failed. */ - WARNING(rt2x00dev, - "TX status report missed for entry %p\n", - entry_done); - rt2x00pci_txdone(rt2x00dev, entry_done, TX_FAIL_OTHER, - 0); - entry_done = rt2x00_get_data_entry_done(ring); + /* Catch up. + * Just report any entries we missed as failed. + */ + rt2x00_warn(rt2x00dev, "TX status report missed for entry %d\n", + entry_done->entry_idx); + + rt2x00lib_txdone_noinfo(entry_done, TXDONE_UNKNOWN); + entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE); } /* * Obtain the status about this packet. */ - tx_status = rt2x00_get_field32(reg, STA_CSR4_TX_RESULT); - retry = rt2x00_get_field32(reg, STA_CSR4_RETRY_COUNT); + txdesc.flags = 0; + switch (rt2x00_get_field32(reg, STA_CSR4_TX_RESULT)) { + case 0: /* Success, maybe with retry */ + __set_bit(TXDONE_SUCCESS, &txdesc.flags); + break; + case 6: /* Failure, excessive retries */ + __set_bit(TXDONE_EXCESSIVE_RETRY, &txdesc.flags); + /* Don't break, this is a failed frame! */ + default: /* Failure */ + __set_bit(TXDONE_FAILURE, &txdesc.flags); + } + txdesc.retry = rt2x00_get_field32(reg, STA_CSR4_RETRY_COUNT); - rt2x00pci_txdone(rt2x00dev, entry, tx_status, retry); + /* + * the frame was retried at least once + * -> hw used fallback rates + */ + if (txdesc.retry) + __set_bit(TXDONE_FALLBACK, &txdesc.flags); + + rt2x00lib_txdone(entry, &txdesc); } } +static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev) +{ + struct rt2x00lib_conf libconf = { .conf = &rt2x00dev->hw->conf }; + + rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); +} + +static inline void rt61pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + rt2x00_set_field32(®, irq_field, 0); + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +static void rt61pci_enable_mcu_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single MCU interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); + rt2x00_set_field32(®, irq_field, 0); + rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); + + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +static void rt61pci_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt61pci_txdone(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TXDONE); +} + +static void rt61pci_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_beacondone(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_BEACON_DONE); +} + +static void rt61pci_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt61pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RXDONE); +} + +static void rt61pci_autowake_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt61pci_wakeup(rt2x00dev); + rt2x00mmio_register_write(rt2x00dev, + M2H_CMD_DONE_CSR, 0xffffffff); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt61pci_enable_mcu_interrupt(rt2x00dev, MCU_INT_MASK_CSR_TWAKEUP); +} + static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) { struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg_mcu; - u32 reg; + u32 reg_mcu, mask_mcu; + u32 reg, mask; /* * Get the interrupt sources & saved to local variable. * Write register value back to clear pending interrupts. */ - rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®_mcu); - rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg_mcu); + rt2x00mmio_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®_mcu); + rt2x00mmio_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg_mcu); - rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); if (!reg && !reg_mcu) return IRQ_NONE; - if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return IRQ_HANDLED; /* - * Handle interrupts, walk through all bits - * and run the tasks, the bits are checked in order of - * priority. - */ - - /* - * 1 - Rx ring done interrupt. + * Schedule tasklets for interrupt handling. */ if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE)) - rt2x00pci_rxdone(rt2x00dev); + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE)) + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); + + if (rt2x00_get_field32(reg_mcu, MCU_INT_SOURCE_CSR_TWAKEUP)) + tasklet_schedule(&rt2x00dev->autowake_tasklet); /* - * 2 - Tx ring done interrupt. + * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits + * for interrupts and interrupt masks we can just use the value of + * INT_SOURCE_CSR to create the interrupt mask. */ - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) - rt61pci_txdone(rt2x00dev); + mask = reg; + mask_mcu = reg_mcu; /* - * 3 - Handle MCU command done. + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. */ - if (reg_mcu) - rt2x00pci_register_write(rt2x00dev, - M2H_CMD_DONE_CSR, 0xffffffff); + spin_lock(&rt2x00dev->irqmask_lock); + + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + reg |= mask; + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, MCU_INT_MASK_CSR, ®); + reg |= mask_mcu; + rt2x00mmio_register_write(rt2x00dev, MCU_INT_MASK_CSR, reg); + + spin_unlock(&rt2x00dev->irqmask_lock); return IRQ_HANDLED; } @@ -1816,7 +2394,7 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) u8 *mac; s8 value; - rt2x00pci_register_read(rt2x00dev, E2PROM_CSR, ®); + rt2x00mmio_register_read(rt2x00dev, E2PROM_CSR, ®); eeprom.data = rt2x00dev; eeprom.register_read = rt61pci_eepromregister_read; @@ -1836,10 +2414,8 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { - DECLARE_MAC_BUF(macbuf); - - random_ether_addr(mac); - EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac)); + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); @@ -1854,19 +2430,20 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF5225); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_ENABLE_DIVERSITY, 0); rt2x00_set_field16(&word, EEPROM_NIC_TX_DIVERSITY, 0); - rt2x00_set_field16(&word, EEPROM_NIC_TX_RX_FIXED, 0); + rt2x00_set_field16(&word, EEPROM_NIC_RX_FIXED, 0); + rt2x00_set_field16(&word, EEPROM_NIC_TX_FIXED, 0); rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_BG, 0); rt2x00_set_field16(&word, EEPROM_NIC_CARDBUS_ACCEL, 0); rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA_A, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &word); @@ -1874,7 +2451,7 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_LED_LED_MODE, LED_MODE_DEFAULT); rt2x00_eeprom_write(rt2x00dev, EEPROM_LED, word); - EEPROM(rt2x00dev, "Led: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Led: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); @@ -1882,7 +2459,7 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); rt2x00_set_field16(&word, EEPROM_FREQ_SEQ, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); - EEPROM(rt2x00dev, "Freq: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &word); @@ -1890,7 +2467,7 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); - EEPROM(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); } else { value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_1); if (value < -10 || value > 10) @@ -1906,7 +2483,7 @@ static int rt61pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); - EEPROM(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET A: 0x%04x\n", word); } else { value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_1); if (value < -10 || value > 10) @@ -1925,7 +2502,6 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) u32 reg; u16 value; u16 eeprom; - u16 device; /* * Read EEPROM word for configuration. @@ -1934,28 +2510,25 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Identify RF chipset. - * To determine the RT chip we have to read the - * PCI header of the device. */ - pci_read_config_word(rt2x00dev_pci(rt2x00dev), - PCI_CONFIG_HEADER_DEVICE, &device); value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt2x00pci_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip(rt2x00dev, device, value, reg); - - if (!rt2x00_rf(&rt2x00dev->chip, RF5225) && - !rt2x00_rf(&rt2x00dev->chip, RF5325) && - !rt2x00_rf(&rt2x00dev->chip, RF2527) && - !rt2x00_rf(&rt2x00dev->chip, RF2529)) { - ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); + rt2x00mmio_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), + value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); + + if (!rt2x00_rf(rt2x00dev, RF5225) && + !rt2x00_rf(rt2x00dev, RF5325) && + !rt2x00_rf(rt2x00dev, RF2527) && + !rt2x00_rf(rt2x00dev, RF2529)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); return -ENODEV; } /* - * Determine number of antenna's. + * Determine number of antennas. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_NUM) == 2) - __set_bit(CONFIG_DOUBLE_ANTENNA, &rt2x00dev->flags); + __set_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags); /* * Identify default antenna configuration. @@ -1969,22 +2542,20 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * Read the Frame type. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) - __set_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags); + __set_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags); /* - * Detect if this device has an hardware controlled radio. + * Detect if this device has a hardware controlled radio. */ -#ifdef CONFIG_RT61PCI_RFKILL if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) - __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT61PCI_RFKILL */ + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Read frequency offset and RF programming sequence. */ rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_FREQ_SEQ)) - __set_bit(CONFIG_RF_SEQUENCE, &rt2x00dev->flags); + __set_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags); rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET); @@ -1994,35 +2565,21 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A)) - __set_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags); + __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG)) - __set_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags); + __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); /* - * When working with a RF2529 chip without double antenna + * When working with a RF2529 chip without double antenna, * the antenna settings should be gathered from the NIC * eeprom word. */ - if (rt2x00_rf(&rt2x00dev->chip, RF2529) && - !test_bit(CONFIG_DOUBLE_ANTENNA, &rt2x00dev->flags)) { - switch (rt2x00_get_field16(eeprom, EEPROM_NIC_TX_RX_FIXED)) { - case 0: - rt2x00dev->default_ant.tx = ANTENNA_B; - rt2x00dev->default_ant.rx = ANTENNA_A; - break; - case 1: - rt2x00dev->default_ant.tx = ANTENNA_B; - rt2x00dev->default_ant.rx = ANTENNA_B; - break; - case 2: - rt2x00dev->default_ant.tx = ANTENNA_A; - rt2x00dev->default_ant.rx = ANTENNA_A; - break; - case 3: - rt2x00dev->default_ant.tx = ANTENNA_A; - rt2x00dev->default_ant.rx = ANTENNA_B; - break; - } + if (rt2x00_rf(rt2x00dev, RF2529) && + !rt2x00_has_cap_double_antenna(rt2x00dev)) { + rt2x00dev->default_ant.rx = + ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED); + rt2x00dev->default_ant.tx = + ANTENNA_B - rt2x00_get_field16(eeprom, EEPROM_NIC_TX_FIXED); if (rt2x00_get_field16(eeprom, EEPROM_NIC_TX_DIVERSITY)) rt2x00dev->default_ant.tx = ANTENNA_SW_DIVERSITY; @@ -2035,35 +2592,41 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * If the eeprom value is invalid, * switch to default led mode. */ +#ifdef CONFIG_RT2X00_LIB_LEDS rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); + value = rt2x00_get_field16(eeprom, EEPROM_LED_LED_MODE); - rt2x00dev->led_mode = rt2x00_get_field16(eeprom, EEPROM_LED_LED_MODE); + rt61pci_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + rt61pci_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); + if (value == LED_MODE_SIGNAL_STRENGTH) + rt61pci_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_QUALITY); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LED_MODE, - rt2x00dev->led_mode); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_0, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_LED_MODE, value); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_0, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_0)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_1, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_1, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_1)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_2, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_2, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_2)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_3, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_3, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_3)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_4, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_4, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_4)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_ACT, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_ACT, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_READY_BG, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_BG, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_G)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_READY_A, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_A)); +#endif /* CONFIG_RT2X00_LIB_LEDS */ return 0; } @@ -2182,45 +2745,52 @@ static const struct rf_channel rf_vals_seq[] = { { 46, 0x00002ccc, 0x000049a6, 0x0009be55, 0x000c0a23 }, }; -static void rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + /* * Initialize all hw fields. */ rt2x00dev->hw->flags = - IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | - IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; - rt2x00dev->hw->extra_tx_headroom = 0; - rt2x00dev->hw->max_signal = MAX_SIGNAL; - rt2x00dev->hw->max_rssi = MAX_RX_SSI; - rt2x00dev->hw->queues = 5; - - SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_pci(rt2x00dev)->dev); + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* - * Convert tx_power array in eeprom. + * As rt61 has a global fallback table we cannot specify + * more then one tx rate per frame but since the hw will + * try several rates (based on the fallback table) we should + * initialize max_report_rates to the maximum number of rates + * we are going to try. Otherwise mac80211 will truncate our + * reported tx rates and the rc algortihm will end up with + * incorrect data. */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + rt2x00dev->hw->max_rates = 1; + rt2x00dev->hw->max_report_rates = 7; + rt2x00dev->hw->max_rate_tries = 1; /* * Initialize hw_mode information. */ - spec->num_modes = 2; - spec->num_rates = 12; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (!test_bit(CONFIG_RF_SEQUENCE, &rt2x00dev->flags)) { + if (!rt2x00_has_cap_rf_sequence(rt2x00dev)) { spec->num_channels = 14; spec->channels = rf_vals_noseq; } else { @@ -2228,22 +2798,47 @@ static void rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->channels = rf_vals_seq; } - if (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF5325)) { - spec->num_modes = 3; + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF5325)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_seq); + } + + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + spec->channels_info = info; - spec->tx_power_a = txpower; + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); + for (i = 0; i < 14; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } + + if (spec->num_channels > 14) { + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); + for (i = 14; i < spec->num_channels; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = + TXPOWER_FROM_DEV(tx_power[i - 14]); + } + } + + return 0; } static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; + u32 reg; + + /* + * Disable power saving. + */ + rt2x00mmio_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000007); /* * Allocate eeprom data. @@ -2257,14 +2852,34 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) return retval; /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2x00mmio_register_read(rt2x00dev, MAC_CSR13, ®); + rt2x00_set_field32(®, MAC_CSR13_DIR5, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_CSR13, reg); + + /* * Initialize hw specifications. */ - rt61pci_probe_hw_mode(rt2x00dev); + retval = rt61pci_probe_hw_mode(rt2x00dev); + if (retval) + return retval; /* - * This device requires firmware + * This device has multiple filters for control frames, + * but has no a separate filter for PS Poll frames. */ - __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); + __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); + + /* + * This device requires firmware and DMA mapped skbs. + */ + __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_DMA, &rt2x00dev->cap_flags); + if (!modparam_nohwcrypt) + __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); /* * Set the rssi offset. @@ -2277,164 +2892,78 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static void rt61pci_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) +static int rt61pci_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + struct rt2x00_field32 field; + int retval; u32 reg; + u32 offset; /* - * Mask off any flags we are going to ignore from - * the total_flags field. + * First pass the configuration through rt2x00lib, that will + * update the queue settings and validate the input. After that + * we are free to update the registers based on the value + * in the queue parameter. */ - *total_flags &= - FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_PROMISC_IN_BSS; + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); + if (retval) + return retval; /* - * Apply some rules to the filters: - * - Some filters imply different filters to be set. - * - Some things we can't filter out at all. - * - Multicast filter seems to kill broadcast traffic so never use it. + * We only need to perform additional register initialization + * for WMM queues. */ - *total_flags |= FIF_ALLMULTI; - if (*total_flags & FIF_OTHER_BSS || - *total_flags & FIF_PROMISC_IN_BSS) - *total_flags |= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS; + if (queue_idx >= 4) + return 0; - /* - * Check if there is any work left for us. - */ - if (rt2x00dev->packet_filter == *total_flags) - return; - rt2x00dev->packet_filter = *total_flags; + queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, - !(*total_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, - !(*total_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, - !(*total_flags & FIF_CONTROL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); - rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, - !(*total_flags & FIF_ALLMULTI)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_BORADCAST, 0); - rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, 1); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg); -} + /* Update WMM TXOP register */ + offset = AC_TXOP_CSR0 + (sizeof(u32) * (!!(queue_idx & 2))); + field.bit_offset = (queue_idx & 1) * 16; + field.bit_mask = 0xffff << field.bit_offset; -static int rt61pci_set_retry_limit(struct ieee80211_hw *hw, - u32 short_retry, u32 long_retry) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; + rt2x00mmio_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, queue->txop); + rt2x00mmio_register_write(rt2x00dev, offset, reg); + + /* Update WMM registers */ + field.bit_offset = queue_idx * 4; + field.bit_mask = 0xf << field.bit_offset; + + rt2x00mmio_register_read(rt2x00dev, AIFSN_CSR, ®); + rt2x00_set_field32(®, field, queue->aifs); + rt2x00mmio_register_write(rt2x00dev, AIFSN_CSR, reg); - rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, long_retry); - rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, short_retry); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg); + rt2x00mmio_register_read(rt2x00dev, CWMIN_CSR, ®); + rt2x00_set_field32(®, field, queue->cw_min); + rt2x00mmio_register_write(rt2x00dev, CWMIN_CSR, reg); + + rt2x00mmio_register_read(rt2x00dev, CWMAX_CSR, ®); + rt2x00_set_field32(®, field, queue->cw_max); + rt2x00mmio_register_write(rt2x00dev, CWMAX_CSR, reg); return 0; } -static u64 rt61pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; - rt2x00pci_register_read(rt2x00dev, TXRX_CSR13, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR13, ®); tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; - rt2x00pci_register_read(rt2x00dev, TXRX_CSR12, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR12, ®); tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); return tsf; } -static void rt61pci_reset_tsf(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - rt2x00pci_register_write(rt2x00dev, TXRX_CSR12, 0); - rt2x00pci_register_write(rt2x00dev, TXRX_CSR13, 0); -} - -static int rt61pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_tx_control *control) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct skb_desc *desc; - struct data_ring *ring; - struct data_entry *entry; - - /* - * Just in case the ieee80211 doesn't set this, - * but we need this queue set for the descriptor - * initialization. - */ - control->queue = IEEE80211_TX_QUEUE_BEACON; - ring = rt2x00lib_get_ring(rt2x00dev, control->queue); - entry = rt2x00_get_data_entry(ring); - - /* - * We need to append the descriptor in front of the - * beacon frame. - */ - if (skb_headroom(skb) < TXD_DESC_SIZE) { - if (pskb_expand_head(skb, TXD_DESC_SIZE, 0, GFP_ATOMIC)) { - dev_kfree_skb(skb); - return -ENOMEM; - } - } - - /* - * Add the descriptor in front of the skb. - */ - skb_push(skb, ring->desc_size); - memset(skb->data, 0, ring->desc_size); - - /* - * Fill in skb descriptor - */ - desc = get_skb_desc(skb); - desc->desc_len = ring->desc_size; - desc->data_len = skb->len - ring->desc_size; - desc->desc = skb->data; - desc->data = skb->data + ring->desc_size; - desc->ring = ring; - desc->entry = entry; - - rt2x00lib_write_tx_desc(rt2x00dev, skb, control); - - /* - * Write entire beacon with descriptor to register, - * and kick the beacon generator. - */ - rt2x00pci_register_multiwrite(rt2x00dev, HW_BEACON_BASE0, - skb->data, skb->len); - rt61pci_kick_tx_queue(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); - - return 0; -} - static const struct ieee80211_ops rt61pci_mac80211_ops = { .tx = rt2x00mac_tx, .start = rt2x00mac_start, @@ -2442,66 +2971,117 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, - .configure_filter = rt61pci_configure_filter, + .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, - .set_retry_limit = rt61pci_set_retry_limit, .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2x00mac_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, + .conf_tx = rt61pci_conf_tx, .get_tsf = rt61pci_get_tsf, - .reset_tsf = rt61pci_reset_tsf, - .beacon_update = rt61pci_beacon_update, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { .irq_handler = rt61pci_interrupt, + .txstatus_tasklet = rt61pci_txstatus_tasklet, + .tbtt_tasklet = rt61pci_tbtt_tasklet, + .rxdone_tasklet = rt61pci_rxdone_tasklet, + .autowake_tasklet = rt61pci_autowake_tasklet, .probe_hw = rt61pci_probe_hw, .get_firmware_name = rt61pci_get_firmware_name, + .check_firmware = rt61pci_check_firmware, .load_firmware = rt61pci_load_firmware, - .initialize = rt2x00pci_initialize, - .uninitialize = rt2x00pci_uninitialize, - .init_rxentry = rt61pci_init_rxentry, - .init_txentry = rt61pci_init_txentry, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt61pci_get_entry_state, + .clear_entry = rt61pci_clear_entry, .set_device_state = rt61pci_set_device_state, .rfkill_poll = rt61pci_rfkill_poll, .link_stats = rt61pci_link_stats, .reset_tuner = rt61pci_reset_tuner, .link_tuner = rt61pci_link_tuner, + .start_queue = rt61pci_start_queue, + .kick_queue = rt61pci_kick_queue, + .stop_queue = rt61pci_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, .write_tx_desc = rt61pci_write_tx_desc, - .write_tx_data = rt2x00pci_write_tx_data, - .kick_tx_queue = rt61pci_kick_tx_queue, + .write_beacon = rt61pci_write_beacon, + .clear_beacon = rt61pci_clear_beacon, .fill_rxdone = rt61pci_fill_rxdone, - .config_mac_addr = rt61pci_config_mac_addr, - .config_bssid = rt61pci_config_bssid, - .config_type = rt61pci_config_type, - .config_preamble = rt61pci_config_preamble, + .config_shared_key = rt61pci_config_shared_key, + .config_pairwise_key = rt61pci_config_pairwise_key, + .config_filter = rt61pci_config_filter, + .config_intf = rt61pci_config_intf, + .config_erp = rt61pci_config_erp, + .config_ant = rt61pci_config_ant, .config = rt61pci_config, }; +static void rt61pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 4; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXINFO_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} + static const struct rt2x00_ops rt61pci_ops = { - .name = KBUILD_MODNAME, - .rxd_size = RXD_DESC_SIZE, - .txd_size = TXD_DESC_SIZE, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .lib = &rt61pci_rt2x00_ops, - .hw = &rt61pci_mac80211_ops, + .name = KBUILD_MODNAME, + .max_ap_intf = 4, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt61pci_queue_init, + .lib = &rt61pci_rt2x00_ops, + .hw = &rt61pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt61pci_rt2x00debug, + .debugfs = &rt61pci_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; /* * RT61pci module information. */ -static struct pci_device_id rt61pci_device_table[] = { +static DEFINE_PCI_DEVICE_TABLE(rt61pci_device_table) = { /* RT2561s */ - { PCI_DEVICE(0x1814, 0x0301), PCI_DEVICE_DATA(&rt61pci_ops) }, + { PCI_DEVICE(0x1814, 0x0301) }, /* RT2561 v2 */ - { PCI_DEVICE(0x1814, 0x0302), PCI_DEVICE_DATA(&rt61pci_ops) }, + { PCI_DEVICE(0x1814, 0x0302) }, /* RT2661 */ - { PCI_DEVICE(0x1814, 0x0401), PCI_DEVICE_DATA(&rt61pci_ops) }, + { PCI_DEVICE(0x1814, 0x0401) }, { 0, } }; @@ -2516,24 +3096,19 @@ MODULE_FIRMWARE(FIRMWARE_RT2561s); MODULE_FIRMWARE(FIRMWARE_RT2661); MODULE_LICENSE("GPL"); +static int rt61pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + return rt2x00pci_probe(pci_dev, &rt61pci_ops); +} + static struct pci_driver rt61pci_driver = { .name = KBUILD_MODNAME, .id_table = rt61pci_device_table, - .probe = rt2x00pci_probe, - .remove = __devexit_p(rt2x00pci_remove), + .probe = rt61pci_probe, + .remove = rt2x00pci_remove, .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; -static int __init rt61pci_init(void) -{ - return pci_register_driver(&rt61pci_driver); -} - -static void __exit rt61pci_exit(void) -{ - pci_unregister_driver(&rt61pci_driver); -} - -module_init(rt61pci_init); -module_exit(rt61pci_exit); +module_pci_driver(rt61pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h index 4c6524eedad..1442075a838 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.h +++ b/drivers/net/wireless/rt2x00/rt61pci.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -28,6 +26,13 @@ #define RT61PCI_H /* + * RT chip PCI IDs. + */ +#define RT2561s_PCI_ID 0x0301 +#define RT2561_PCI_ID 0x0302 +#define RT2661_PCI_ID 0x0401 + +/* * RF chip defines. */ #define RF5225 0x0001 @@ -37,10 +42,8 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ -#define MAX_SIGNAL 100 -#define MAX_RX_SSI -1 #define DEFAULT_RSSI_OFFSET 120 /* @@ -50,18 +53,19 @@ #define CSR_REG_SIZE 0x04b0 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0100 +#define BBP_BASE 0x0000 #define BBP_SIZE 0x0080 -#define RF_SIZE 0x0014 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 /* - * PCI registers. + * Number of TX queues. */ +#define NUM_TX_QUEUES 4 /* - * PCI Configuration Header + * PCI registers. */ -#define PCI_CONFIG_HEADER_VENDOR 0x0000 -#define PCI_CONFIG_HEADER_DEVICE 0x0002 /* * HOST_CMD_CSR: For HOST to interrupt embedded processor @@ -83,8 +87,10 @@ /* * SOFT_RESET_CSR + * FORCE_CLOCK_ON: Host force MAC clock ON */ #define SOFT_RESET_CSR 0x0010 +#define SOFT_RESET_CSR_FORCE_CLOCK_ON FIELD32(0x00000002) /* * MCU_INT_SOURCE_CSR: MCU interrupt source/mask register. @@ -131,16 +137,27 @@ #define PAIRWISE_KEY_TABLE_BASE 0x1200 #define PAIRWISE_TA_TABLE_BASE 0x1a00 +#define SHARED_KEY_ENTRY(__idx) \ + ( SHARED_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_KEY_ENTRY(__idx) \ + ( PAIRWISE_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_TA_ENTRY(__idx) \ + ( PAIRWISE_TA_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) + struct hw_key_entry { u8 key[16]; u8 tx_mic[8]; u8 rx_mic[8]; -} __attribute__ ((packed)); +} __packed; struct hw_pairwise_ta_entry { u8 address[6]; - u8 reserved[2]; -} __attribute__ ((packed)); + u8 cipher; + u8 reserved; +} __packed; /* * Other on-chip shared memory space. @@ -161,7 +178,9 @@ struct hw_pairwise_ta_entry { #define HW_BEACON_BASE1 0x2d00 #define HW_BEACON_BASE2 0x2e00 #define HW_BEACON_BASE3 0x2f00 -#define HW_BEACON_OFFSET 0x0100 + +#define HW_BEACON_OFFSET(__index) \ + ( HW_BEACON_BASE0 + (__index * 0x0100) ) /* * HOST-MCU shared memory. @@ -211,6 +230,8 @@ struct hw_pairwise_ta_entry { * MAC_CSR0: ASIC revision number. */ #define MAC_CSR0 0x3000 +#define MAC_CSR0_REVISION FIELD32(0x0000000f) +#define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) /* * MAC_CSR1: System control register. @@ -234,6 +255,11 @@ struct hw_pairwise_ta_entry { /* * MAC_CSR3: STA MAC register 1. + * UNICAST_TO_ME_MASK: + * Used to mask off bits from byte 5 of the MAC address + * to determine the UNICAST_TO_ME bit for RX frames. + * The full mask is complemented by BSS_ID_MASK: + * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK */ #define MAC_CSR3 0x300c #define MAC_CSR3_BYTE4 FIELD32(0x000000ff) @@ -251,7 +277,14 @@ struct hw_pairwise_ta_entry { /* * MAC_CSR5: BSSID register 1. - * BSS_ID_MASK: 3: one BSSID, 0: 4 BSSID, 2 or 1: 2 BSSID. + * BSS_ID_MASK: + * This mask is used to mask off bits 0 and 1 of byte 5 of the + * BSSID. This will make sure that those bits will be ignored + * when determining the MY_BSS of RX frames. + * 0: 1-BSSID mode (BSS index = 0) + * 1: 2-BSSID mode (BSS index: Byte5, bit 0) + * 2: 2-BSSID mode (BSS index: byte5, bit 1) + * 3: 4-BSSID mode (BSS index: byte5, bit 0 - 1) */ #define MAC_CSR5 0x3014 #define MAC_CSR5_BYTE4 FIELD32(0x000000ff) @@ -322,21 +355,22 @@ struct hw_pairwise_ta_entry { /* * MAC_CSR13: GPIO. + * MAC_CSR13_VALx: GPIO value + * MAC_CSR13_DIRx: GPIO direction: 0 = output; 1 = input */ #define MAC_CSR13 0x3034 -#define MAC_CSR13_BIT0 FIELD32(0x00000001) -#define MAC_CSR13_BIT1 FIELD32(0x00000002) -#define MAC_CSR13_BIT2 FIELD32(0x00000004) -#define MAC_CSR13_BIT3 FIELD32(0x00000008) -#define MAC_CSR13_BIT4 FIELD32(0x00000010) -#define MAC_CSR13_BIT5 FIELD32(0x00000020) -#define MAC_CSR13_BIT6 FIELD32(0x00000040) -#define MAC_CSR13_BIT7 FIELD32(0x00000080) -#define MAC_CSR13_BIT8 FIELD32(0x00000100) -#define MAC_CSR13_BIT9 FIELD32(0x00000200) -#define MAC_CSR13_BIT10 FIELD32(0x00000400) -#define MAC_CSR13_BIT11 FIELD32(0x00000800) -#define MAC_CSR13_BIT12 FIELD32(0x00001000) +#define MAC_CSR13_VAL0 FIELD32(0x00000001) +#define MAC_CSR13_VAL1 FIELD32(0x00000002) +#define MAC_CSR13_VAL2 FIELD32(0x00000004) +#define MAC_CSR13_VAL3 FIELD32(0x00000008) +#define MAC_CSR13_VAL4 FIELD32(0x00000010) +#define MAC_CSR13_VAL5 FIELD32(0x00000020) +#define MAC_CSR13_DIR0 FIELD32(0x00000100) +#define MAC_CSR13_DIR1 FIELD32(0x00000200) +#define MAC_CSR13_DIR2 FIELD32(0x00000400) +#define MAC_CSR13_DIR3 FIELD32(0x00000800) +#define MAC_CSR13_DIR4 FIELD32(0x00001000) +#define MAC_CSR13_DIR5 FIELD32(0x00002000) /* * MAC_CSR14: LED control register. @@ -377,7 +411,7 @@ struct hw_pairwise_ta_entry { * DROP_VERSION_ERROR: Drop version error frame. * DROP_MULTICAST: Drop multicast frames. * DROP_BORADCAST: Drop broadcast frames. - * ROP_ACK_CTS: Drop received ACK and CTS. + * DROP_ACK_CTS: Drop received ACK and CTS. */ #define TXRX_CSR0 0x3040 #define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) @@ -391,7 +425,7 @@ struct hw_pairwise_ta_entry { #define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) #define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) #define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) -#define TXRX_CSR0_DROP_BORADCAST FIELD32(0x01000000) +#define TXRX_CSR0_DROP_BROADCAST FIELD32(0x01000000) #define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) #define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) @@ -645,6 +679,10 @@ struct hw_pairwise_ta_entry { * SEC_CSR4: Pairwise key table lookup control. */ #define SEC_CSR4 0x30b0 +#define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) +#define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) +#define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) +#define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) /* * SEC_CSR5: shared key table security mode register. @@ -745,25 +783,25 @@ struct hw_pairwise_ta_entry { */ /* - * AC0_BASE_CSR: AC_BK base address. + * AC0_BASE_CSR: AC_VO base address. */ #define AC0_BASE_CSR 0x3400 #define AC0_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* - * AC1_BASE_CSR: AC_BE base address. + * AC1_BASE_CSR: AC_VI base address. */ #define AC1_BASE_CSR 0x3404 #define AC1_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* - * AC2_BASE_CSR: AC_VI base address. + * AC2_BASE_CSR: AC_BE base address. */ #define AC2_BASE_CSR 0x3408 #define AC2_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* - * AC3_BASE_CSR: AC_VO base address. + * AC3_BASE_CSR: AC_BK base address. */ #define AC3_BASE_CSR 0x340c #define AC3_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) @@ -775,7 +813,7 @@ struct hw_pairwise_ta_entry { #define MGMT_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) /* - * TX_RING_CSR0: TX Ring size for AC_BK, AC_BE, AC_VI, AC_VO. + * TX_RING_CSR0: TX Ring size for AC_VO, AC_VI, AC_BE, AC_BK. */ #define TX_RING_CSR0 0x3418 #define TX_RING_CSR0_AC0_RING_SIZE FIELD32(0x000000ff) @@ -794,10 +832,10 @@ struct hw_pairwise_ta_entry { /* * AIFSN_CSR: AIFSN for each EDCA AC. - * AIFSN0: For AC_BK. - * AIFSN1: For AC_BE. - * AIFSN2: For AC_VI. - * AIFSN3: For AC_VO. + * AIFSN0: For AC_VO. + * AIFSN1: For AC_VI. + * AIFSN2: For AC_BE. + * AIFSN3: For AC_BK. */ #define AIFSN_CSR 0x3420 #define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) @@ -807,10 +845,10 @@ struct hw_pairwise_ta_entry { /* * CWMIN_CSR: CWmin for each EDCA AC. - * CWMIN0: For AC_BK. - * CWMIN1: For AC_BE. - * CWMIN2: For AC_VI. - * CWMIN3: For AC_VO. + * CWMIN0: For AC_VO. + * CWMIN1: For AC_VI. + * CWMIN2: For AC_BE. + * CWMIN3: For AC_BK. */ #define CWMIN_CSR 0x3424 #define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) @@ -820,10 +858,10 @@ struct hw_pairwise_ta_entry { /* * CWMAX_CSR: CWmax for each EDCA AC. - * CWMAX0: For AC_BK. - * CWMAX1: For AC_BE. - * CWMAX2: For AC_VI. - * CWMAX3: For AC_VO. + * CWMAX0: For AC_VO. + * CWMAX1: For AC_VI. + * CWMAX2: For AC_BE. + * CWMAX3: For AC_BK. */ #define CWMAX_CSR 0x3428 #define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) @@ -844,14 +882,14 @@ struct hw_pairwise_ta_entry { /* * TX_CNTL_CSR: KICK/Abort TX. - * KICK_TX_AC0: For AC_BK. - * KICK_TX_AC1: For AC_BE. - * KICK_TX_AC2: For AC_VI. - * KICK_TX_AC3: For AC_VO. - * ABORT_TX_AC0: For AC_BK. - * ABORT_TX_AC1: For AC_BE. - * ABORT_TX_AC2: For AC_VI. - * ABORT_TX_AC3: For AC_VO. + * KICK_TX_AC0: For AC_VO. + * KICK_TX_AC1: For AC_VI. + * KICK_TX_AC2: For AC_BE. + * KICK_TX_AC3: For AC_BK. + * ABORT_TX_AC0: For AC_VO. + * ABORT_TX_AC1: For AC_VI. + * ABORT_TX_AC2: For AC_BE. + * ABORT_TX_AC3: For AC_BK. */ #define TX_CNTL_CSR 0x3430 #define TX_CNTL_CSR_KICK_TX_AC0 FIELD32(0x00000001) @@ -866,7 +904,7 @@ struct hw_pairwise_ta_entry { #define TX_CNTL_CSR_ABORT_TX_MGMT FIELD32(0x00100000) /* - * LOAD_TX_RING_CSR: Load RX de + * LOAD_TX_RING_CSR: Load RX desriptor */ #define LOAD_TX_RING_CSR 0x3434 #define LOAD_TX_RING_CSR_LOAD_TXD_AC0 FIELD32(0x00000001) @@ -971,18 +1009,18 @@ struct hw_pairwise_ta_entry { #define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) /* - * AC_TXOP_CSR0: AC_BK/AC_BE TXOP register. - * AC0_TX_OP: For AC_BK, in unit of 32us. - * AC1_TX_OP: For AC_BE, in unit of 32us. + * AC_TXOP_CSR0: AC_VO/AC_VI TXOP register. + * AC0_TX_OP: For AC_VO, in unit of 32us. + * AC1_TX_OP: For AC_VI, in unit of 32us. */ #define AC_TXOP_CSR0 0x3474 #define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) #define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) /* - * AC_TXOP_CSR1: AC_VO/AC_VI TXOP register. - * AC2_TX_OP: For AC_VI, in unit of 32us. - * AC3_TX_OP: For AC_VO, in unit of 32us. + * AC_TXOP_CSR1: AC_BE/AC_BK TXOP register. + * AC2_TX_OP: For AC_BE, in unit of 32us. + * AC3_TX_OP: For AC_BK, in unit of 32us. */ #define AC_TXOP_CSR1 0x3478 #define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) @@ -1020,8 +1058,10 @@ struct hw_pairwise_ta_entry { /* * IO_CNTL_CSR + * RF_PS: Set RF interface value to power save */ #define IO_CNTL_CSR 0x3498 +#define IO_CNTL_CSR_RF_PS FIELD32(0x00000004) /* * UART_INT_SOURCE_CSR @@ -1116,10 +1156,10 @@ struct hw_pairwise_ta_entry { #define EEPROM_MAC_ADDR_0 0x0002 #define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) -#define EEPROM_MAC_ADDR1 0x0004 +#define EEPROM_MAC_ADDR1 0x0003 #define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) -#define EEPROM_MAC_ADDR_2 0x0006 +#define EEPROM_MAC_ADDR_2 0x0004 #define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) #define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) @@ -1152,7 +1192,8 @@ struct hw_pairwise_ta_entry { #define EEPROM_NIC 0x0011 #define EEPROM_NIC_ENABLE_DIVERSITY FIELD16(0x0001) #define EEPROM_NIC_TX_DIVERSITY FIELD16(0x0002) -#define EEPROM_NIC_TX_RX_FIXED FIELD16(0x000c) +#define EEPROM_NIC_RX_FIXED FIELD16(0x0004) +#define EEPROM_NIC_TX_FIXED FIELD16(0x0008) #define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0010) #define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0020) #define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0040) @@ -1247,6 +1288,7 @@ struct hw_pairwise_ta_entry { * DMA descriptor defines. */ #define TXD_DESC_SIZE ( 16 * sizeof(__le32) ) +#define TXINFO_SIZE ( 6 * sizeof(__le32) ) #define RXD_DESC_SIZE ( 16 * sizeof(__le32) ) /* @@ -1410,8 +1452,10 @@ struct hw_pairwise_ta_entry { /* * Word4 + * ICV: Received ICV of originally encrypted. + * NOTE: This is a guess, the official definition is "reserved" */ -#define RXD_W4_RESERVED FIELD32(0xffffffff) +#define RXD_W4_ICV FIELD32(0xffffffff) /* * the above 20-byte is called RXINFO and will be DMAed to MAC RX block @@ -1440,24 +1484,17 @@ struct hw_pairwise_ta_entry { #define RXD_W15_RESERVED FIELD32(0xffffffff) /* - * Macro's for converting txpower from EEPROM to dscape value - * and from dscape value to register value. + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? \ - DEFAULT_TXPOWER : (__txpower); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ - (__txpower)); \ -}) +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT61PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 3909cf42f47..95724ff9c72 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -24,11 +22,12 @@ Supported chipsets: rt2571W & rt2671. */ +#include <linux/crc-itu-t.h> #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/usb.h> #include "rt2x00.h" @@ -36,9 +35,16 @@ #include "rt73usb.h" /* + * Allow hardware encryption to be disabled. + */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +/* * Register access. * All access to the CSR registers will go through the methods - * rt73usb_register_read and rt73usb_register_write. + * rt2x00usb_register_read and rt2x00usb_register_write. * BBP and RF register require indirect register access, * and use the CSR registers BBPCSR and RFCSR to achieve this. * These indirect registers work with busy bits, @@ -47,109 +53,35 @@ * between each attampt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. - * The _lock versions must be used if you already hold the usb_cache_mutex + * The _lock versions must be used if you already hold the csr_mutex */ -static inline void rt73usb_register_read(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, u32 *value) -{ - __le32 reg; - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - ®, sizeof(u32), REGISTER_TIMEOUT); - *value = le32_to_cpu(reg); -} - -static inline void rt73usb_register_read_lock(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, u32 *value) -{ - __le32 reg; - rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - ®, sizeof(u32), REGISTER_TIMEOUT); - *value = le32_to_cpu(reg); -} - -static inline void rt73usb_register_multiread(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u32 length) -{ - int timeout = REGISTER_TIMEOUT * (length / sizeof(u32)); - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ, - USB_VENDOR_REQUEST_IN, offset, - value, length, timeout); -} - -static inline void rt73usb_register_write(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, u32 value) -{ - __le32 reg = cpu_to_le32(value); - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - ®, sizeof(u32), REGISTER_TIMEOUT); -} - -static inline void rt73usb_register_write_lock(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, u32 value) -{ - __le32 reg = cpu_to_le32(value); - rt2x00usb_vendor_req_buff_lock(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - ®, sizeof(u32), REGISTER_TIMEOUT); -} - -static inline void rt73usb_register_multiwrite(struct rt2x00_dev *rt2x00dev, - const unsigned int offset, - void *value, const u32 length) -{ - int timeout = REGISTER_TIMEOUT * (length / sizeof(u32)); - rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, offset, - value, length, timeout); -} - -static u32 rt73usb_bbp_check(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - unsigned int i; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt73usb_register_read_lock(rt2x00dev, PHY_CSR3, ®); - if (!rt2x00_get_field32(reg, PHY_CSR3_BUSY)) - break; - udelay(REGISTER_BUSY_DELAY); - } - - return reg; -} +#define WAIT_FOR_BBP(__dev, __reg) \ + rt2x00usb_regbusy_read((__dev), PHY_CSR3, PHY_CSR3_BUSY, (__reg)) +#define WAIT_FOR_RF(__dev, __reg) \ + rt2x00usb_regbusy_read((__dev), PHY_CSR4, PHY_CSR4_BUSY, (__reg)) static void rt73usb_bbp_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) { u32 reg; - mutex_lock(&rt2x00dev->usb_cache_mutex); + mutex_lock(&rt2x00dev->csr_mutex); /* - * Wait until the BBP becomes ready. + * Wait until the BBP becomes available, afterwards we + * can safely write the new data into the register. */ - reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Write failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_VALUE, value); + rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); - /* - * Write the data into the BBP. - */ - reg = 0; - rt2x00_set_field32(®, PHY_CSR3_VALUE, value); - rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); - rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); - rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); + rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); + } - rt73usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); - mutex_unlock(&rt2x00dev->usb_cache_mutex); + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, @@ -157,268 +89,487 @@ static void rt73usb_bbp_read(struct rt2x00_dev *rt2x00dev, { u32 reg; - mutex_lock(&rt2x00dev->usb_cache_mutex); + mutex_lock(&rt2x00dev->csr_mutex); /* - * Wait until the BBP becomes ready. + * Wait until the BBP becomes available, afterwards we + * can safely write the read request into the register. + * After the data has been written, we wait until hardware + * returns the correct value, if at any time the register + * doesn't become available in time, reg will be 0xffffffff + * which means we return 0xff to the caller. */ - reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); - mutex_unlock(&rt2x00dev->usb_cache_mutex); - return; - } + if (WAIT_FOR_BBP(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); - /* - * Write the request into the BBP. - */ - reg = 0; - rt2x00_set_field32(®, PHY_CSR3_REGNUM, word); - rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); - rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); - - rt73usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); + rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR3, reg); - /* - * Wait until the BBP becomes ready. - */ - reg = rt73usb_bbp_check(rt2x00dev); - if (rt2x00_get_field32(reg, PHY_CSR3_BUSY)) { - ERROR(rt2x00dev, "PHY_CSR3 register busy. Read failed.\n"); - *value = 0xff; - return; + WAIT_FOR_BBP(rt2x00dev, ®); } *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); - mutex_unlock(&rt2x00dev->usb_cache_mutex); + + mutex_unlock(&rt2x00dev->csr_mutex); } static void rt73usb_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { u32 reg; - unsigned int i; - - if (!word) - return; - mutex_lock(&rt2x00dev->usb_cache_mutex); - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt73usb_register_read_lock(rt2x00dev, PHY_CSR4, ®); - if (!rt2x00_get_field32(reg, PHY_CSR4_BUSY)) - goto rf_write; - udelay(REGISTER_BUSY_DELAY); - } - - mutex_unlock(&rt2x00dev->usb_cache_mutex); - ERROR(rt2x00dev, "PHY_CSR4 register busy. Write failed.\n"); - return; - -rf_write: - reg = 0; - rt2x00_set_field32(®, PHY_CSR4_VALUE, value); + mutex_lock(&rt2x00dev->csr_mutex); /* - * RF5225 and RF2527 contain 21 bits per RF register value, - * all others contain 20 bits. + * Wait until the RF becomes available, afterwards we + * can safely write the new data into the register. */ - rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, - 20 + (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF2527))); - rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); - rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); + if (WAIT_FOR_RF(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, PHY_CSR4_VALUE, value); + /* + * RF5225 and RF2527 contain 21 bits per RF register value, + * all others contain 20 bits. + */ + rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, + 20 + (rt2x00_rf(rt2x00dev, RF5225) || + rt2x00_rf(rt2x00dev, RF2527))); + rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); + rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); + + rt2x00usb_register_write_lock(rt2x00dev, PHY_CSR4, reg); + rt2x00_rf_write(rt2x00dev, word, value); + } - rt73usb_register_write_lock(rt2x00dev, PHY_CSR4, reg); - rt2x00_rf_write(rt2x00dev, word, value); - mutex_unlock(&rt2x00dev->usb_cache_mutex); + mutex_unlock(&rt2x00dev->csr_mutex); } #ifdef CONFIG_RT2X00_LIB_DEBUGFS -#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) ) - -static void rt73usb_read_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 *data) -{ - rt73usb_register_read(rt2x00dev, CSR_OFFSET(word), data); -} - -static void rt73usb_write_csr(struct rt2x00_dev *rt2x00dev, - const unsigned int word, u32 data) -{ - rt73usb_register_write(rt2x00dev, CSR_OFFSET(word), data); -} - static const struct rt2x00debug rt73usb_rt2x00debug = { .owner = THIS_MODULE, .csr = { - .read = rt73usb_read_csr, - .write = rt73usb_write_csr, + .read = rt2x00usb_register_read, + .write = rt2x00usb_register_write, + .flags = RT2X00DEBUGFS_OFFSET, + .word_base = CSR_REG_BASE, .word_size = sizeof(u32), .word_count = CSR_REG_SIZE / sizeof(u32), }, .eeprom = { .read = rt2x00_eeprom_read, .write = rt2x00_eeprom_write, + .word_base = EEPROM_BASE, .word_size = sizeof(u16), .word_count = EEPROM_SIZE / sizeof(u16), }, .bbp = { .read = rt73usb_bbp_read, .write = rt73usb_bbp_write, + .word_base = BBP_BASE, .word_size = sizeof(u8), .word_count = BBP_SIZE / sizeof(u8), }, .rf = { .read = rt2x00_rf_read, .write = rt73usb_rf_write, + .word_base = RF_BASE, .word_size = sizeof(u32), .word_count = RF_SIZE / sizeof(u32), }, }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -/* - * Configuration handlers. - */ -static void rt73usb_config_mac_addr(struct rt2x00_dev *rt2x00dev, __le32 *mac) +static int rt73usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) { - u32 tmp; + u32 reg; - tmp = le32_to_cpu(mac[1]); - rt2x00_set_field32(&tmp, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); - mac[1] = cpu_to_le32(tmp); + rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); + return rt2x00_get_field32(reg, MAC_CSR13_VAL7); +} - rt73usb_register_multiwrite(rt2x00dev, MAC_CSR2, mac, - (2 * sizeof(__le32))); +#ifdef CONFIG_RT2X00_LIB_LEDS +static void rt73usb_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + unsigned int enabled = brightness != LED_OFF; + unsigned int a_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); + unsigned int bg_mode = + (enabled && led->rt2x00dev->curr_band == IEEE80211_BAND_2GHZ); + + if (led->type == LED_TYPE_RADIO) { + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_RADIO_STATUS, enabled); + + rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, + 0, led->rt2x00dev->led_mcu_reg, + REGISTER_TIMEOUT); + } else if (led->type == LED_TYPE_ASSOC) { + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_LINK_BG_STATUS, bg_mode); + rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg, + MCU_LEDCS_LINK_A_STATUS, a_mode); + + rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, + 0, led->rt2x00dev->led_mcu_reg, + REGISTER_TIMEOUT); + } else if (led->type == LED_TYPE_QUALITY) { + /* + * The brightness is divided into 6 levels (0 - 5), + * this means we need to convert the brightness + * argument into the matching level within that range. + */ + rt2x00usb_vendor_request_sw(led->rt2x00dev, USB_LED_CONTROL, + brightness / (LED_FULL / 6), + led->rt2x00dev->led_mcu_reg, + REGISTER_TIMEOUT); + } } -static void rt73usb_config_bssid(struct rt2x00_dev *rt2x00dev, __le32 *bssid) +static int rt73usb_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) { - u32 tmp; + struct rt2x00_led *led = + container_of(led_cdev, struct rt2x00_led, led_dev); + u32 reg; + + rt2x00usb_register_read(led->rt2x00dev, MAC_CSR14, ®); + rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, *delay_on); + rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, *delay_off); + rt2x00usb_register_write(led->rt2x00dev, MAC_CSR14, reg); - tmp = le32_to_cpu(bssid[1]); - rt2x00_set_field32(&tmp, MAC_CSR5_BSS_ID_MASK, 3); - bssid[1] = cpu_to_le32(tmp); + return 0; +} - rt73usb_register_multiwrite(rt2x00dev, MAC_CSR4, bssid, - (2 * sizeof(__le32))); +static void rt73usb_init_led(struct rt2x00_dev *rt2x00dev, + struct rt2x00_led *led, + enum led_type type) +{ + led->rt2x00dev = rt2x00dev; + led->type = type; + led->led_dev.brightness_set = rt73usb_brightness_set; + led->led_dev.blink_set = rt73usb_blink_set; + led->flags = LED_INITIALIZED; } +#endif /* CONFIG_RT2X00_LIB_LEDS */ -static void rt73usb_config_type(struct rt2x00_dev *rt2x00dev, const int type, - const int tsf_sync) +/* + * Configuration handlers. + */ +static int rt73usb_config_shared_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) { + struct hw_key_entry key_entry; + struct rt2x00_field32 field; + u32 mask; u32 reg; - /* - * Clear current synchronisation setup. - * For the Beacon base registers we only need to clear - * the first byte since that byte contains the VALID and OWNER - * bits which (when set to 0) will invalidate the entire beacon. - */ - rt73usb_register_write(rt2x00dev, TXRX_CSR9, 0); - rt73usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0); - rt73usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0); - rt73usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0); - rt73usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0); + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for shared keys. We have 1 register + * with key valid bits. The goal is simple, read + * the register, if that is full we have no slots + * left. + * Note that each BSS is allowed to have up to 4 + * shared keys, so put a mask over the allowed + * entries. + */ + mask = (0xf << crypto->bssidx); + + rt2x00usb_register_read(rt2x00dev, SEC_CSR0, ®); + reg &= mask; + + if (reg && reg == mask) + return -ENOSPC; + + key->hw_key_idx += reg ? ffz(reg) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = SHARED_KEY_ENTRY(key->hw_key_idx); + rt2x00usb_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + /* + * The cipher types are stored over 2 registers. + * bssidx 0 and 1 keys are stored in SEC_CSR1 and + * bssidx 1 and 2 keys are stored in SEC_CSR5. + * Using the correct defines correctly will cause overhead, + * so just calculate the correct offset. + */ + if (key->hw_key_idx < 8) { + field.bit_offset = (3 * key->hw_key_idx); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00usb_register_read(rt2x00dev, SEC_CSR1, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00usb_register_write(rt2x00dev, SEC_CSR1, reg); + } else { + field.bit_offset = (3 * (key->hw_key_idx - 8)); + field.bit_mask = 0x7 << field.bit_offset; + + rt2x00usb_register_read(rt2x00dev, SEC_CSR5, ®); + rt2x00_set_field32(®, field, crypto->cipher); + rt2x00usb_register_write(rt2x00dev, SEC_CSR5, reg); + } + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided separately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } /* - * Enable synchronisation. + * SEC_CSR0 contains only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead we use + * a calculation to determine the correct bit directly. */ - rt73usb_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, - (tsf_sync == TSF_SYNC_BEACON)); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); - rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, tsf_sync); - rt73usb_register_write(rt2x00dev, TXRX_CSR9, reg); + mask = 1 << key->hw_key_idx; + + rt2x00usb_register_read(rt2x00dev, SEC_CSR0, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00usb_register_write(rt2x00dev, SEC_CSR0, reg); + + return 0; } -static void rt73usb_config_preamble(struct rt2x00_dev *rt2x00dev, - const int short_preamble, - const int ack_timeout, - const int ack_consume_time) +static int rt73usb_config_pairwise_key(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_crypto *crypto, + struct ieee80211_key_conf *key) { + struct hw_pairwise_ta_entry addr_entry; + struct hw_key_entry key_entry; + u32 mask; u32 reg; + if (crypto->cmd == SET_KEY) { + /* + * rt2x00lib can't determine the correct free + * key_idx for pairwise keys. We have 2 registers + * with key valid bits. The goal is simple, read + * the first register, if that is full move to + * the next register. + * When both registers are full, we drop the key, + * otherwise we use the first invalid entry. + */ + rt2x00usb_register_read(rt2x00dev, SEC_CSR2, ®); + if (reg && reg == ~0) { + key->hw_key_idx = 32; + rt2x00usb_register_read(rt2x00dev, SEC_CSR3, ®); + if (reg && reg == ~0) + return -ENOSPC; + } + + key->hw_key_idx += reg ? ffz(reg) : 0; + + /* + * Upload key to hardware + */ + memcpy(key_entry.key, crypto->key, + sizeof(key_entry.key)); + memcpy(key_entry.tx_mic, crypto->tx_mic, + sizeof(key_entry.tx_mic)); + memcpy(key_entry.rx_mic, crypto->rx_mic, + sizeof(key_entry.rx_mic)); + + reg = PAIRWISE_KEY_ENTRY(key->hw_key_idx); + rt2x00usb_register_multiwrite(rt2x00dev, reg, + &key_entry, sizeof(key_entry)); + + /* + * Send the address and cipher type to the hardware register. + */ + memset(&addr_entry, 0, sizeof(addr_entry)); + memcpy(&addr_entry, crypto->address, ETH_ALEN); + addr_entry.cipher = crypto->cipher; + + reg = PAIRWISE_TA_ENTRY(key->hw_key_idx); + rt2x00usb_register_multiwrite(rt2x00dev, reg, + &addr_entry, sizeof(addr_entry)); + + /* + * Enable pairwise lookup table for given BSS idx, + * without this received frames will not be decrypted + * by the hardware. + */ + rt2x00usb_register_read(rt2x00dev, SEC_CSR4, ®); + reg |= (1 << crypto->bssidx); + rt2x00usb_register_write(rt2x00dev, SEC_CSR4, reg); + + /* + * The driver does not support the IV/EIV generation + * in hardware. However it doesn't support the IV/EIV + * inside the ieee80211 frame either, but requires it + * to be provided separately for the descriptor. + * rt2x00lib will cut the IV/EIV data out of all frames + * given to us by mac80211, but we must tell mac80211 + * to generate the IV/EIV data. + */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + } + /* - * When in atomic context, reschedule and let rt2x00lib - * call this function again. + * SEC_CSR2 and SEC_CSR3 contain only single-bit fields to indicate + * a particular key is valid. Because using the FIELD32() + * defines directly will cause a lot of overhead we use + * a calculation to determine the correct bit directly. */ - if (in_atomic()) { - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->config_work); - return; - } + if (key->hw_key_idx < 32) { + mask = 1 << key->hw_key_idx; - rt73usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, ack_timeout); - rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg); + rt2x00usb_register_read(rt2x00dev, SEC_CSR2, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00usb_register_write(rt2x00dev, SEC_CSR2, reg); + } else { + mask = 1 << (key->hw_key_idx - 32); + + rt2x00usb_register_read(rt2x00dev, SEC_CSR3, ®); + if (crypto->cmd == SET_KEY) + reg |= mask; + else if (crypto->cmd == DISABLE_KEY) + reg &= ~mask; + rt2x00usb_register_write(rt2x00dev, SEC_CSR3, reg); + } - rt73usb_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, - !!short_preamble); - rt73usb_register_write(rt2x00dev, TXRX_CSR4, reg); + return 0; } -static void rt73usb_config_phymode(struct rt2x00_dev *rt2x00dev, - const int basic_rate_mask) +static void rt73usb_config_filter(struct rt2x00_dev *rt2x00dev, + const unsigned int filter_flags) { - rt73usb_register_write(rt2x00dev, TXRX_CSR5, basic_rate_mask); + u32 reg; + + /* + * Start configuration steps. + * Note that the version error will always be dropped + * and broadcast frames will always be accepted since + * there is no filter for it at this time. + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, + !(filter_flags & FIF_FCSFAIL)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, + !(filter_flags & FIF_PLCPFAIL)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, + !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); + rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, + !(filter_flags & FIF_PROMISC_IN_BSS)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, + !(filter_flags & FIF_PROMISC_IN_BSS) && + !rt2x00dev->intf_ap_count); + rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, + !(filter_flags & FIF_ALLMULTI)); + rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, + !(filter_flags & FIF_CONTROL)); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); } -static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev, - struct rf_channel *rf, const int txpower) +static void rt73usb_config_intf(struct rt2x00_dev *rt2x00dev, + struct rt2x00_intf *intf, + struct rt2x00intf_conf *conf, + const unsigned int flags) { - u8 r3; - u8 r94; - u8 smart; + u32 reg; - rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); - rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + if (flags & CONFIG_UPDATE_TYPE) { + /* + * Enable synchronisation. + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, conf->sync); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + } - smart = !(rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF2527)); + if (flags & CONFIG_UPDATE_MAC) { + reg = le32_to_cpu(conf->mac[1]); + rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); + conf->mac[1] = cpu_to_le32(reg); - rt73usb_bbp_read(rt2x00dev, 3, &r3); - rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); - rt73usb_bbp_write(rt2x00dev, 3, r3); + rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR2, + conf->mac, sizeof(conf->mac)); + } - r94 = 6; - if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) - r94 += txpower - MAX_TXPOWER; - else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) - r94 += txpower; - rt73usb_bbp_write(rt2x00dev, 94, r94); + if (flags & CONFIG_UPDATE_BSSID) { + reg = le32_to_cpu(conf->bssid[1]); + rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); + conf->bssid[1] = cpu_to_le32(reg); - rt73usb_rf_write(rt2x00dev, 1, rf->rf1); - rt73usb_rf_write(rt2x00dev, 2, rf->rf2); - rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt73usb_rf_write(rt2x00dev, 4, rf->rf4); + rt2x00usb_register_multiwrite(rt2x00dev, MAC_CSR4, + conf->bssid, sizeof(conf->bssid)); + } +} - rt73usb_rf_write(rt2x00dev, 1, rf->rf1); - rt73usb_rf_write(rt2x00dev, 2, rf->rf2); - rt73usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); - rt73usb_rf_write(rt2x00dev, 4, rf->rf4); +static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_erp *erp, + u32 changed) +{ + u32 reg; - rt73usb_rf_write(rt2x00dev, 1, rf->rf1); - rt73usb_rf_write(rt2x00dev, 2, rf->rf2); - rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); - rt73usb_rf_write(rt2x00dev, 4, rf->rf4); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, 0x32); + rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, + !!erp->short_preamble); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg); + } - udelay(10); -} + if (changed & BSS_CHANGED_BASIC_RATES) + rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, + erp->basic_rates); -static void rt73usb_config_txpower(struct rt2x00_dev *rt2x00dev, - const int txpower) -{ - struct rf_channel rf; + if (changed & BSS_CHANGED_BEACON_INT) { + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, + erp->beacon_int * 16); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + } - rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); - rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); - rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); - rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); + if (changed & BSS_CHANGED_ERP_SLOT) { + rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time); + rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg); - rt73usb_config_channel(rt2x00dev, &rf, txpower); + rt2x00usb_register_read(rt2x00dev, MAC_CSR8, ®); + rt2x00_set_field32(®, MAC_CSR8_SIFS, erp->sifs); + rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); + rt2x00_set_field32(®, MAC_CSR8_EIFS, erp->eifs); + rt2x00usb_register_write(rt2x00dev, MAC_CSR8, reg); + } } static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev, @@ -441,29 +592,23 @@ static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev, switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); - temp = !test_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags) - && (rt2x00dev->curr_hwmode != HWMODE_A); + temp = !rt2x00_has_cap_frame_type(rt2x00dev) && + (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp); break; case ANTENNA_A: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); - if (rt2x00dev->curr_hwmode == HWMODE_A) + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); else rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); break; - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ case ANTENNA_B: + default: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 0); - if (rt2x00dev->curr_hwmode == HWMODE_A) + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); else rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); @@ -488,7 +633,7 @@ static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - !test_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags)); + !rt2x00_has_cap_frame_type(rt2x00dev)); /* * Configure the RX antenna. @@ -501,14 +646,8 @@ static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 3); rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); break; - case ANTENNA_SW_DIVERSITY: - /* - * NOTE: We should never come here because rt2x00lib is - * supposed to catch this and send us the correct antenna - * explicitely. However we are nog going to bug about this. - * Instead, just default to antenna B. - */ case ANTENNA_B: + default: rt2x00_set_field8(&r77, BBP_R77_RX_ANTENNA, 0); rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 1); break; @@ -550,149 +689,192 @@ static const struct antenna_sel antenna_sel_bg[] = { { 98, { 0x48, 0x48 } }, }; -static void rt73usb_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) +static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev, + struct antenna_setup *ant) { const struct antenna_sel *sel; unsigned int lna; unsigned int i; u32 reg; - if (rt2x00dev->curr_hwmode == HWMODE_A) { + /* + * We should never come here because rt2x00lib is supposed + * to catch this and send us the correct antenna explicitely. + */ + BUG_ON(ant->rx == ANTENNA_SW_DIVERSITY || + ant->tx == ANTENNA_SW_DIVERSITY); + + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { sel = antenna_sel_a; - lna = test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags); + lna = rt2x00_has_cap_external_lna_a(rt2x00dev); } else { sel = antenna_sel_bg; - lna = test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags); + lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); } for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) rt73usb_bbp_write(rt2x00dev, sel[i].word, sel[i].value[lna]); - rt73usb_register_read(rt2x00dev, PHY_CSR0, ®); + rt2x00usb_register_read(rt2x00dev, PHY_CSR0, ®); rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, - (rt2x00dev->curr_hwmode == HWMODE_B || - rt2x00dev->curr_hwmode == HWMODE_G)); + (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ)); rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, - (rt2x00dev->curr_hwmode == HWMODE_A)); + (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ)); - rt73usb_register_write(rt2x00dev, PHY_CSR0, reg); + rt2x00usb_register_write(rt2x00dev, PHY_CSR0, reg); - if (rt2x00_rf(&rt2x00dev->chip, RF5226) || - rt2x00_rf(&rt2x00dev->chip, RF5225)) + if (rt2x00_rf(rt2x00dev, RF5226) || rt2x00_rf(rt2x00dev, RF5225)) rt73usb_config_antenna_5x(rt2x00dev, ant); - else if (rt2x00_rf(&rt2x00dev->chip, RF2528) || - rt2x00_rf(&rt2x00dev->chip, RF2527)) + else if (rt2x00_rf(rt2x00dev, RF2528) || rt2x00_rf(rt2x00dev, RF2527)) rt73usb_config_antenna_2x(rt2x00dev, ant); } -static void rt73usb_config_duration(struct rt2x00_dev *rt2x00dev, +static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_conf *libconf) { - u32 reg; - - rt73usb_register_read(rt2x00dev, MAC_CSR9, ®); - rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, libconf->slot_time); - rt73usb_register_write(rt2x00dev, MAC_CSR9, reg); - - rt73usb_register_read(rt2x00dev, MAC_CSR8, ®); - rt2x00_set_field32(®, MAC_CSR8_SIFS, libconf->sifs); - rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); - rt2x00_set_field32(®, MAC_CSR8_EIFS, libconf->eifs); - rt73usb_register_write(rt2x00dev, MAC_CSR8, reg); + u16 eeprom; + short lna_gain = 0; - rt73usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); - rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg); + if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) + lna_gain += 14; - rt73usb_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); - rt73usb_register_write(rt2x00dev, TXRX_CSR4, reg); + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); + } else { + rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); + lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); + } - rt73usb_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, - libconf->conf->beacon_int * 16); - rt73usb_register_write(rt2x00dev, TXRX_CSR9, reg); + rt2x00dev->lna_gain = lna_gain; } -static void rt73usb_config(struct rt2x00_dev *rt2x00dev, - const unsigned int flags, - struct rt2x00lib_conf *libconf) +static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev, + struct rf_channel *rf, const int txpower) { - if (flags & CONFIG_UPDATE_PHYMODE) - rt73usb_config_phymode(rt2x00dev, libconf->basic_rates); - if (flags & CONFIG_UPDATE_CHANNEL) - rt73usb_config_channel(rt2x00dev, &libconf->rf, - libconf->conf->power_level); - if ((flags & CONFIG_UPDATE_TXPOWER) && !(flags & CONFIG_UPDATE_CHANNEL)) - rt73usb_config_txpower(rt2x00dev, libconf->conf->power_level); - if (flags & CONFIG_UPDATE_ANTENNA) - rt73usb_config_antenna(rt2x00dev, &libconf->ant); - if (flags & (CONFIG_UPDATE_SLOT_TIME | CONFIG_UPDATE_BEACON_INT)) - rt73usb_config_duration(rt2x00dev, libconf); + u8 r3; + u8 r94; + u8 smart; + + rt2x00_set_field32(&rf->rf3, RF3_TXPOWER, TXPOWER_TO_DEV(txpower)); + rt2x00_set_field32(&rf->rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + smart = !(rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)); + + rt73usb_bbp_read(rt2x00dev, 3, &r3); + rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, smart); + rt73usb_bbp_write(rt2x00dev, 3, r3); + + r94 = 6; + if (txpower > MAX_TXPOWER && txpower <= (MAX_TXPOWER + r94)) + r94 += txpower - MAX_TXPOWER; + else if (txpower < MIN_TXPOWER && txpower >= (MIN_TXPOWER - r94)) + r94 += txpower; + rt73usb_bbp_write(rt2x00dev, 94, r94); + + rt73usb_rf_write(rt2x00dev, 1, rf->rf1); + rt73usb_rf_write(rt2x00dev, 2, rf->rf2); + rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt73usb_rf_write(rt2x00dev, 4, rf->rf4); + + rt73usb_rf_write(rt2x00dev, 1, rf->rf1); + rt73usb_rf_write(rt2x00dev, 2, rf->rf2); + rt73usb_rf_write(rt2x00dev, 3, rf->rf3 | 0x00000004); + rt73usb_rf_write(rt2x00dev, 4, rf->rf4); + + rt73usb_rf_write(rt2x00dev, 1, rf->rf1); + rt73usb_rf_write(rt2x00dev, 2, rf->rf2); + rt73usb_rf_write(rt2x00dev, 3, rf->rf3 & ~0x00000004); + rt73usb_rf_write(rt2x00dev, 4, rf->rf4); + + udelay(10); } -/* - * LED functions. - */ -static void rt73usb_enable_led(struct rt2x00_dev *rt2x00dev) +static void rt73usb_config_txpower(struct rt2x00_dev *rt2x00dev, + const int txpower) { - u32 reg; - - rt73usb_register_read(rt2x00dev, MAC_CSR14, ®); - rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, 70); - rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, 30); - rt73usb_register_write(rt2x00dev, MAC_CSR14, reg); + struct rf_channel rf; - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_RADIO_STATUS, 1); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LINK_A_STATUS, - (rt2x00dev->rx_status.phymode == MODE_IEEE80211A)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LINK_BG_STATUS, - (rt2x00dev->rx_status.phymode != MODE_IEEE80211A)); + rt2x00_rf_read(rt2x00dev, 1, &rf.rf1); + rt2x00_rf_read(rt2x00dev, 2, &rf.rf2); + rt2x00_rf_read(rt2x00dev, 3, &rf.rf3); + rt2x00_rf_read(rt2x00dev, 4, &rf.rf4); - rt2x00usb_vendor_request_sw(rt2x00dev, USB_LED_CONTROL, 0x0000, - rt2x00dev->led_reg, REGISTER_TIMEOUT); + rt73usb_config_channel(rt2x00dev, &rf, txpower); } -static void rt73usb_disable_led(struct rt2x00_dev *rt2x00dev) +static void rt73usb_config_retry_limit(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) { - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_RADIO_STATUS, 0); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LINK_BG_STATUS, 0); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LINK_A_STATUS, 0); + u32 reg; - rt2x00usb_vendor_request_sw(rt2x00dev, USB_LED_CONTROL, 0x0000, - rt2x00dev->led_reg, REGISTER_TIMEOUT); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_DOWN, 1); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_RATE_STEP, 0); + rt2x00_set_field32(®, TXRX_CSR4_OFDM_TX_FALLBACK_CCK, 0); + rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, + libconf->conf->long_frame_max_tx_count); + rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, + libconf->conf->short_frame_max_tx_count); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg); } -static void rt73usb_activity_led(struct rt2x00_dev *rt2x00dev, int rssi) +static void rt73usb_config_ps(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf) { - u32 led; + enum dev_state state = + (libconf->conf->flags & IEEE80211_CONF_PS) ? + STATE_SLEEP : STATE_AWAKE; + u32 reg; - if (rt2x00dev->led_mode != LED_MODE_SIGNAL_STRENGTH) - return; + if (state == STATE_SLEEP) { + rt2x00usb_register_read(rt2x00dev, MAC_CSR11, ®); + rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, + rt2x00dev->beacon_int - 10); + rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, + libconf->conf->listen_interval - 1); + rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 5); - /* - * Led handling requires a positive value for the rssi, - * to do that correctly we need to add the correction. - */ - rssi += rt2x00dev->rssi_offset; + /* We must first disable autowake before it can be enabled */ + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); + rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); - if (rssi <= 30) - led = 0; - else if (rssi <= 39) - led = 1; - else if (rssi <= 49) - led = 2; - else if (rssi <= 53) - led = 3; - else if (rssi <= 63) - led = 4; - else - led = 5; + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 1); + rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); - rt2x00usb_vendor_request_sw(rt2x00dev, USB_LED_CONTROL, led, - rt2x00dev->led_reg, REGISTER_TIMEOUT); + rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, + USB_MODE_SLEEP, REGISTER_TIMEOUT); + } else { + rt2x00usb_register_read(rt2x00dev, MAC_CSR11, ®); + rt2x00_set_field32(®, MAC_CSR11_DELAY_AFTER_TBCN, 0); + rt2x00_set_field32(®, MAC_CSR11_TBCN_BEFORE_WAKEUP, 0); + rt2x00_set_field32(®, MAC_CSR11_AUTOWAKE, 0); + rt2x00_set_field32(®, MAC_CSR11_WAKEUP_LATENCY, 0); + rt2x00usb_register_write(rt2x00dev, MAC_CSR11, reg); + + rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, 0, + USB_MODE_WAKEUP, REGISTER_TIMEOUT); + } +} + +static void rt73usb_config(struct rt2x00_dev *rt2x00dev, + struct rt2x00lib_conf *libconf, + const unsigned int flags) +{ + /* Always recalculate LNA gain before changing configuration */ + rt73usb_config_lna_gain(rt2x00dev, libconf); + + if (flags & IEEE80211_CONF_CHANGE_CHANNEL) + rt73usb_config_channel(rt2x00dev, &libconf->rf, + libconf->conf->power_level); + if ((flags & IEEE80211_CONF_CHANGE_POWER) && + !(flags & IEEE80211_CONF_CHANGE_CHANNEL)) + rt73usb_config_txpower(rt2x00dev, libconf->conf->power_level); + if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) + rt73usb_config_retry_limit(rt2x00dev, libconf); + if (flags & IEEE80211_CONF_CHANGE_PS) + rt73usb_config_ps(rt2x00dev, libconf); } /* @@ -706,52 +888,54 @@ static void rt73usb_link_stats(struct rt2x00_dev *rt2x00dev, /* * Update FCS error count from register. */ - rt73usb_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00usb_register_read(rt2x00dev, STA_CSR0, ®); qual->rx_failed = rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); /* * Update False CCA count from register. */ - rt73usb_register_read(rt2x00dev, STA_CSR1, ®); + rt2x00usb_register_read(rt2x00dev, STA_CSR1, ®); qual->false_cca = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); } -static void rt73usb_reset_tuner(struct rt2x00_dev *rt2x00dev) +static inline void rt73usb_set_vgc(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, u8 vgc_level) { - rt73usb_bbp_write(rt2x00dev, 17, 0x20); - rt2x00dev->link.vgc_level = 0x20; + if (qual->vgc_level != vgc_level) { + rt73usb_bbp_write(rt2x00dev, 17, vgc_level); + qual->vgc_level = vgc_level; + qual->vgc_level_reg = vgc_level; + } } -static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev) +static void rt73usb_reset_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual) +{ + rt73usb_set_vgc(rt2x00dev, qual, 0x20); +} + +static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev, + struct link_qual *qual, const u32 count) { - int rssi = rt2x00_get_link_rssi(&rt2x00dev->link); - u8 r17; u8 up_bound; u8 low_bound; /* - * Update Led strength - */ - rt73usb_activity_led(rt2x00dev, rssi); - - rt73usb_bbp_read(rt2x00dev, 17, &r17); - - /* * Determine r17 bounds. */ - if (rt2x00dev->rx_status.phymode == MODE_IEEE80211A) { + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { low_bound = 0x28; up_bound = 0x48; - if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } } else { - if (rssi > -82) { + if (qual->rssi > -82) { low_bound = 0x1c; up_bound = 0x40; - } else if (rssi > -84) { + } else if (qual->rssi > -84) { low_bound = 0x1c; up_bound = 0x20; } else { @@ -759,46 +943,48 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev) up_bound = 0x1c; } - if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { low_bound += 0x14; up_bound += 0x10; } } /* + * If we are not associated, we should go straight to the + * dynamic CCA tuning. + */ + if (!rt2x00dev->intf_associated) + goto dynamic_cca_tune; + + /* * Special big-R17 for very short distance */ - if (rssi > -35) { - if (r17 != 0x60) - rt73usb_bbp_write(rt2x00dev, 17, 0x60); + if (qual->rssi > -35) { + rt73usb_set_vgc(rt2x00dev, qual, 0x60); return; } /* * Special big-R17 for short distance */ - if (rssi >= -58) { - if (r17 != up_bound) - rt73usb_bbp_write(rt2x00dev, 17, up_bound); + if (qual->rssi >= -58) { + rt73usb_set_vgc(rt2x00dev, qual, up_bound); return; } /* * Special big-R17 for middle-short distance */ - if (rssi >= -66) { - low_bound += 0x10; - if (r17 != low_bound) - rt73usb_bbp_write(rt2x00dev, 17, low_bound); + if (qual->rssi >= -66) { + rt73usb_set_vgc(rt2x00dev, qual, low_bound + 0x10); return; } /* * Special mid-R17 for middle distance */ - if (rssi >= -74) { - if (r17 != (low_bound + 0x10)) - rt73usb_bbp_write(rt2x00dev, 17, low_bound + 0x08); + if (qual->rssi >= -74) { + rt73usb_set_vgc(rt2x00dev, qual, low_bound + 0x08); return; } @@ -806,125 +992,171 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev) * Special case: Change up_bound based on the rssi. * Lower up_bound when rssi is weaker then -74 dBm. */ - up_bound -= 2 * (-74 - rssi); + up_bound -= 2 * (-74 - qual->rssi); if (low_bound > up_bound) up_bound = low_bound; - if (r17 > up_bound) { - rt73usb_bbp_write(rt2x00dev, 17, up_bound); + if (qual->vgc_level > up_bound) { + rt73usb_set_vgc(rt2x00dev, qual, up_bound); return; } +dynamic_cca_tune: + /* * r17 does not yet exceed upper limit, continue and base * the r17 tuning on the false CCA count. */ - if (rt2x00dev->link.qual.false_cca > 512 && r17 < up_bound) { - r17 += 4; - if (r17 > up_bound) - r17 = up_bound; - rt73usb_bbp_write(rt2x00dev, 17, r17); - } else if (rt2x00dev->link.qual.false_cca < 100 && r17 > low_bound) { - r17 -= 4; - if (r17 < low_bound) - r17 = low_bound; - rt73usb_bbp_write(rt2x00dev, 17, r17); + if ((qual->false_cca > 512) && (qual->vgc_level < up_bound)) + rt73usb_set_vgc(rt2x00dev, qual, + min_t(u8, qual->vgc_level + 4, up_bound)); + else if ((qual->false_cca < 100) && (qual->vgc_level > low_bound)) + rt73usb_set_vgc(rt2x00dev, qual, + max_t(u8, qual->vgc_level - 4, low_bound)); +} + +/* + * Queue handlers. + */ +static void rt73usb_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); + break; + case QID_BEACON: + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + break; + default: + break; + } +} + +static void rt73usb_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); + break; + case QID_BEACON: + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + break; + default: + break; } } /* - * Firmware name function. + * Firmware functions */ static char *rt73usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) { return FIRMWARE_RT2571; } -/* - * Initialization functions. - */ -static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, void *data, - const size_t len) +static int rt73usb_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + u16 fw_crc; + u16 crc; + + /* + * Only support 2kb firmware files. + */ + if (len != 2048) + return FW_BAD_LENGTH; + + /* + * The last 2 bytes in the firmware array are the crc checksum itself, + * this means that we should never pass those 2 bytes to the crc + * algorithm. + */ + fw_crc = (data[len - 2] << 8 | data[len - 1]); + + /* + * Use the crc itu-t algorithm. + */ + crc = crc_itu_t(0, data, len - 2); + crc = crc_itu_t_byte(crc, 0); + crc = crc_itu_t_byte(crc, 0); + + return (fw_crc == crc) ? FW_OK : FW_BAD_CRC; +} + +static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) { unsigned int i; int status; u32 reg; - char *ptr = data; - char *cache; - int buflen; - int timeout; /* * Wait for stable hardware. */ for (i = 0; i < 100; i++) { - rt73usb_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); if (reg) break; msleep(1); } if (!reg) { - ERROR(rt2x00dev, "Unstable hardware.\n"); + rt2x00_err(rt2x00dev, "Unstable hardware\n"); return -EBUSY; } /* * Write firmware to device. - * We setup a seperate cache for this action, - * since we are going to write larger chunks of data - * then normally used cache size. */ - cache = kmalloc(CSR_CACHE_SIZE_FIRMWARE, GFP_KERNEL); - if (!cache) { - ERROR(rt2x00dev, "Failed to allocate firmware cache.\n"); - return -ENOMEM; - } - - for (i = 0; i < len; i += CSR_CACHE_SIZE_FIRMWARE) { - buflen = min_t(int, len - i, CSR_CACHE_SIZE_FIRMWARE); - timeout = REGISTER_TIMEOUT * (buflen / sizeof(u32)); - - memcpy(cache, ptr, buflen); - - rt2x00usb_vendor_request(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, - FIRMWARE_IMAGE_BASE + i, 0x0000, - cache, buflen, timeout); - - ptr += buflen; - } - - kfree(cache); + rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, data, len); /* * Send firmware request to device to load firmware, * we need to specify a long timeout time. */ status = rt2x00usb_vendor_request_sw(rt2x00dev, USB_DEVICE_MODE, - 0x0000, USB_MODE_FIRMWARE, + 0, USB_MODE_FIRMWARE, REGISTER_TIMEOUT_FIRMWARE); if (status < 0) { - ERROR(rt2x00dev, "Failed to write Firmware to device.\n"); + rt2x00_err(rt2x00dev, "Failed to write Firmware to device\n"); return status; } - rt73usb_disable_led(rt2x00dev); - return 0; } +/* + * Initialization functions. + */ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt73usb_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®); rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, 0); rt2x00_set_field32(®, TXRX_CSR0_TX_WITHOUT_WAITING, 0); - rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg); - rt73usb_register_read(rt2x00dev, TXRX_CSR1, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR1, ®); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0, 47); /* CCK Signal */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID1, 30); /* Rssi */ @@ -933,12 +1165,12 @@ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, TXRX_CSR1_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3, 30); /* Rssi */ rt2x00_set_field32(®, TXRX_CSR1_BBP_ID3_VALID, 1); - rt73usb_register_write(rt2x00dev, TXRX_CSR1, reg); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR1, reg); /* * CCK TXD BBP registers */ - rt73usb_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR2, ®); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0, 13); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID1, 12); @@ -947,127 +1179,143 @@ static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, TXRX_CSR2_BBP_ID2_VALID, 1); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3, 10); rt2x00_set_field32(®, TXRX_CSR2_BBP_ID3_VALID, 1); - rt73usb_register_write(rt2x00dev, TXRX_CSR2, reg); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR2, reg); /* * OFDM TXD BBP registers */ - rt73usb_register_read(rt2x00dev, TXRX_CSR3, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR3, ®); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0, 7); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID0_VALID, 1); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1, 6); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID1_VALID, 1); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2, 5); rt2x00_set_field32(®, TXRX_CSR3_BBP_ID2_VALID, 1); - rt73usb_register_write(rt2x00dev, TXRX_CSR3, reg); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR3, reg); - rt73usb_register_read(rt2x00dev, TXRX_CSR7, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR7, ®); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_6MBS, 59); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_9MBS, 53); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_12MBS, 49); rt2x00_set_field32(®, TXRX_CSR7_ACK_CTS_18MBS, 46); - rt73usb_register_write(rt2x00dev, TXRX_CSR7, reg); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR7, reg); - rt73usb_register_read(rt2x00dev, TXRX_CSR8, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR8, ®); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_24MBS, 44); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_36MBS, 42); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_48MBS, 42); rt2x00_set_field32(®, TXRX_CSR8_ACK_CTS_54MBS, 42); - rt73usb_register_write(rt2x00dev, TXRX_CSR8, reg); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR8, reg); - rt73usb_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 0); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00_set_field32(®, TXRX_CSR9_TIMESTAMP_COMPENSATE, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + + rt2x00usb_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); - rt73usb_register_read(rt2x00dev, MAC_CSR6, ®); + rt2x00usb_register_read(rt2x00dev, MAC_CSR6, ®); rt2x00_set_field32(®, MAC_CSR6_MAX_FRAME_UNIT, 0xfff); - rt73usb_register_write(rt2x00dev, MAC_CSR6, reg); + rt2x00usb_register_write(rt2x00dev, MAC_CSR6, reg); - rt73usb_register_write(rt2x00dev, MAC_CSR10, 0x00000718); + rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00000718); if (rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_AWAKE)) return -EBUSY; - rt73usb_register_write(rt2x00dev, MAC_CSR13, 0x00007f00); + rt2x00usb_register_write(rt2x00dev, MAC_CSR13, 0x00007f00); /* * Invalidate all Shared Keys (SEC_CSR0), * and clear the Shared key Cipher algorithms (SEC_CSR1 & SEC_CSR5) */ - rt73usb_register_write(rt2x00dev, SEC_CSR0, 0x00000000); - rt73usb_register_write(rt2x00dev, SEC_CSR1, 0x00000000); - rt73usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000); + rt2x00usb_register_write(rt2x00dev, SEC_CSR0, 0x00000000); + rt2x00usb_register_write(rt2x00dev, SEC_CSR1, 0x00000000); + rt2x00usb_register_write(rt2x00dev, SEC_CSR5, 0x00000000); reg = 0x000023b0; - if (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF2527)) + if (rt2x00_rf(rt2x00dev, RF5225) || rt2x00_rf(rt2x00dev, RF2527)) rt2x00_set_field32(®, PHY_CSR1_RF_RPI, 1); - rt73usb_register_write(rt2x00dev, PHY_CSR1, reg); - - rt73usb_register_write(rt2x00dev, PHY_CSR5, 0x00040a06); - rt73usb_register_write(rt2x00dev, PHY_CSR6, 0x00080606); - rt73usb_register_write(rt2x00dev, PHY_CSR7, 0x00000408); - - rt73usb_register_read(rt2x00dev, AC_TXOP_CSR0, ®); - rt2x00_set_field32(®, AC_TXOP_CSR0_AC0_TX_OP, 0); - rt2x00_set_field32(®, AC_TXOP_CSR0_AC1_TX_OP, 0); - rt73usb_register_write(rt2x00dev, AC_TXOP_CSR0, reg); + rt2x00usb_register_write(rt2x00dev, PHY_CSR1, reg); - rt73usb_register_read(rt2x00dev, AC_TXOP_CSR1, ®); - rt2x00_set_field32(®, AC_TXOP_CSR1_AC2_TX_OP, 192); - rt2x00_set_field32(®, AC_TXOP_CSR1_AC3_TX_OP, 48); - rt73usb_register_write(rt2x00dev, AC_TXOP_CSR1, reg); + rt2x00usb_register_write(rt2x00dev, PHY_CSR5, 0x00040a06); + rt2x00usb_register_write(rt2x00dev, PHY_CSR6, 0x00080606); + rt2x00usb_register_write(rt2x00dev, PHY_CSR7, 0x00000408); - rt73usb_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®); rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); - rt73usb_register_write(rt2x00dev, MAC_CSR9, reg); + rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg); + + /* + * Clear all beacons + * For the Beacon base registers we only need to clear + * the first byte since that byte contains the VALID and OWNER + * bits which (when set to 0) will invalidate the entire beacon. + */ + rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE0, 0); + rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE1, 0); + rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE2, 0); + rt2x00usb_register_write(rt2x00dev, HW_BEACON_BASE3, 0); /* * We must clear the error counters. * These registers are cleared on read, * so we may pass a useless variable to store the value. */ - rt73usb_register_read(rt2x00dev, STA_CSR0, ®); - rt73usb_register_read(rt2x00dev, STA_CSR1, ®); - rt73usb_register_read(rt2x00dev, STA_CSR2, ®); + rt2x00usb_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00usb_register_read(rt2x00dev, STA_CSR1, ®); + rt2x00usb_register_read(rt2x00dev, STA_CSR2, ®); /* * Reset MAC and BBP registers. */ - rt73usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); - rt73usb_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); - rt73usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); - rt73usb_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); - rt73usb_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00usb_register_read(rt2x00dev, MAC_CSR1, ®); rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); - rt73usb_register_write(rt2x00dev, MAC_CSR1, reg); + rt2x00usb_register_write(rt2x00dev, MAC_CSR1, reg); return 0; } -static int rt73usb_init_bbp(struct rt2x00_dev *rt2x00dev) +static int rt73usb_wait_bbp_ready(struct rt2x00_dev *rt2x00dev) { unsigned int i; - u16 eeprom; - u8 reg_id; u8 value; for (i = 0; i < REGISTER_BUSY_COUNT; i++) { rt73usb_bbp_read(rt2x00dev, 0, &value); if ((value != 0xff) && (value != 0x00)) - goto continue_csr_init; - NOTICE(rt2x00dev, "Waiting for BBP register.\n"); + return 0; udelay(REGISTER_BUSY_DELAY); } - ERROR(rt2x00dev, "BBP register access failed, aborting.\n"); + rt2x00_err(rt2x00dev, "BBP register access failed, aborting\n"); return -EACCES; +} + +static int rt73usb_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + if (unlikely(rt73usb_wait_bbp_ready(rt2x00dev))) + return -EACCES; -continue_csr_init: rt73usb_bbp_write(rt2x00dev, 3, 0x80); rt73usb_bbp_write(rt2x00dev, 15, 0x30); rt73usb_bbp_write(rt2x00dev, 21, 0xc8); @@ -1094,19 +1342,15 @@ continue_csr_init: rt73usb_bbp_write(rt2x00dev, 102, 0x16); rt73usb_bbp_write(rt2x00dev, 107, 0x04); - DEBUG(rt2x00dev, "Start initialization from EEPROM...\n"); for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); if (eeprom != 0xffff && eeprom != 0x0000) { reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); - DEBUG(rt2x00dev, "BBP: 0x%02x, value: 0x%02x.\n", - reg_id, value); rt73usb_bbp_write(rt2x00dev, reg_id, value); } } - DEBUG(rt2x00dev, "...End initialization from EEPROM.\n"); return 0; } @@ -1114,66 +1358,42 @@ continue_csr_init: /* * Device state switch handlers. */ -static void rt73usb_toggle_rx(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg; - - rt73usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, - state == STATE_RADIO_RX_OFF); - rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg); -} - static int rt73usb_enable_radio(struct rt2x00_dev *rt2x00dev) { /* * Initialize all registers. */ - if (rt73usb_init_registers(rt2x00dev) || - rt73usb_init_bbp(rt2x00dev)) { - ERROR(rt2x00dev, "Register initialization failed.\n"); + if (unlikely(rt73usb_init_registers(rt2x00dev) || + rt73usb_init_bbp(rt2x00dev))) return -EIO; - } - - /* - * Enable LED - */ - rt73usb_enable_led(rt2x00dev); return 0; } static void rt73usb_disable_radio(struct rt2x00_dev *rt2x00dev) { - /* - * Disable LED - */ - rt73usb_disable_led(rt2x00dev); - - rt73usb_register_write(rt2x00dev, MAC_CSR10, 0x00001818); + rt2x00usb_register_write(rt2x00dev, MAC_CSR10, 0x00001818); /* * Disable synchronisation. */ - rt73usb_register_write(rt2x00dev, TXRX_CSR9, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, 0); rt2x00usb_disable_radio(rt2x00dev); } static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - u32 reg; + u32 reg, reg2; unsigned int i; char put_to_sleep; - char current_state; put_to_sleep = (state != STATE_AWAKE); - rt73usb_register_read(rt2x00dev, MAC_CSR12, ®); + rt2x00usb_register_read(rt2x00dev, MAC_CSR12, ®); rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); - rt73usb_register_write(rt2x00dev, MAC_CSR12, reg); + rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg); /* * Device is not guaranteed to be in the requested state yet. @@ -1181,17 +1401,14 @@ static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) * device has entered the correct state. */ for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt73usb_register_read(rt2x00dev, MAC_CSR12, ®); - current_state = - rt2x00_get_field32(reg, MAC_CSR12_BBP_CURRENT_STATE); - if (current_state == !put_to_sleep) + rt2x00usb_register_read(rt2x00dev, MAC_CSR12, ®2); + state = rt2x00_get_field32(reg2, MAC_CSR12_BBP_CURRENT_STATE); + if (state == !put_to_sleep) return 0; + rt2x00usb_register_write(rt2x00dev, MAC_CSR12, reg); msleep(10); } - NOTICE(rt2x00dev, "Device failed to enter state %d, " - "current device state %d.\n", !put_to_sleep, current_state); - return -EBUSY; } @@ -1207,13 +1424,9 @@ static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev, case STATE_RADIO_OFF: rt73usb_disable_radio(rt2x00dev); break; - case STATE_RADIO_RX_ON: - case STATE_RADIO_RX_ON_LINK: - rt73usb_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); - break; - case STATE_RADIO_RX_OFF: - case STATE_RADIO_RX_OFF_LINK: - rt73usb_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + /* No support, but no error either */ break; case STATE_DEEP_SLEEP: case STATE_SLEEP: @@ -1226,107 +1439,199 @@ static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev, break; } + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + return retval; } /* * TX descriptor initialization */ -static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb, - struct txdata_entry_desc *desc, - struct ieee80211_tx_control *control) +static void rt73usb_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) { - struct skb_desc *skbdesc = get_skb_desc(skb); - __le32 *txd = skbdesc->desc; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + __le32 *txd = (__le32 *) entry->skb->data; u32 word; /* * Start writing the descriptor words. */ - rt2x00_desc_read(txd, 1, &word); - rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, desc->queue); - rt2x00_set_field32(&word, TXD_W1_AIFSN, desc->aifs); - rt2x00_set_field32(&word, TXD_W1_CWMIN, desc->cw_min); - rt2x00_set_field32(&word, TXD_W1_CWMAX, desc->cw_max); - rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, 1); - rt2x00_desc_write(txd, 1, word); - - rt2x00_desc_read(txd, 2, &word); - rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, desc->signal); - rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, desc->service); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, desc->length_low); - rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, desc->length_high); - rt2x00_desc_write(txd, 2, word); - - rt2x00_desc_read(txd, 5, &word); - rt2x00_set_field32(&word, TXD_W5_TX_POWER, - TXPOWER_TO_DEV(control->power_level)); - rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); - rt2x00_desc_write(txd, 5, word); - rt2x00_desc_read(txd, 0, &word); rt2x00_set_field32(&word, TXD_W0_BURST, - test_bit(ENTRY_TXD_BURST, &desc->flags)); + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_VALID, 1); rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, - test_bit(ENTRY_TXD_MORE_FRAG, &desc->flags)); + test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_ACK, - test_bit(ENTRY_TXD_ACK, &desc->flags)); + test_bit(ENTRY_TXD_ACK, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, - test_bit(ENTRY_TXD_REQ_TIMESTAMP, &desc->flags)); + test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W0_OFDM, - test_bit(ENTRY_TXD_OFDM_RATE, &desc->flags)); - rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + (txdesc->rate_mode == RATE_MODE_OFDM)); + rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->u.plcp.ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, - !!(control->flags & - IEEE80211_TXCTL_LONG_RETRY_LIMIT)); - rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, 0); - rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skbdesc->data_len); + test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, + test_bit(ENTRY_TXD_ENCRYPT_MMIC, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_TABLE, + test_bit(ENTRY_TXD_ENCRYPT_PAIRWISE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_KEY_INDEX, txdesc->key_idx); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, txdesc->length); rt2x00_set_field32(&word, TXD_W0_BURST2, - test_bit(ENTRY_TXD_BURST, &desc->flags)); - rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, txdesc->cipher); rt2x00_desc_write(txd, 0, word); -} -static int rt73usb_get_tx_data_len(struct rt2x00_dev *rt2x00dev, - struct sk_buff *skb) -{ - int length; + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, entry->queue->qid); + rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->queue->aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->queue->cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->queue->cw_max); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, txdesc->iv_offset); + rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, txdesc->u.plcp.signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, txdesc->u.plcp.service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, + txdesc->u.plcp.length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, + txdesc->u.plcp.length_high); + rt2x00_desc_write(txd, 2, word); + + if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags)) { + _rt2x00_desc_write(txd, 3, skbdesc->iv[0]); + _rt2x00_desc_write(txd, 4, skbdesc->iv[1]); + } + + rt2x00_desc_read(txd, 5, &word); + rt2x00_set_field32(&word, TXD_W5_TX_POWER, + TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power)); + rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); + rt2x00_desc_write(txd, 5, word); /* - * The length _must_ be a multiple of 4, - * but it must _not_ be a multiple of the USB packet size. + * Register descriptor details in skb frame descriptor. */ - length = roundup(skb->len, 4); - length += (4 * !(length % rt2x00dev->usb_maxpacket)); - - return length; + skbdesc->flags |= SKBDESC_DESC_IN_SKB; + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; } /* * TX data initialization */ -static void rt73usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, - unsigned int queue) +static void rt73usb_write_beacon(struct queue_entry *entry, + struct txentry_desc *txdesc) { - u32 reg; + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + unsigned int beacon_base; + unsigned int padding_len; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + orig_reg = reg; + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); - if (queue != IEEE80211_TX_QUEUE_BEACON) + /* + * Add space for the descriptor in front of the skb. + */ + skb_push(entry->skb, TXD_DESC_SIZE); + memset(entry->skb->data, 0, TXD_DESC_SIZE); + + /* + * Write the TX descriptor for the beacon. + */ + rt73usb_write_tx_desc(entry, txdesc); + + /* + * Dump beacon to userspace through debugfs. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_BEACON, entry->skb); + + /* + * Write entire beacon with descriptor and padding to register. + */ + padding_len = roundup(entry->skb->len, 4) - entry->skb->len; + if (padding_len && skb_pad(entry->skb, padding_len)) { + rt2x00_err(rt2x00dev, "Failure padding beacon, aborting\n"); + /* skb freed by skb_pad() on failure */ + entry->skb = NULL; + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg); return; + } + + beacon_base = HW_BEACON_OFFSET(entry->entry_idx); + rt2x00usb_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data, + entry->skb->len + padding_len); /* + * Enable beaconing again. + * * For Wi-Fi faily generated beacons between participating stations. * Set TBTT phase adaptive adjustment step to 8us (default 16us) */ - rt73usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR10, 0x00001008); - rt73usb_register_read(rt2x00dev, TXRX_CSR9, ®); - if (!rt2x00_get_field32(reg, TXRX_CSR9_BEACON_GEN)) { - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); - rt73usb_register_write(rt2x00dev, TXRX_CSR9, reg); - } + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Clean up the beacon skb. + */ + dev_kfree_skb(entry->skb); + entry->skb = NULL; +} + +static void rt73usb_clear_beacon(struct queue_entry *entry) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + unsigned int beacon_base; + u32 orig_reg, reg; + + /* + * Disable beaconing while we are reloading the beacon data, + * otherwise we might be sending out invalid data. + */ + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &orig_reg); + reg = orig_reg; + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Clear beacon. + */ + beacon_base = HW_BEACON_OFFSET(entry->entry_idx); + rt2x00usb_register_write(rt2x00dev, beacon_base, 0); + + /* + * Restore beaconing state. + */ + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg); +} + +static int rt73usb_get_tx_data_len(struct queue_entry *entry) +{ + int length; + + /* + * The length _must_ be a multiple of 4, + * but it must _not_ be a multiple of the USB packet size. + */ + length = roundup(entry->skb->len, 4); + length += (4 * !(length % entry->queue->usb_maxpacket)); + + return length; } /* @@ -1334,27 +1639,26 @@ static void rt73usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, */ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) { - u16 eeprom; - u8 offset; + u8 offset = rt2x00dev->lna_gain; u8 lna; lna = rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_LNA); switch (lna) { case 3: - offset = 90; + offset += 90; break; case 2: - offset = 74; + offset += 74; break; case 1: - offset = 64; + offset += 64; break; default: return 0; } - if (rt2x00dev->rx_status.phymode == MODE_IEEE80211A) { - if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) { + if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { if (lna == 3 || lna == 2) offset += 10; } else { @@ -1363,51 +1667,88 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) else if (lna == 2) offset += 8; } - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); - offset -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_A_1); - } else { - if (test_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags)) - offset += 14; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); - offset -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); } return rt2x00_get_field32(rxd_w1, RXD_W1_RSSI_AGC) * 2 - offset; } -static void rt73usb_fill_rxdone(struct data_entry *entry, - struct rxdata_entry_desc *desc) +static void rt73usb_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) { - struct skb_desc *skbdesc = get_skb_desc(entry->skb); + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); __le32 *rxd = (__le32 *)entry->skb->data; u32 word0; u32 word1; + /* + * Copy descriptor to the skbdesc->desc buffer, making it safe from moving of + * frame data in rt2x00usb. + */ + memcpy(skbdesc->desc, rxd, skbdesc->desc_len); + rxd = (__le32 *)skbdesc->desc; + + /* + * It is now safe to read the descriptor on all architectures. + */ rt2x00_desc_read(rxd, 0, &word0); rt2x00_desc_read(rxd, 1, &word1); - desc->flags = 0; if (rt2x00_get_field32(word0, RXD_W0_CRC_ERROR)) - desc->flags |= RX_FLAG_FAILED_FCS_CRC; + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + rxdesc->cipher = rt2x00_get_field32(word0, RXD_W0_CIPHER_ALG); + rxdesc->cipher_status = rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR); + + if (rxdesc->cipher != CIPHER_NONE) { + _rt2x00_desc_read(rxd, 2, &rxdesc->iv[0]); + _rt2x00_desc_read(rxd, 3, &rxdesc->iv[1]); + rxdesc->dev_flags |= RXDONE_CRYPTO_IV; + + _rt2x00_desc_read(rxd, 4, &rxdesc->icv); + rxdesc->dev_flags |= RXDONE_CRYPTO_ICV; + + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. It has provided the data separately but rt2x00lib + * should decide if it should be reinserted. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } /* * Obtain the status about this packet. + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 100kbit/s. */ - desc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); - desc->rssi = rt73usb_agc_to_rssi(entry->ring->rt2x00dev, word1); - desc->ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); - desc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); - desc->my_bss = !!rt2x00_get_field32(word0, RXD_W0_MY_BSS); + rxdesc->signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rxdesc->rssi = rt73usb_agc_to_rssi(rt2x00dev, word1); + rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + if (rt2x00_get_field32(word0, RXD_W0_OFDM)) + rxdesc->dev_flags |= RXDONE_SIGNAL_PLCP; + else + rxdesc->dev_flags |= RXDONE_SIGNAL_BITRATE; + if (rt2x00_get_field32(word0, RXD_W0_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; /* - * Set descriptor and data pointer. + * Set skb pointers, and update frame information. */ - skbdesc->desc = entry->skb->data; - skbdesc->desc_len = entry->ring->desc_size; - skbdesc->data = entry->skb->data + entry->ring->desc_size; - skbdesc->data_len = desc->size; + skb_pull(entry->skb, entry->queue->desc_size); + skb_trim(entry->skb, rxdesc->size); } /* @@ -1426,10 +1767,8 @@ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { - DECLARE_MAC_BUF(macbuf); - - random_ether_addr(mac); - EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac)); + eth_random_addr(mac); + rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); @@ -1444,14 +1783,14 @@ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_ANTENNA_HARDWARE_RADIO, 0); rt2x00_set_field16(&word, EEPROM_ANTENNA_RF_TYPE, RF5226); rt2x00_eeprom_write(rt2x00dev, EEPROM_ANTENNA, word); - EEPROM(rt2x00dev, "Antenna: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &word); if (word == 0xffff) { rt2x00_set_field16(&word, EEPROM_NIC_EXTERNAL_LNA, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC, word); - EEPROM(rt2x00dev, "NIC: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &word); @@ -1467,7 +1806,7 @@ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_LED_LED_MODE, LED_MODE_DEFAULT); rt2x00_eeprom_write(rt2x00dev, EEPROM_LED, word); - EEPROM(rt2x00dev, "Led: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Led: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word); @@ -1475,7 +1814,7 @@ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0); rt2x00_set_field16(&word, EEPROM_FREQ_SEQ, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word); - EEPROM(rt2x00dev, "Freq: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &word); @@ -1483,7 +1822,7 @@ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_1, 0); rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_BG_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_BG, word); - EEPROM(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); } else { value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_BG_1); if (value < -10 || value > 10) @@ -1499,7 +1838,7 @@ static int rt73usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_1, 0); rt2x00_set_field16(&word, EEPROM_RSSI_OFFSET_A_2, 0); rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_OFFSET_A, word); - EEPROM(rt2x00dev, "RSSI OFFSET BG: 0x%04x\n", word); + rt2x00_eeprom_dbg(rt2x00dev, "RSSI OFFSET A: 0x%04x\n", word); } else { value = rt2x00_get_field16(word, EEPROM_RSSI_OFFSET_A_1); if (value < -10 || value > 10) @@ -1528,19 +1867,20 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) * Identify RF chipset. */ value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); - rt73usb_register_read(rt2x00dev, MAC_CSR0, ®); - rt2x00_set_chip(rt2x00dev, RT2571, value, reg); + rt2x00usb_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00_set_chip(rt2x00dev, rt2x00_get_field32(reg, MAC_CSR0_CHIPSET), + value, rt2x00_get_field32(reg, MAC_CSR0_REVISION)); - if (!rt2x00_check_rev(&rt2x00dev->chip, 0x25730)) { - ERROR(rt2x00dev, "Invalid RT chipset detected.\n"); + if (!rt2x00_rt(rt2x00dev, RT2573) || (rt2x00_rev(rt2x00dev) == 0)) { + rt2x00_err(rt2x00dev, "Invalid RT chipset detected\n"); return -ENODEV; } - if (!rt2x00_rf(&rt2x00dev->chip, RF5226) && - !rt2x00_rf(&rt2x00dev->chip, RF2528) && - !rt2x00_rf(&rt2x00dev->chip, RF5225) && - !rt2x00_rf(&rt2x00dev->chip, RF2527)) { - ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); + if (!rt2x00_rf(rt2x00dev, RF5226) && + !rt2x00_rf(rt2x00dev, RF2528) && + !rt2x00_rf(rt2x00dev, RF5225) && + !rt2x00_rf(rt2x00dev, RF2527)) { + rt2x00_err(rt2x00dev, "Invalid RF chipset detected\n"); return -ENODEV; } @@ -1556,7 +1896,13 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) * Read the Frame type. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) - __set_bit(CONFIG_FRAME_TYPE, &rt2x00dev->flags); + __set_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags); + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); /* * Read frequency offset. @@ -1570,40 +1916,47 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA)) { - __set_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags); - __set_bit(CONFIG_EXTERNAL_LNA_BG, &rt2x00dev->flags); + __set_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); } /* * Store led settings, for correct led behaviour. */ +#ifdef CONFIG_RT2X00_LIB_LEDS rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LED_MODE, - rt2x00dev->led_mode); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_0, + rt73usb_init_led(rt2x00dev, &rt2x00dev->led_radio, LED_TYPE_RADIO); + rt73usb_init_led(rt2x00dev, &rt2x00dev->led_assoc, LED_TYPE_ASSOC); + if (value == LED_MODE_SIGNAL_STRENGTH) + rt73usb_init_led(rt2x00dev, &rt2x00dev->led_qual, + LED_TYPE_QUALITY); + + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_LED_MODE, value); + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_0, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_0)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_1, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_1, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_1)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_2, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_2, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_2)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_3, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_3, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_3)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_4, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_GPIO_4, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_4)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_ACT, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_ACT, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_READY_BG, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_BG, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_G)); - rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_READY_A, + rt2x00_set_field16(&rt2x00dev->led_mcu_reg, MCU_LEDCS_POLARITY_READY_A, rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_A)); +#endif /* CONFIG_RT2X00_LIB_LEDS */ return 0; } @@ -1744,73 +2097,85 @@ static const struct rf_channel rf_vals_5225_2527[] = { }; -static void rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) +static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; - u8 *txpower; + struct channel_info *info; + char *tx_power; unsigned int i; /* * Initialize all hw fields. + * + * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING unless we are + * capable of sending the buffered frames out after the DTIM + * transmission using rt2x00lib_beacondone. This will send out + * multicast and broadcast traffic immediately instead of buffering it + * infinitly and thus dropping it after some time. */ rt2x00dev->hw->flags = - IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | - IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; - rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; - rt2x00dev->hw->max_signal = MAX_SIGNAL; - rt2x00dev->hw->max_rssi = MAX_RX_SSI; - rt2x00dev->hw->queues = 5; - - SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_usb(rt2x00dev)->dev); + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK; + + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0)); /* - * Convert tx_power array in eeprom. - */ - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); - - /* * Initialize hw_mode information. */ - spec->num_modes = 2; - spec->num_rates = 12; - spec->tx_power_a = NULL; - spec->tx_power_bg = txpower; - spec->tx_power_default = DEFAULT_TXPOWER; + spec->supported_bands = SUPPORT_BAND_2GHZ; + spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(&rt2x00dev->chip, RF2528)) { + if (rt2x00_rf(rt2x00dev, RF2528)) { spec->num_channels = ARRAY_SIZE(rf_vals_bg_2528); spec->channels = rf_vals_bg_2528; - } else if (rt2x00_rf(&rt2x00dev->chip, RF5226)) { + } else if (rt2x00_rf(rt2x00dev, RF5226)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5226); spec->channels = rf_vals_5226; - } else if (rt2x00_rf(&rt2x00dev->chip, RF2527)) { + } else if (rt2x00_rf(rt2x00dev, RF2527)) { spec->num_channels = 14; spec->channels = rf_vals_5225_2527; - } else if (rt2x00_rf(&rt2x00dev->chip, RF5225)) { + } else if (rt2x00_rf(rt2x00dev, RF5225)) { + spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->num_channels = ARRAY_SIZE(rf_vals_5225_2527); spec->channels = rf_vals_5225_2527; } - if (rt2x00_rf(&rt2x00dev->chip, RF5225) || - rt2x00_rf(&rt2x00dev->chip, RF5226)) { - spec->num_modes = 3; + /* + * Create channel information array + */ + info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; - txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); - for (i = 0; i < 14; i++) - txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + spec->channels_info = info; - spec->tx_power_a = txpower; + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); + for (i = 0; i < 14; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]); } + + if (spec->num_channels > 14) { + tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); + for (i = 14; i < spec->num_channels; i++) { + info[i].max_power = MAX_TXPOWER; + info[i].default_power1 = + TXPOWER_FROM_DEV(tx_power[i - 14]); + } + } + + return 0; } static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; + u32 reg; /* * Allocate eeprom data. @@ -1824,14 +2189,34 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) return retval; /* + * Enable rfkill polling by setting GPIO direction of the + * rfkill switch GPIO pin correctly. + */ + rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); + rt2x00_set_field32(®, MAC_CSR13_DIR7, 0); + rt2x00usb_register_write(rt2x00dev, MAC_CSR13, reg); + + /* * Initialize hw specifications. */ - rt73usb_probe_hw_mode(rt2x00dev); + retval = rt73usb_probe_hw_mode(rt2x00dev); + if (retval) + return retval; + + /* + * This device has multiple filters for control frames, + * but has no a separate filter for PS Poll frames. + */ + __set_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags); /* - * This device requires firmware + * This device requires firmware. */ - __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); + __set_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags); + if (!modparam_nohwcrypt) + __set_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags); + __set_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags); + __set_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags); /* * Set the rssi offset. @@ -1844,175 +2229,77 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static void rt73usb_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) +static int rt73usb_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue_idx, + const struct ieee80211_tx_queue_params *params) { struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_queue *queue; + struct rt2x00_field32 field; + int retval; u32 reg; + u32 offset; /* - * Mask off any flags we are going to ignore from - * the total_flags field. + * First pass the configuration through rt2x00lib, that will + * update the queue settings and validate the input. After that + * we are free to update the registers based on the value + * in the queue parameter. */ - *total_flags &= - FIF_ALLMULTI | - FIF_FCSFAIL | - FIF_PLCPFAIL | - FIF_CONTROL | - FIF_OTHER_BSS | - FIF_PROMISC_IN_BSS; + retval = rt2x00mac_conf_tx(hw, vif, queue_idx, params); + if (retval) + return retval; /* - * Apply some rules to the filters: - * - Some filters imply different filters to be set. - * - Some things we can't filter out at all. - * - Multicast filter seems to kill broadcast traffic so never use it. + * We only need to perform additional register initialization + * for WMM queues/ */ - *total_flags |= FIF_ALLMULTI; - if (*total_flags & FIF_OTHER_BSS || - *total_flags & FIF_PROMISC_IN_BSS) - *total_flags |= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS; + if (queue_idx >= 4) + return 0; - /* - * Check if there is any work left for us. - */ - if (rt2x00dev->packet_filter == *total_flags) - return; - rt2x00dev->packet_filter = *total_flags; + queue = rt2x00queue_get_tx_queue(rt2x00dev, queue_idx); - /* - * When in atomic context, reschedule and let rt2x00lib - * call this function again. - */ - if (in_atomic()) { - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->filter_work); - return; - } + /* Update WMM TXOP register */ + offset = AC_TXOP_CSR0 + (sizeof(u32) * (!!(queue_idx & 2))); + field.bit_offset = (queue_idx & 1) * 16; + field.bit_mask = 0xffff << field.bit_offset; - /* - * Start configuration steps. - * Note that the version error will always be dropped - * and broadcast frames will always be accepted since - * there is no filter for it at this time. - */ - rt73usb_register_read(rt2x00dev, TXRX_CSR0, ®); - rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, - !(*total_flags & FIF_FCSFAIL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, - !(*total_flags & FIF_PLCPFAIL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, - !(*total_flags & FIF_CONTROL)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, - !(*total_flags & FIF_PROMISC_IN_BSS)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); - rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, - !(*total_flags & FIF_ALLMULTI)); - rt2x00_set_field32(®, TXRX_CSR0_DROP_BROADCAST, 0); - rt2x00_set_field32(®, TXRX_CSR0_DROP_ACK_CTS, 1); - rt73usb_register_write(rt2x00dev, TXRX_CSR0, reg); -} + rt2x00usb_register_read(rt2x00dev, offset, ®); + rt2x00_set_field32(®, field, queue->txop); + rt2x00usb_register_write(rt2x00dev, offset, reg); -static int rt73usb_set_retry_limit(struct ieee80211_hw *hw, - u32 short_retry, u32 long_retry) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - u32 reg; + /* Update WMM registers */ + field.bit_offset = queue_idx * 4; + field.bit_mask = 0xf << field.bit_offset; - rt73usb_register_read(rt2x00dev, TXRX_CSR4, ®); - rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, long_retry); - rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, short_retry); - rt73usb_register_write(rt2x00dev, TXRX_CSR4, reg); + rt2x00usb_register_read(rt2x00dev, AIFSN_CSR, ®); + rt2x00_set_field32(®, field, queue->aifs); + rt2x00usb_register_write(rt2x00dev, AIFSN_CSR, reg); + + rt2x00usb_register_read(rt2x00dev, CWMIN_CSR, ®); + rt2x00_set_field32(®, field, queue->cw_min); + rt2x00usb_register_write(rt2x00dev, CWMIN_CSR, reg); + + rt2x00usb_register_read(rt2x00dev, CWMAX_CSR, ®); + rt2x00_set_field32(®, field, queue->cw_max); + rt2x00usb_register_write(rt2x00dev, CWMAX_CSR, reg); return 0; } -#if 0 -/* - * Mac80211 demands get_tsf must be atomic. - * This is not possible for rt73usb since all register access - * functions require sleeping. Untill mac80211 no longer needs - * get_tsf to be atomic, this function should be disabled. - */ -static u64 rt73usb_get_tsf(struct ieee80211_hw *hw) +static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; u32 reg; - rt73usb_register_read(rt2x00dev, TXRX_CSR13, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR13, ®); tsf = (u64) rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; - rt73usb_register_read(rt2x00dev, TXRX_CSR12, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR12, ®); tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); return tsf; } -#else -#define rt73usb_get_tsf NULL -#endif - -static void rt73usb_reset_tsf(struct ieee80211_hw *hw) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - - rt73usb_register_write(rt2x00dev, TXRX_CSR12, 0); - rt73usb_register_write(rt2x00dev, TXRX_CSR13, 0); -} - -static int rt73usb_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ieee80211_tx_control *control) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct skb_desc *desc; - struct data_ring *ring; - struct data_entry *entry; - int timeout; - - /* - * Just in case the ieee80211 doesn't set this, - * but we need this queue set for the descriptor - * initialization. - */ - control->queue = IEEE80211_TX_QUEUE_BEACON; - ring = rt2x00lib_get_ring(rt2x00dev, control->queue); - entry = rt2x00_get_data_entry(ring); - - /* - * Add the descriptor in front of the skb. - */ - skb_push(skb, ring->desc_size); - memset(skb->data, 0, ring->desc_size); - - /* - * Fill in skb descriptor - */ - desc = get_skb_desc(skb); - desc->desc_len = ring->desc_size; - desc->data_len = skb->len - ring->desc_size; - desc->desc = skb->data; - desc->data = skb->data + ring->desc_size; - desc->ring = ring; - desc->entry = entry; - - rt2x00lib_write_tx_desc(rt2x00dev, skb, control); - - /* - * Write entire beacon with descriptor to register, - * and kick the beacon generator. - */ - timeout = REGISTER_TIMEOUT * (skb->len / sizeof(u32)); - rt2x00usb_vendor_request(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, - HW_BEACON_BASE0, 0x0000, - skb->data, skb->len, timeout); - rt73usb_kick_tx_queue(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); - - return 0; -} static const struct ieee80211_ops rt73usb_mac80211_ops = { .tx = rt2x00mac_tx, @@ -2021,52 +2308,101 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, - .configure_filter = rt73usb_configure_filter, + .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, .get_stats = rt2x00mac_get_stats, - .set_retry_limit = rt73usb_set_retry_limit, .bss_info_changed = rt2x00mac_bss_info_changed, - .conf_tx = rt2x00mac_conf_tx, - .get_tx_stats = rt2x00mac_get_tx_stats, + .conf_tx = rt73usb_conf_tx, .get_tsf = rt73usb_get_tsf, - .reset_tsf = rt73usb_reset_tsf, - .beacon_update = rt73usb_beacon_update, + .rfkill_poll = rt2x00mac_rfkill_poll, + .flush = rt2x00mac_flush, + .set_antenna = rt2x00mac_set_antenna, + .get_antenna = rt2x00mac_get_antenna, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, }; static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { .probe_hw = rt73usb_probe_hw, .get_firmware_name = rt73usb_get_firmware_name, + .check_firmware = rt73usb_check_firmware, .load_firmware = rt73usb_load_firmware, .initialize = rt2x00usb_initialize, .uninitialize = rt2x00usb_uninitialize, - .init_rxentry = rt2x00usb_init_rxentry, - .init_txentry = rt2x00usb_init_txentry, + .clear_entry = rt2x00usb_clear_entry, .set_device_state = rt73usb_set_device_state, + .rfkill_poll = rt73usb_rfkill_poll, .link_stats = rt73usb_link_stats, .reset_tuner = rt73usb_reset_tuner, .link_tuner = rt73usb_link_tuner, + .watchdog = rt2x00usb_watchdog, + .start_queue = rt73usb_start_queue, + .kick_queue = rt2x00usb_kick_queue, + .stop_queue = rt73usb_stop_queue, + .flush_queue = rt2x00usb_flush_queue, .write_tx_desc = rt73usb_write_tx_desc, - .write_tx_data = rt2x00usb_write_tx_data, + .write_beacon = rt73usb_write_beacon, + .clear_beacon = rt73usb_clear_beacon, .get_tx_data_len = rt73usb_get_tx_data_len, - .kick_tx_queue = rt73usb_kick_tx_queue, .fill_rxdone = rt73usb_fill_rxdone, - .config_mac_addr = rt73usb_config_mac_addr, - .config_bssid = rt73usb_config_bssid, - .config_type = rt73usb_config_type, - .config_preamble = rt73usb_config_preamble, + .config_shared_key = rt73usb_config_shared_key, + .config_pairwise_key = rt73usb_config_pairwise_key, + .config_filter = rt73usb_config_filter, + .config_intf = rt73usb_config_intf, + .config_erp = rt73usb_config_erp, + .config_ant = rt73usb_config_ant, .config = rt73usb_config, }; +static void rt73usb_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_BEACON: + queue->limit = 4; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXINFO_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} + static const struct rt2x00_ops rt73usb_ops = { - .name = KBUILD_MODNAME, - .rxd_size = RXD_DESC_SIZE, - .txd_size = TXD_DESC_SIZE, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .lib = &rt73usb_rt2x00_ops, - .hw = &rt73usb_mac80211_ops, + .name = KBUILD_MODNAME, + .max_ap_intf = 4, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt73usb_queue_init, + .lib = &rt73usb_rt2x00_ops, + .hw = &rt73usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt73usb_rt2x00debug, + .debugfs = &rt73usb_rt2x00debug, #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; @@ -2075,67 +2411,115 @@ static const struct rt2x00_ops rt73usb_ops = { */ static struct usb_device_id rt73usb_device_table[] = { /* AboCom */ - { USB_DEVICE(0x07b8, 0xb21d), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x07b8, 0xb21b) }, + { USB_DEVICE(0x07b8, 0xb21c) }, + { USB_DEVICE(0x07b8, 0xb21d) }, + { USB_DEVICE(0x07b8, 0xb21e) }, + { USB_DEVICE(0x07b8, 0xb21f) }, + /* AL */ + { USB_DEVICE(0x14b2, 0x3c10) }, + /* Amigo */ + { USB_DEVICE(0x148f, 0x9021) }, + { USB_DEVICE(0x0eb0, 0x9021) }, + /* AMIT */ + { USB_DEVICE(0x18c5, 0x0002) }, /* Askey */ - { USB_DEVICE(0x1690, 0x0722), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x1690, 0x0722) }, /* ASUS */ - { USB_DEVICE(0x0b05, 0x1723), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x0b05, 0x1724), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0b05, 0x1723) }, + { USB_DEVICE(0x0b05, 0x1724) }, /* Belkin */ - { USB_DEVICE(0x050d, 0x7050), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x050d, 0x705a), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x050d, 0x905b), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x050d, 0x905c), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x050d, 0x7050) }, /* FCC ID: K7SF5D7050B ver. 3.x */ + { USB_DEVICE(0x050d, 0x705a) }, + { USB_DEVICE(0x050d, 0x905b) }, + { USB_DEVICE(0x050d, 0x905c) }, /* Billionton */ - { USB_DEVICE(0x1631, 0xc019), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x1631, 0xc019) }, + { USB_DEVICE(0x08dd, 0x0120) }, /* Buffalo */ - { USB_DEVICE(0x0411, 0x00f4), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0411, 0x00d8) }, + { USB_DEVICE(0x0411, 0x00d9) }, + { USB_DEVICE(0x0411, 0x00e6) }, + { USB_DEVICE(0x0411, 0x00f4) }, + { USB_DEVICE(0x0411, 0x0116) }, + { USB_DEVICE(0x0411, 0x0119) }, + { USB_DEVICE(0x0411, 0x0137) }, + /* CEIVA */ + { USB_DEVICE(0x178d, 0x02be) }, /* CNet */ - { USB_DEVICE(0x1371, 0x9022), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x1371, 0x9032), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x1371, 0x9022) }, + { USB_DEVICE(0x1371, 0x9032) }, /* Conceptronic */ - { USB_DEVICE(0x14b2, 0x3c22), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x14b2, 0x3c22) }, /* Corega */ - { USB_DEVICE(0x07aa, 0x002e), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x07aa, 0x002e) }, /* D-Link */ - { USB_DEVICE(0x07d1, 0x3c03), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x07d1, 0x3c04), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x07d1, 0x3c07), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x07d1, 0x3c03) }, + { USB_DEVICE(0x07d1, 0x3c04) }, + { USB_DEVICE(0x07d1, 0x3c06) }, + { USB_DEVICE(0x07d1, 0x3c07) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x7318) }, + { USB_DEVICE(0x7392, 0x7618) }, + /* EnGenius */ + { USB_DEVICE(0x1740, 0x3701) }, /* Gemtek */ - { USB_DEVICE(0x15a9, 0x0004), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x15a9, 0x0004) }, /* Gigabyte */ - { USB_DEVICE(0x1044, 0x8008), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x1044, 0x800a), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x1044, 0x8008) }, + { USB_DEVICE(0x1044, 0x800a) }, /* Huawei-3Com */ - { USB_DEVICE(0x1472, 0x0009), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x1472, 0x0009) }, /* Hercules */ - { USB_DEVICE(0x06f8, 0xe010), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x06f8, 0xe020), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x06f8, 0xe002) }, + { USB_DEVICE(0x06f8, 0xe010) }, + { USB_DEVICE(0x06f8, 0xe020) }, /* Linksys */ - { USB_DEVICE(0x13b1, 0x0020), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x13b1, 0x0023), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x13b1, 0x0020) }, + { USB_DEVICE(0x13b1, 0x0023) }, + { USB_DEVICE(0x13b1, 0x0028) }, /* MSI */ - { USB_DEVICE(0x0db0, 0x6877), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x0db0, 0x6874), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x0db0, 0xa861), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x0db0, 0xa874), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0db0, 0x4600) }, + { USB_DEVICE(0x0db0, 0x6877) }, + { USB_DEVICE(0x0db0, 0x6874) }, + { USB_DEVICE(0x0db0, 0xa861) }, + { USB_DEVICE(0x0db0, 0xa874) }, + /* Ovislink */ + { USB_DEVICE(0x1b75, 0x7318) }, /* Ralink */ - { USB_DEVICE(0x148f, 0x2573), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x148f, 0x2671), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x04bb, 0x093d) }, + { USB_DEVICE(0x148f, 0x2573) }, + { USB_DEVICE(0x148f, 0x2671) }, + { USB_DEVICE(0x0812, 0x3101) }, /* Qcom */ - { USB_DEVICE(0x18e8, 0x6196), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x18e8, 0x6229), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x18e8, 0x6238), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x18e8, 0x6196) }, + { USB_DEVICE(0x18e8, 0x6229) }, + { USB_DEVICE(0x18e8, 0x6238) }, + /* Samsung */ + { USB_DEVICE(0x04e8, 0x4471) }, /* Senao */ - { USB_DEVICE(0x1740, 0x7100), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x1740, 0x7100) }, /* Sitecom */ - { USB_DEVICE(0x0df6, 0x9712), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x0df6, 0x90ac), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0df6, 0x0024) }, + { USB_DEVICE(0x0df6, 0x0027) }, + { USB_DEVICE(0x0df6, 0x002f) }, + { USB_DEVICE(0x0df6, 0x90ac) }, + { USB_DEVICE(0x0df6, 0x9712) }, /* Surecom */ - { USB_DEVICE(0x0769, 0x31f3), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0769, 0x31f3) }, + /* Tilgin */ + { USB_DEVICE(0x6933, 0x5001) }, + /* Philips */ + { USB_DEVICE(0x0471, 0x200a) }, /* Planex */ - { USB_DEVICE(0x2019, 0xab01), USB_DEVICE_DATA(&rt73usb_ops) }, - { USB_DEVICE(0x2019, 0xab50), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x2019, 0xab01) }, + { USB_DEVICE(0x2019, 0xab50) }, + /* WideTell */ + { USB_DEVICE(0x7167, 0x3840) }, + /* Zcom */ + { USB_DEVICE(0x0cde, 0x001c) }, + /* ZyXEL */ + { USB_DEVICE(0x0586, 0x3415) }, { 0, } }; @@ -2147,24 +2531,21 @@ MODULE_DEVICE_TABLE(usb, rt73usb_device_table); MODULE_FIRMWARE(FIRMWARE_RT2571); MODULE_LICENSE("GPL"); +static int rt73usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + return rt2x00usb_probe(usb_intf, &rt73usb_ops); +} + static struct usb_driver rt73usb_driver = { .name = KBUILD_MODNAME, .id_table = rt73usb_device_table, - .probe = rt2x00usb_probe, + .probe = rt73usb_probe, .disconnect = rt2x00usb_disconnect, .suspend = rt2x00usb_suspend, .resume = rt2x00usb_resume, + .reset_resume = rt2x00usb_resume, + .disable_hub_initiated_lpm = 1, }; -static int __init rt73usb_init(void) -{ - return usb_register(&rt73usb_driver); -} - -static void __exit rt73usb_exit(void) -{ - usb_deregister(&rt73usb_driver); -} - -module_init(rt73usb_init); -module_exit(rt73usb_exit); +module_usb_driver(rt73usb_driver); diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h index d49dcaacece..4a4f235466d 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.h +++ b/drivers/net/wireless/rt2x00/rt73usb.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> <http://rt2x00.serialmonkey.com> This program is free software; you can redistribute it and/or modify @@ -13,9 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the - Free Software Foundation, Inc., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -37,10 +35,8 @@ /* * Signal information. - * Defaul offset is required for RSSI <-> dBm conversion. + * Default offset is required for RSSI <-> dBm conversion. */ -#define MAX_SIGNAL 100 -#define MAX_RX_SSI -1 #define DEFAULT_RSSI_OFFSET 120 /* @@ -50,8 +46,15 @@ #define CSR_REG_SIZE 0x04b0 #define EEPROM_BASE 0x0000 #define EEPROM_SIZE 0x0100 +#define BBP_BASE 0x0000 #define BBP_SIZE 0x0080 -#define RF_SIZE 0x0014 +#define RF_BASE 0x0004 +#define RF_SIZE 0x0010 + +/* + * Number of TX queues. + */ +#define NUM_TX_QUEUES 4 /* * USB registers. @@ -89,16 +92,27 @@ #define PAIRWISE_KEY_TABLE_BASE 0x1200 #define PAIRWISE_TA_TABLE_BASE 0x1a00 +#define SHARED_KEY_ENTRY(__idx) \ + ( SHARED_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_KEY_ENTRY(__idx) \ + ( PAIRWISE_KEY_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_key_entry)) ) +#define PAIRWISE_TA_ENTRY(__idx) \ + ( PAIRWISE_TA_TABLE_BASE + \ + ((__idx) * sizeof(struct hw_pairwise_ta_entry)) ) + struct hw_key_entry { u8 key[16]; u8 tx_mic[8]; u8 rx_mic[8]; -} __attribute__ ((packed)); +} __packed; struct hw_pairwise_ta_entry { u8 address[6]; - u8 reserved[2]; -} __attribute__ ((packed)); + u8 cipher; + u8 reserved; +} __packed; /* * Since NULL frame won't be that long (256 byte), @@ -114,6 +128,9 @@ struct hw_pairwise_ta_entry { #define HW_BEACON_BASE2 0x2600 #define HW_BEACON_BASE3 0x2700 +#define HW_BEACON_OFFSET(__index) \ + ( HW_BEACON_BASE0 + (__index * 0x0100) ) + /* * MAC Control/Status Registers(CSR). * Some values are set in TU, whereas 1 TU == 1024 us. @@ -123,6 +140,8 @@ struct hw_pairwise_ta_entry { * MAC_CSR0: ASIC revision number. */ #define MAC_CSR0 0x3000 +#define MAC_CSR0_REVISION FIELD32(0x0000000f) +#define MAC_CSR0_CHIPSET FIELD32(0x000ffff0) /* * MAC_CSR1: System control register. @@ -146,6 +165,11 @@ struct hw_pairwise_ta_entry { /* * MAC_CSR3: STA MAC register 1. + * UNICAST_TO_ME_MASK: + * Used to mask off bits from byte 5 of the MAC address + * to determine the UNICAST_TO_ME bit for RX frames. + * The full mask is complemented by BSS_ID_MASK: + * MASK = BSS_ID_MASK & UNICAST_TO_ME_MASK */ #define MAC_CSR3 0x300c #define MAC_CSR3_BYTE4 FIELD32(0x000000ff) @@ -163,7 +187,14 @@ struct hw_pairwise_ta_entry { /* * MAC_CSR5: BSSID register 1. - * BSS_ID_MASK: 3: one BSSID, 0: 4 BSSID, 2 or 1: 2 BSSID. + * BSS_ID_MASK: + * This mask is used to mask off bits 0 and 1 of byte 5 of the + * BSSID. This will make sure that those bits will be ignored + * when determining the MY_BSS of RX frames. + * 0: 1-BSSID mode (BSS index = 0) + * 1: 2-BSSID mode (BSS index: Byte5, bit 0) + * 2: 2-BSSID mode (BSS index: byte5, bit 1) + * 3: 4-BSSID mode (BSS index: byte5, bit 0 - 1) */ #define MAC_CSR5 0x3014 #define MAC_CSR5_BYTE4 FIELD32(0x000000ff) @@ -234,8 +265,26 @@ struct hw_pairwise_ta_entry { /* * MAC_CSR13: GPIO. + * MAC_CSR13_VALx: GPIO value + * MAC_CSR13_DIRx: GPIO direction: 0 = input; 1 = output */ #define MAC_CSR13 0x3034 +#define MAC_CSR13_VAL0 FIELD32(0x00000001) +#define MAC_CSR13_VAL1 FIELD32(0x00000002) +#define MAC_CSR13_VAL2 FIELD32(0x00000004) +#define MAC_CSR13_VAL3 FIELD32(0x00000008) +#define MAC_CSR13_VAL4 FIELD32(0x00000010) +#define MAC_CSR13_VAL5 FIELD32(0x00000020) +#define MAC_CSR13_VAL6 FIELD32(0x00000040) +#define MAC_CSR13_VAL7 FIELD32(0x00000080) +#define MAC_CSR13_DIR0 FIELD32(0x00000100) +#define MAC_CSR13_DIR1 FIELD32(0x00000200) +#define MAC_CSR13_DIR2 FIELD32(0x00000400) +#define MAC_CSR13_DIR3 FIELD32(0x00000800) +#define MAC_CSR13_DIR4 FIELD32(0x00001000) +#define MAC_CSR13_DIR5 FIELD32(0x00002000) +#define MAC_CSR13_DIR6 FIELD32(0x00004000) +#define MAC_CSR13_DIR7 FIELD32(0x00008000) /* * MAC_CSR14: LED control register. @@ -276,7 +325,7 @@ struct hw_pairwise_ta_entry { * DROP_VERSION_ERROR: Drop version error frame. * DROP_MULTICAST: Drop multicast frames. * DROP_BORADCAST: Drop broadcast frames. - * ROP_ACK_CTS: Drop received ACK and CTS. + * DROP_ACK_CTS: Drop received ACK and CTS. */ #define TXRX_CSR0 0x3040 #define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) @@ -545,6 +594,10 @@ struct hw_pairwise_ta_entry { * SEC_CSR4: Pairwise key table lookup control. */ #define SEC_CSR4 0x30b0 +#define SEC_CSR4_ENABLE_BSS0 FIELD32(0x00000001) +#define SEC_CSR4_ENABLE_BSS1 FIELD32(0x00000002) +#define SEC_CSR4_ENABLE_BSS2 FIELD32(0x00000004) +#define SEC_CSR4_ENABLE_BSS3 FIELD32(0x00000008) /* * SEC_CSR5: shared key table security mode register. @@ -639,10 +692,10 @@ struct hw_pairwise_ta_entry { /* * AIFSN_CSR: AIFSN for each EDCA AC. - * AIFSN0: For AC_BK. - * AIFSN1: For AC_BE. - * AIFSN2: For AC_VI. - * AIFSN3: For AC_VO. + * AIFSN0: For AC_VO. + * AIFSN1: For AC_VI. + * AIFSN2: For AC_BE. + * AIFSN3: For AC_BK. */ #define AIFSN_CSR 0x0400 #define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) @@ -652,10 +705,10 @@ struct hw_pairwise_ta_entry { /* * CWMIN_CSR: CWmin for each EDCA AC. - * CWMIN0: For AC_BK. - * CWMIN1: For AC_BE. - * CWMIN2: For AC_VI. - * CWMIN3: For AC_VO. + * CWMIN0: For AC_VO. + * CWMIN1: For AC_VI. + * CWMIN2: For AC_BE. + * CWMIN3: For AC_BK. */ #define CWMIN_CSR 0x0404 #define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) @@ -665,10 +718,10 @@ struct hw_pairwise_ta_entry { /* * CWMAX_CSR: CWmax for each EDCA AC. - * CWMAX0: For AC_BK. - * CWMAX1: For AC_BE. - * CWMAX2: For AC_VI. - * CWMAX3: For AC_VO. + * CWMAX0: For AC_VO. + * CWMAX1: For AC_VI. + * CWMAX2: For AC_BE. + * CWMAX3: For AC_BK. */ #define CWMAX_CSR 0x0408 #define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) @@ -677,18 +730,18 @@ struct hw_pairwise_ta_entry { #define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) /* - * AC_TXOP_CSR0: AC_BK/AC_BE TXOP register. - * AC0_TX_OP: For AC_BK, in unit of 32us. - * AC1_TX_OP: For AC_BE, in unit of 32us. + * AC_TXOP_CSR0: AC_VO/AC_VI TXOP register. + * AC0_TX_OP: For AC_VO, in unit of 32us. + * AC1_TX_OP: For AC_VI, in unit of 32us. */ #define AC_TXOP_CSR0 0x040c #define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) #define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) /* - * AC_TXOP_CSR1: AC_VO/AC_VI TXOP register. - * AC2_TX_OP: For AC_VI, in unit of 32us. - * AC3_TX_OP: For AC_VO, in unit of 32us. + * AC_TXOP_CSR1: AC_BE/AC_BK TXOP register. + * AC2_TX_OP: For AC_BE, in unit of 32us. + * AC3_TX_OP: For AC_BK, in unit of 32us. */ #define AC_TXOP_CSR1 0x0410 #define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) @@ -761,7 +814,7 @@ struct hw_pairwise_ta_entry { /* * EEPROM antenna. - * ANTENNA_NUM: Number of antenna's. + * ANTENNA_NUM: Number of antennas. * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. @@ -867,6 +920,7 @@ struct hw_pairwise_ta_entry { * DMA descriptor defines. */ #define TXD_DESC_SIZE ( 6 * sizeof(__le32) ) +#define TXINFO_SIZE ( 6 * sizeof(__le32) ) #define RXD_DESC_SIZE ( 6 * sizeof(__le32) ) /* @@ -991,8 +1045,10 @@ struct hw_pairwise_ta_entry { /* * Word4 + * ICV: Received ICV of originally encrypted. + * NOTE: This is a guess, the official definition is "reserved" */ -#define RXD_W4_RESERVED FIELD32(0xffffffff) +#define RXD_W4_ICV FIELD32(0xffffffff) /* * the above 20-byte is called RXINFO and will be DMAed to MAC RX block @@ -1007,24 +1063,17 @@ struct hw_pairwise_ta_entry { #define RXD_W5_RESERVED FIELD32(0xffffffff) /* - * Macro's for converting txpower from EEPROM to dscape value - * and from dscape value to register value. + * Macros for converting txpower from EEPROM to mac80211 value + * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 #define MAX_TXPOWER 31 #define DEFAULT_TXPOWER 24 -#define TXPOWER_FROM_DEV(__txpower) \ -({ \ - ((__txpower) > MAX_TXPOWER) ? \ - DEFAULT_TXPOWER : (__txpower); \ -}) - -#define TXPOWER_TO_DEV(__txpower) \ -({ \ - ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ - (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ - (__txpower)); \ -}) +#define TXPOWER_FROM_DEV(__txpower) \ + (((u8)(__txpower)) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower) + +#define TXPOWER_TO_DEV(__txpower) \ + clamp_t(char, __txpower, MIN_TXPOWER, MAX_TXPOWER) #endif /* RT73USB_H */ |
