Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 8 Feb 2013 18:16:17 +0000 (13:16 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 8 Feb 2013 18:16:17 +0000 (13:16 -0500)
249 files changed:
drivers/bcma/bcma_private.h
drivers/bcma/driver_chipcommon_nflash.c
drivers/bcma/driver_chipcommon_sflash.c
drivers/bcma/driver_gpio.c
drivers/bcma/driver_mips.c
drivers/bcma/main.c
drivers/net/wireless/ath/ath9k/Kconfig
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/iwlegacy/3945-mac.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlegacy/4965.c
drivers/net/wireless/iwlegacy/commands.h
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/Makefile
drivers/net/wireless/iwlwifi/dvm/agn.h
drivers/net/wireless/iwlwifi/dvm/calib.c
drivers/net/wireless/iwlwifi/dvm/calib.h
drivers/net/wireless/iwlwifi/dvm/commands.h
drivers/net/wireless/iwlwifi/dvm/debugfs.c
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/devices.c
drivers/net/wireless/iwlwifi/dvm/led.c
drivers/net/wireless/iwlwifi/dvm/led.h
drivers/net/wireless/iwlwifi/dvm/lib.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/power.c
drivers/net/wireless/iwlwifi/dvm/power.h
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/dvm/rs.h
drivers/net/wireless/iwlwifi/dvm/rx.c
drivers/net/wireless/iwlwifi/dvm/rxon.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/sta.c
drivers/net/wireless/iwlwifi/dvm/testmode.c
drivers/net/wireless/iwlwifi/dvm/tt.c
drivers/net/wireless/iwlwifi/dvm/tt.h
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-agn-hw.h
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-devtrace.c
drivers/net/wireless/iwlwifi/iwl-devtrace.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-drv.h
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
drivers/net/wireless/iwlwifi/iwl-fh.h
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-notif-wait.c
drivers/net/wireless/iwlwifi/iwl-notif-wait.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-nvm-parse.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-phy-db.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-test.c
drivers/net/wireless/iwlwifi/iwl-test.h
drivers/net/wireless/iwlwifi/iwl-testmode.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/binding.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/d3.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/debugfs.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw-api.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/fw.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/led.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/mac80211.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/mvm.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/nvm.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/ops.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/power.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/quota.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/rs.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/rs.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/rx.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/scan.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/sta.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/sta.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/time-event.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/time-event.h [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/tx.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/utils.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/pcie/1000.c
drivers/net/wireless/iwlwifi/pcie/2000.c
drivers/net/wireless/iwlwifi/pcie/5000.c
drivers/net/wireless/iwlwifi/pcie/6000.c
drivers/net/wireless/iwlwifi/pcie/7000.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/pcie/cfg.h
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n.h
drivers/net/wireless/mwifiex/README
drivers/net/wireless/mwifiex/debugfs.c
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/util.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rtlwifi/rc.c
drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
drivers/net/wireless/rtlwifi/rtl8192de/dm.c
drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c
drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
drivers/net/wireless/rtlwifi/usb.c
drivers/net/wireless/rtlwifi/wifi.h
drivers/net/wireless/ti/wl1251/Kconfig
drivers/net/wireless/ti/wl12xx/Makefile
drivers/net/wireless/ti/wl12xx/cmd.c
drivers/net/wireless/ti/wl12xx/cmd.h
drivers/net/wireless/ti/wl12xx/event.c [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/event.h [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl12xx/scan.c [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/scan.h [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/wl12xx.h
drivers/net/wireless/ti/wl18xx/Makefile
drivers/net/wireless/ti/wl18xx/acx.c
drivers/net/wireless/ti/wl18xx/acx.h
drivers/net/wireless/ti/wl18xx/cmd.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/cmd.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/conf.h
drivers/net/wireless/ti/wl18xx/event.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/event.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/scan.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/scan.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/tx.c
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/boot.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/conf.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/event.h
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/io.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/rx.c
drivers/net/wireless/ti/wlcore/rx.h
drivers/net/wireless/ti/wlcore/scan.c
drivers/net/wireless/ti/wlcore/scan.h
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/ti/wlcore/spi.c
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
drivers/ssb/driver_gpio.c
drivers/ssb/driver_mipscore.c
drivers/ssb/main.c
drivers/ssb/ssb_private.h
include/linux/bcma/bcma_driver_chipcommon.h
include/linux/bcma/bcma_driver_mips.h
include/linux/ieee80211.h
include/linux/ssb/ssb_driver_mips.h
include/net/bluetooth/a2mp.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/cfg80211.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/bluetooth/a2mp.c
net/bluetooth/amp.c
net/bluetooth/bnep/core.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sysfs.c
net/bluetooth/l2cap_core.c
net/bluetooth/mgmt.c
net/bluetooth/sco.c
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/main.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/sta_info.c
net/mac80211/trace.h
net/mac80211/tx.c
net/wireless/core.c
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/trace.h
net/wireless/util.c

index 04f7c86..6f377bd 100644 (file)
@@ -47,6 +47,7 @@ int bcma_sprom_get(struct bcma_bus *bus);
 /* driver_chipcommon.c */
 #ifdef CONFIG_BCMA_DRIVER_MIPS
 void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
+extern struct platform_device bcma_pflash_dev;
 #endif /* CONFIG_BCMA_DRIVER_MIPS */
 
 /* driver_chipcommon_pmu.c */
index 1f0b83e..d4f699a 100644 (file)
@@ -5,11 +5,11 @@
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
+#include "bcma_private.h"
+
 #include <linux/platform_device.h>
 #include <linux/bcma/bcma.h>
 
-#include "bcma_private.h"
-
 struct platform_device bcma_nflash_dev = {
        .name           = "bcma_nflash",
        .num_resources  = 0,
index 1e694db..e6ed4fe 100644 (file)
@@ -5,11 +5,11 @@
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
+#include "bcma_private.h"
+
 #include <linux/platform_device.h>
 #include <linux/bcma/bcma.h>
 
-#include "bcma_private.h"
-
 static struct resource bcma_sflash_resource = {
        .name   = "bcma_sflash",
        .start  = BCMA_SOC_FLASH2,
index 9a6f585..0b5df53 100644 (file)
@@ -73,6 +73,16 @@ static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio)
        bcma_chipco_gpio_pullup(cc, 1 << gpio, 0);
 }
 
+static int bcma_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+       struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip);
+
+       if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
+               return bcma_core_irq(cc->core);
+       else
+               return -EINVAL;
+}
+
 int bcma_gpio_init(struct bcma_drv_cc *cc)
 {
        struct gpio_chip *chip = &cc->gpio;
@@ -85,6 +95,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
        chip->set               = bcma_gpio_set_value;
        chip->direction_input   = bcma_gpio_direction_input;
        chip->direction_output  = bcma_gpio_direction_output;
+       chip->to_irq            = bcma_gpio_to_irq;
        chip->ngpio             = 16;
        /* There is just one SoC in one device and its GPIO addresses should be
         * deterministic to address them more easily. The other buses could get
index 9fe86ee..9a7f0e3 100644 (file)
 
 #include <linux/bcma/bcma.h>
 
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
 #include <linux/serial_reg.h>
 #include <linux/time.h>
 
+static const char *part_probes[] = { "bcm47xxpart", NULL };
+
+static struct physmap_flash_data bcma_pflash_data = {
+       .part_probe_types       = part_probes,
+};
+
+static struct resource bcma_pflash_resource = {
+       .name   = "bcma_pflash",
+       .flags  = IORESOURCE_MEM,
+};
+
+struct platform_device bcma_pflash_dev = {
+       .name           = "physmap-flash",
+       .dev            = {
+               .platform_data  = &bcma_pflash_data,
+       },
+       .resource       = &bcma_pflash_resource,
+       .num_resources  = 1,
+};
+
 /* The 47162a0 hangs when reading MIPS DMP registers registers */
 static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
 {
@@ -211,6 +233,7 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
 {
        struct bcma_bus *bus = mcore->core->bus;
        struct bcma_drv_cc *cc = &bus->drv_cc;
+       struct bcma_pflash *pflash = &cc->pflash;
 
        switch (cc->capabilities & BCMA_CC_CAP_FLASHT) {
        case BCMA_CC_FLASHT_STSER:
@@ -220,15 +243,20 @@ static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
                break;
        case BCMA_CC_FLASHT_PARA:
                bcma_debug(bus, "Found parallel flash\n");
-               cc->pflash.present = true;
-               cc->pflash.window = BCMA_SOC_FLASH2;
-               cc->pflash.window_size = BCMA_SOC_FLASH2_SZ;
+               pflash->present = true;
+               pflash->window = BCMA_SOC_FLASH2;
+               pflash->window_size = BCMA_SOC_FLASH2_SZ;
 
                if ((bcma_read32(cc->core, BCMA_CC_FLASH_CFG) &
                     BCMA_CC_FLASH_CFG_DS) == 0)
-                       cc->pflash.buswidth = 1;
+                       pflash->buswidth = 1;
                else
-                       cc->pflash.buswidth = 2;
+                       pflash->buswidth = 2;
+
+               bcma_pflash_data.width = pflash->buswidth;
+               bcma_pflash_resource.start = pflash->window;
+               bcma_pflash_resource.end = pflash->window + pflash->window_size;
+
                break;
        default:
                bcma_err(bus, "Flash type not supported\n");
index ff85289..6eda7ef 100644 (file)
@@ -149,6 +149,14 @@ static int bcma_register_cores(struct bcma_bus *bus)
                dev_id++;
        }
 
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+       if (bus->drv_cc.pflash.present) {
+               err = platform_device_register(&bcma_pflash_dev);
+               if (err)
+                       bcma_err(bus, "Error registering parallel flash\n");
+       }
+#endif
+
 #ifdef CONFIG_BCMA_SFLASH
        if (bus->drv_cc.sflash.present) {
                err = platform_device_register(&bcma_sflash_dev);
index 7647ed6..17507dc 100644 (file)
@@ -58,6 +58,7 @@ config ATH9K_DEBUGFS
        bool "Atheros ath9k debugging"
        depends on ATH9K
        select MAC80211_DEBUGFS
+       select RELAY
        ---help---
          Say Y, if you need access to ath9k's statistics for
          interrupts, rate control, etc.
index b2d6c18..97c90b2 100644 (file)
@@ -319,6 +319,8 @@ struct ath_rx {
        struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
 
        struct sk_buff *frag;
+
+       u32 ampdu_ref;
 };
 
 int ath_startrecv(struct ath_softc *sc);
@@ -754,6 +756,7 @@ struct ath_softc {
        /* relay(fs) channel for spectral scan */
        struct rchan *rfs_chan_spec_scan;
        enum spectral_mode spectral_mode;
+       struct ath_spec_scan spec_config;
        int scanning;
 
 #ifdef CONFIG_PM_SLEEP
@@ -863,31 +866,31 @@ static inline u8 spectral_bitmap_weight(u8 *bins)
  * interface.
  */
 enum ath_fft_sample_type {
-       ATH_FFT_SAMPLE_HT20 = 0,
+       ATH_FFT_SAMPLE_HT20 = 1,
 };
 
 struct fft_sample_tlv {
        u8 type;        /* see ath_fft_sample */
-       u16 length;
+       __be16 length;
        /* type dependent data follows */
 } __packed;
 
 struct fft_sample_ht20 {
        struct fft_sample_tlv tlv;
 
-       u8 __alignment;
+       u8 max_exp;
 
-       u16 freq;
+       __be16 freq;
        s8 rssi;
        s8 noise;
 
-       u16 max_magnitude;
+       __be16 max_magnitude;
        u8 max_index;
        u8 bitmap_weight;
 
-       u64 tsf;
+       __be64 tsf;
 
-       u16 data[SPECTRAL_HT20_NUM_BINS];
+       u8 data[SPECTRAL_HT20_NUM_BINS];
 } __packed;
 
 void ath9k_tasklet(unsigned long data);
index 6c5d313..3714b97 100644 (file)
@@ -895,6 +895,7 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
        RXS_ERR("RX-Bytes-All", rx_bytes_all);
        RXS_ERR("RX-Beacons", rx_beacons);
        RXS_ERR("RX-Frags", rx_frags);
+       RXS_ERR("RX-Spectral", rx_spectral);
 
        if (len > size)
                len = size;
@@ -1035,6 +1036,182 @@ static const struct file_operations fops_spec_scan_ctl = {
        .llseek = default_llseek,
 };
 
+static ssize_t read_file_spectral_short_repeat(struct file *file,
+                                              char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_short_repeat(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 1)
+               return -EINVAL;
+
+       sc->spec_config.short_repeat = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_short_repeat = {
+       .read = read_file_spectral_short_repeat,
+       .write = write_file_spectral_short_repeat,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_spectral_count(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.count);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_count(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 255)
+               return -EINVAL;
+
+       sc->spec_config.count = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_count = {
+       .read = read_file_spectral_count,
+       .write = write_file_spectral_count,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_spectral_period(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.period);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_period(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 255)
+               return -EINVAL;
+
+       sc->spec_config.period = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_period = {
+       .read = read_file_spectral_period,
+       .write = write_file_spectral_period,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+static ssize_t read_file_spectral_fft_period(struct file *file,
+                                            char __user *user_buf,
+                                            size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_fft_period(struct file *file,
+                                             const char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 15)
+               return -EINVAL;
+
+       sc->spec_config.fft_period = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_fft_period = {
+       .read = read_file_spectral_fft_period,
+       .write = write_file_spectral_fft_period,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 static struct dentry *create_buf_file_handler(const char *filename,
                                              struct dentry *parent,
                                              umode_t mode,
@@ -1059,11 +1236,13 @@ static int remove_buf_file_handler(struct dentry *dentry)
 void ath_debug_send_fft_sample(struct ath_softc *sc,
                               struct fft_sample_tlv *fft_sample_tlv)
 {
+       int length;
        if (!sc->rfs_chan_spec_scan)
                return;
 
-       relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv,
-                   fft_sample_tlv->length + sizeof(*fft_sample_tlv));
+       length = __be16_to_cpu(fft_sample_tlv->length) +
+                sizeof(*fft_sample_tlv);
+       relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
 }
 
 static struct rchan_callbacks rfs_spec_scan_cb = {
@@ -1893,6 +2072,16 @@ int ath9k_init_debug(struct ath_hw *ah)
        debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR,
                            sc->debug.debugfs_phy, sc,
                            &fops_spec_scan_ctl);
+       debugfs_create_file("spectral_short_repeat", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spectral_short_repeat);
+       debugfs_create_file("spectral_count", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc, &fops_spectral_count);
+       debugfs_create_file("spectral_period", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc, &fops_spectral_period);
+       debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spectral_fft_period);
 
 #ifdef CONFIG_ATH9K_MAC_DEBUG
        debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc,
index a22c0d7..410d6d8 100644 (file)
@@ -219,6 +219,7 @@ struct ath_tx_stats {
  * @rx_too_many_frags_err:  Frames dropped due to too-many-frags received.
  * @rx_beacons:  No. of beacons received.
  * @rx_frags:  No. of rx-fragements received.
+ * @rx_spectral: No of spectral packets received.
  */
 struct ath_rx_stats {
        u32 rx_pkts_all;
@@ -237,6 +238,7 @@ struct ath_rx_stats {
        u32 rx_too_many_frags_err;
        u32 rx_beacons;
        u32 rx_frags;
+       u32 rx_spectral;
 };
 
 struct ath_stats {
index 4b1abc7..af932c9 100644 (file)
@@ -497,6 +497,13 @@ static void ath9k_init_misc(struct ath_softc *sc)
 
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
                sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
+
+       sc->spec_config.enabled = 0;
+       sc->spec_config.short_repeat = true;
+       sc->spec_config.count = 8;
+       sc->spec_config.endless = false;
+       sc->spec_config.period = 0xFF;
+       sc->spec_config.fft_period = 0xF;
 }
 
 static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
@@ -915,7 +922,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
 
        ath9k_eeprom_release(sc);
 
-       if (sc->rfs_chan_spec_scan) {
+       if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) {
                relay_close(sc->rfs_chan_spec_scan);
                sc->rfs_chan_spec_scan = NULL;
        }
index b42be91..811007e 100644 (file)
@@ -605,13 +605,13 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
                 * reported, then decryption and MIC errors are irrelevant,
                 * the frame is going to be dropped either way
                 */
-               if (ads.ds_rxstatus8 & AR_CRCErr)
-                       rs->rs_status |= ATH9K_RXERR_CRC;
-               else if (ads.ds_rxstatus8 & AR_PHYErr) {
+               if (ads.ds_rxstatus8 & AR_PHYErr) {
                        rs->rs_status |= ATH9K_RXERR_PHY;
                        phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode);
                        rs->rs_phyerr = phyerr;
-               } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
+               } else if (ads.ds_rxstatus8 & AR_CRCErr)
+                       rs->rs_status |= ATH9K_RXERR_CRC;
+               else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
                        rs->rs_status |= ATH9K_RXERR_DECRYPT;
                else if (ads.ds_rxstatus8 & AR_MichaelErr)
                        rs->rs_status |= ATH9K_RXERR_MIC;
index 4b72b66..5432f12 100644 (file)
@@ -1099,45 +1099,34 @@ int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
-       struct ath_spec_scan param;
 
        if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
                ath_err(common, "spectrum analyzer not implemented on this hardware\n");
                return -1;
        }
 
-       /* NOTE: this will generate a few samples ...
-        *
-        * TODO: review default parameters, and/or define an interface to set
-        * them.
-        */
-       param.enabled = 1;
-       param.short_repeat = true;
-       param.count = 8;
-       param.endless = false;
-       param.period = 0xFF;
-       param.fft_period = 0xF;
-
        switch (spectral_mode) {
        case SPECTRAL_DISABLED:
-               param.enabled = 0;
+               sc->spec_config.enabled = 0;
                break;
        case SPECTRAL_BACKGROUND:
                /* send endless samples.
                 * TODO: is this really useful for "background"?
                 */
-               param.endless = 1;
+               sc->spec_config.endless = 1;
+               sc->spec_config.enabled = 1;
                break;
        case SPECTRAL_CHANSCAN:
-               break;
        case SPECTRAL_MANUAL:
+               sc->spec_config.endless = 0;
+               sc->spec_config.enabled = 1;
                break;
        default:
                return -1;
        }
 
        ath9k_ps_wakeup(sc);
-       ath9k_hw_ops(ah)->spectral_scan_config(ah, &param);
+       ath9k_hw_ops(ah)->spectral_scan_config(ah, &sc->spec_config);
        ath9k_ps_restore(sc);
 
        sc->spectral_mode = spectral_mode;
index d207433..815bee2 100644 (file)
@@ -474,8 +474,6 @@ void ath_mci_cleanup(struct ath_softc *sc)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_hw *ah = sc->sc_ah;
-       struct ath_mci_coex *mci = &sc->mci_coex;
-       struct ath_mci_buf *buf = &mci->sched_buf;
 
        ar9003_mci_cleanup(ah);
 
index d7c129b..2d0fd17 100644 (file)
@@ -1016,18 +1016,20 @@ static void ath9k_rx_skb_postprocess(struct ath_common *common,
                rxs->flag &= ~RX_FLAG_DECRYPTED;
 }
 
+#ifdef CONFIG_ATH9K_DEBUGFS
 static s8 fix_rssi_inv_only(u8 rssi_val)
 {
        if (rssi_val == 128)
                rssi_val = 0;
        return (s8) rssi_val;
 }
+#endif
 
-
-static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
-                           struct ath_rx_status *rs, u64 tsf)
+/* returns 1 if this was a spectral frame, even if not handled. */
+static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
+                          struct ath_rx_status *rs, u64 tsf)
 {
-#ifdef CONFIG_ATH_DEBUG
+#ifdef CONFIG_ATH9K_DEBUGFS
        struct ath_hw *ah = sc->sc_ah;
        u8 bins[SPECTRAL_HT20_NUM_BINS];
        u8 *vdata = (u8 *)hdr;
@@ -1035,7 +1037,8 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
        struct ath_radar_info *radar_info;
        struct ath_ht20_mag_info *mag_info;
        int len = rs->rs_datalen;
-       int i, dc_pos;
+       int dc_pos;
+       u16 length, max_magnitude;
 
        /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
         * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
@@ -1044,7 +1047,14 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
        if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
            rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
            rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
-               return;
+               return 0;
+
+       /* check if spectral scan bit is set. This does not have to be checked
+        * if received through a SPECTRAL phy error, but shouldn't hurt.
+        */
+       radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
+       if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
+               return 0;
 
        /* Variation in the data length is possible and will be fixed later.
         * Note that we only support HT20 for now.
@@ -1053,19 +1063,13 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
         */
        if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) ||
            (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1))
-               return;
-
-       /* check if spectral scan bit is set. This does not have to be checked
-        * if received through a SPECTRAL phy error, but shouldn't hurt.
-        */
-       radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
-       if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
-               return;
+               return 1;
 
        fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20;
-       fft_sample.tlv.length = sizeof(fft_sample) - sizeof(fft_sample.tlv);
+       length = sizeof(fft_sample) - sizeof(fft_sample.tlv);
+       fft_sample.tlv.length = __cpu_to_be16(length);
 
-       fft_sample.freq = ah->curchan->chan->center_freq;
+       fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq);
        fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
        fft_sample.noise = ah->noise;
 
@@ -1093,7 +1097,7 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
                memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32);
                break;
        default:
-               return;
+               return 1;
        }
 
        /* DC value (value in the middle) is the blind spot of the spectral
@@ -1105,19 +1109,41 @@ static void ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
        /* mag data is at the end of the frame, in front of radar_info */
        mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
 
-       /* Apply exponent and grab further auxiliary information. */
-       for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++)
-               fft_sample.data[i] = bins[i] << mag_info->max_exp;
+       /* copy raw bins without scaling them */
+       memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS);
+       fft_sample.max_exp = mag_info->max_exp & 0xf;
 
-       fft_sample.max_magnitude = spectral_max_magnitude(mag_info->all_bins);
+       max_magnitude = spectral_max_magnitude(mag_info->all_bins);
+       fft_sample.max_magnitude = __cpu_to_be16(max_magnitude);
        fft_sample.max_index = spectral_max_index(mag_info->all_bins);
        fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins);
-       fft_sample.tsf = tsf;
+       fft_sample.tsf = __cpu_to_be64(tsf);
 
        ath_debug_send_fft_sample(sc, &fft_sample.tlv);
+       return 1;
+#else
+       return 0;
 #endif
 }
 
+static void ath9k_apply_ampdu_details(struct ath_softc *sc,
+       struct ath_rx_status *rs, struct ieee80211_rx_status *rxs)
+{
+       if (rs->rs_isaggr) {
+               rxs->flag |= RX_FLAG_AMPDU_DETAILS | RX_FLAG_AMPDU_LAST_KNOWN;
+
+               rxs->ampdu_reference = sc->rx.ampdu_ref;
+
+               if (!rs->rs_moreaggr) {
+                       rxs->flag |= RX_FLAG_AMPDU_IS_LAST;
+                       sc->rx.ampdu_ref++;
+               }
+
+               if (rs->rs_flags & ATH9K_RX_DELIM_CRC_PRE)
+                       rxs->flag |= RX_FLAG_AMPDU_DELIM_CRC_ERROR;
+       }
+}
+
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 {
        struct ath_buf *bf;
@@ -1202,8 +1228,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                    unlikely(tsf_lower - rs.rs_tstamp > 0x10000000))
                        rxs->mactime += 0x100000000ULL;
 
-               if ((rs.rs_status & ATH9K_RXERR_PHY))
-                       ath_process_fft(sc, hdr, &rs, rxs->mactime);
+               if (rs.rs_status & ATH9K_RXERR_PHY) {
+                       if (ath_process_fft(sc, hdr, &rs, rxs->mactime)) {
+                               RX_STAT_INC(rx_spectral);
+                               goto requeue_drop_frag;
+                       }
+               }
 
                retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs,
                                                 rxs, &decrypt_error);
@@ -1320,6 +1350,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3)
                        ath_ant_comb_scan(sc, &rs);
 
+               ath9k_apply_ampdu_details(sc, &rs, rxs);
+
                ieee80211_rx(hw, skb);
 
 requeue_drop_frag:
index 116f4e8..002851f 100644 (file)
@@ -204,7 +204,6 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
                break;
        default:
                return -EOPNOTSUPP;
-
        }
 
        /* FW don't support scan after connection attempt */
@@ -228,8 +227,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
                }
                /* 0-based channel indexes */
                cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
-               wil_dbg(wil, "Scan for ch %d  : %d MHz\n", ch,
-                       request->channels[i]->center_freq);
+               wil_dbg_misc(wil, "Scan for ch %d  : %d MHz\n", ch,
+                            request->channels[i]->center_freq);
        }
 
        return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
@@ -425,8 +424,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
                return -EINVAL;
        }
 
-       wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
-               channel->center_freq, info->privacy ? "secure" : "open");
+       wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
+                    channel->center_freq, info->privacy ? "secure" : "open");
        print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
                             info->ssid, info->ssid_len);
 
index 38049da..dc97e7b 100644 (file)
@@ -38,7 +38,9 @@
 #define WIL6210_IMC_RX         BIT_DMA_EP_RX_ICR_RX_DONE
 #define WIL6210_IMC_TX         (BIT_DMA_EP_TX_ICR_TX_DONE | \
                                BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
-#define WIL6210_IMC_MISC       (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT)
+#define WIL6210_IMC_MISC       (ISR_MISC_FW_READY | \
+                                ISR_MISC_MBOX_EVT | \
+                                ISR_MISC_FW_ERROR)
 
 #define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \
                                        BIT_DMA_PSEUDO_CAUSE_TX | \
@@ -50,7 +52,6 @@
 
 static inline void wil_icr_clear(u32 x, void __iomem *addr)
 {
-
 }
 #else /* defined(CONFIG_WIL6210_ISR_COR) */
 /* configure to Write-1-to-Clear mode */
@@ -94,7 +95,7 @@ static void wil6210_mask_irq_misc(struct wil6210_priv *wil)
 
 static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
 {
-       wil_dbg_IRQ(wil, "%s()\n", __func__);
+       wil_dbg_irq(wil, "%s()\n", __func__);
 
        iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
                  HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
@@ -125,7 +126,7 @@ static void wil6210_unmask_irq_misc(struct wil6210_priv *wil)
 
 static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
 {
-       wil_dbg_IRQ(wil, "%s()\n", __func__);
+       wil_dbg_irq(wil, "%s()\n", __func__);
 
        set_bit(wil_status_irqen, &wil->status);
 
@@ -135,7 +136,7 @@ static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
 
 void wil6210_disable_irq(struct wil6210_priv *wil)
 {
-       wil_dbg_IRQ(wil, "%s()\n", __func__);
+       wil_dbg_irq(wil, "%s()\n", __func__);
 
        wil6210_mask_irq_tx(wil);
        wil6210_mask_irq_rx(wil);
@@ -145,7 +146,7 @@ void wil6210_disable_irq(struct wil6210_priv *wil)
 
 void wil6210_enable_irq(struct wil6210_priv *wil)
 {
-       wil_dbg_IRQ(wil, "%s()\n", __func__);
+       wil_dbg_irq(wil, "%s()\n", __func__);
 
        iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +
                  offsetof(struct RGF_ICR, ICC));
@@ -167,7 +168,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_RX_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
-       wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr);
+       wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
 
        if (!isr) {
                wil_err(wil, "spurious IRQ: RX\n");
@@ -177,7 +178,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
        wil6210_mask_irq_rx(wil);
 
        if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
-               wil_dbg_IRQ(wil, "RX done\n");
+               wil_dbg_irq(wil, "RX done\n");
                isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
                wil_rx_handle(wil);
        }
@@ -197,7 +198,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_TX_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
-       wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr);
+       wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
 
        if (!isr) {
                wil_err(wil, "spurious IRQ: TX\n");
@@ -208,13 +209,13 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
 
        if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
                uint i;
-               wil_dbg_IRQ(wil, "TX done\n");
+               wil_dbg_irq(wil, "TX done\n");
                isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
                for (i = 0; i < 24; i++) {
                        u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
                        if (isr & mask) {
                                isr &= ~mask;
-                               wil_dbg_IRQ(wil, "TX done(%i)\n", i);
+                               wil_dbg_irq(wil, "TX done(%i)\n", i);
                                wil_tx_complete(wil, i);
                        }
                }
@@ -228,6 +229,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
        return IRQ_HANDLED;
 }
 
+static void wil_notify_fw_error(struct wil6210_priv *wil)
+{
+       struct device *dev = &wil_to_ndev(wil)->dev;
+       char *envp[3] = {
+               [0] = "SOURCE=wil6210",
+               [1] = "EVENT=FW_ERROR",
+               [2] = NULL,
+       };
+       kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+}
+
 static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
 {
        struct wil6210_priv *wil = cookie;
@@ -235,7 +247,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
                                         HOSTADDR(RGF_DMA_EP_MISC_ICR) +
                                         offsetof(struct RGF_ICR, ICR));
 
-       wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr);
+       wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
 
        if (!isr) {
                wil_err(wil, "spurious IRQ: MISC\n");
@@ -244,8 +256,15 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
 
        wil6210_mask_irq_misc(wil);
 
+       if (isr & ISR_MISC_FW_ERROR) {
+               wil_dbg_irq(wil, "IRQ: Firmware error\n");
+               clear_bit(wil_status_fwready, &wil->status);
+               wil_notify_fw_error(wil);
+               isr &= ~ISR_MISC_FW_ERROR;
+       }
+
        if (isr & ISR_MISC_FW_READY) {
-               wil_dbg_IRQ(wil, "IRQ: FW ready\n");
+               wil_dbg_irq(wil, "IRQ: FW ready\n");
                /**
                 * Actual FW ready indicated by the
                 * WMI_FW_READY_EVENTID
@@ -268,10 +287,10 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
        struct wil6210_priv *wil = cookie;
        u32 isr = wil->isr_misc;
 
-       wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr);
+       wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
 
        if (isr & ISR_MISC_MBOX_EVT) {
-               wil_dbg_IRQ(wil, "MBOX event\n");
+               wil_dbg_irq(wil, "MBOX event\n");
                wmi_recv_cmd(wil);
                isr &= ~ISR_MISC_MBOX_EVT;
        }
@@ -293,7 +312,7 @@ static irqreturn_t wil6210_thread_irq(int irq, void *cookie)
 {
        struct wil6210_priv *wil = cookie;
 
-       wil_dbg_IRQ(wil, "Thread IRQ\n");
+       wil_dbg_irq(wil, "Thread IRQ\n");
        /* Discover real IRQ cause */
        if (wil->isr_misc)
                wil6210_irq_misc_thread(irq, cookie);
@@ -370,6 +389,8 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
        if (wil6210_debug_irq_mask(wil, pseudo_cause))
                return IRQ_NONE;
 
+       wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
+
        wil6210_mask_irq_pseudo(wil);
 
        /* Discover real IRQ cause
@@ -401,8 +422,6 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
        if (rc != IRQ_WAKE_THREAD)
                wil6210_unmask_irq_pseudo(wil);
 
-       wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause);
-
        return rc;
 }
 
index 95fcd36..761c389 100644 (file)
@@ -64,7 +64,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
        struct net_device *ndev = wil_to_ndev(wil);
        struct wireless_dev *wdev = wil->wdev;
 
-       wil_dbg(wil, "%s()\n", __func__);
+       wil_dbg_misc(wil, "%s()\n", __func__);
 
        wil_link_off(wil);
        clear_bit(wil_status_fwconnected, &wil->status);
@@ -80,11 +80,13 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
                                        GFP_KERNEL);
                break;
        default:
-               ;
+               break;
        }
 
        for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
                wil_vring_fini_tx(wil, i);
+
+       clear_bit(wil_status_dontscan, &wil->status);
 }
 
 static void wil_disconnect_worker(struct work_struct *work)
@@ -99,7 +101,7 @@ static void wil_connect_timer_fn(ulong x)
 {
        struct wil6210_priv *wil = (void *)x;
 
-       wil_dbg(wil, "Connect timeout\n");
+       wil_dbg_misc(wil, "Connect timeout\n");
 
        /* reschedule to thread context - disconnect won't
         * run from atomic context
@@ -107,9 +109,18 @@ static void wil_connect_timer_fn(ulong x)
        schedule_work(&wil->disconnect_worker);
 }
 
+static void wil_cache_mbox_regs(struct wil6210_priv *wil)
+{
+       /* make shadow copy of registers that should not change on run time */
+       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
+                            sizeof(struct wil6210_mbox_ctl));
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
+       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+}
+
 int wil_priv_init(struct wil6210_priv *wil)
 {
-       wil_dbg(wil, "%s()\n", __func__);
+       wil_dbg_misc(wil, "%s()\n", __func__);
 
        mutex_init(&wil->mutex);
        mutex_init(&wil->wmi_mutex);
@@ -136,11 +147,7 @@ int wil_priv_init(struct wil6210_priv *wil)
                return -EAGAIN;
        }
 
-       /* make shadow copy of registers that should not change on run time */
-       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
-                            sizeof(struct wil6210_mbox_ctl));
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+       wil_cache_mbox_regs(wil);
 
        return 0;
 }
@@ -162,7 +169,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
 
 static void wil_target_reset(struct wil6210_priv *wil)
 {
-       wil_dbg(wil, "Resetting...\n");
+       wil_dbg_misc(wil, "Resetting...\n");
 
        /* register write */
 #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
@@ -202,7 +209,7 @@ static void wil_target_reset(struct wil6210_priv *wil)
 
        msleep(2000);
 
-       wil_dbg(wil, "Reset completed\n");
+       wil_dbg_misc(wil, "Reset completed\n");
 
 #undef W
 #undef S
@@ -225,8 +232,8 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
                wil_err(wil, "Firmware not ready\n");
                return -ETIME;
        } else {
-               wil_dbg(wil, "FW ready after %d ms\n",
-                       jiffies_to_msecs(to-left));
+               wil_dbg_misc(wil, "FW ready after %d ms\n",
+                            jiffies_to_msecs(to-left));
        }
        return 0;
 }
@@ -243,13 +250,13 @@ int wil_reset(struct wil6210_priv *wil)
        cancel_work_sync(&wil->disconnect_worker);
        wil6210_disconnect(wil, NULL);
 
+       wil6210_disable_irq(wil);
+       wil->status = 0;
+
        wmi_event_flush(wil);
 
-       flush_workqueue(wil->wmi_wq);
        flush_workqueue(wil->wmi_wq_conn);
-
-       wil6210_disable_irq(wil);
-       wil->status = 0;
+       flush_workqueue(wil->wmi_wq);
 
        /* TODO: put MAC in reset */
        wil_target_reset(wil);
@@ -258,11 +265,7 @@ int wil_reset(struct wil6210_priv *wil)
        wil->pending_connect_cid = -1;
        INIT_COMPLETION(wil->wmi_ready);
 
-       /* make shadow copy of registers that should not change on run time */
-       wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
-                            sizeof(struct wil6210_mbox_ctl));
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
-       wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
+       wil_cache_mbox_regs(wil);
 
        /* TODO: release MAC reset */
        wil6210_enable_irq(wil);
@@ -278,7 +281,7 @@ void wil_link_on(struct wil6210_priv *wil)
 {
        struct net_device *ndev = wil_to_ndev(wil);
 
-       wil_dbg(wil, "%s()\n", __func__);
+       wil_dbg_misc(wil, "%s()\n", __func__);
 
        netif_carrier_on(ndev);
        netif_tx_wake_all_queues(ndev);
@@ -288,7 +291,7 @@ void wil_link_off(struct wil6210_priv *wil)
 {
        struct net_device *ndev = wil_to_ndev(wil);
 
-       wil_dbg(wil, "%s()\n", __func__);
+       wil_dbg_misc(wil, "%s()\n", __func__);
 
        netif_tx_stop_all_queues(ndev);
        netif_carrier_off(ndev);
@@ -311,27 +314,27 @@ static int __wil_up(struct wil6210_priv *wil)
        wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
-               wil_dbg(wil, "type: STATION\n");
+               wil_dbg_misc(wil, "type: STATION\n");
                bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_AP:
-               wil_dbg(wil, "type: AP\n");
+               wil_dbg_misc(wil, "type: AP\n");
                bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
-               wil_dbg(wil, "type: P2P_CLIENT\n");
+               wil_dbg_misc(wil, "type: P2P_CLIENT\n");
                bi = 0;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_P2P_GO:
-               wil_dbg(wil, "type: P2P_GO\n");
+               wil_dbg_misc(wil, "type: P2P_GO\n");
                bi = 100;
                ndev->type = ARPHRD_ETHER;
                break;
        case NL80211_IFTYPE_MONITOR:
-               wil_dbg(wil, "type: Monitor\n");
+               wil_dbg_misc(wil, "type: Monitor\n");
                bi = 0;
                ndev->type = ARPHRD_IEEE80211_RADIOTAP;
                /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
@@ -354,7 +357,7 @@ static int __wil_up(struct wil6210_priv *wil)
                        wmi_set_channel(wil, channel->hw_value);
                break;
        default:
-               ;
+               break;
        }
 
        /* MAC address - pre-requisite for other commands */
index 3068b5c..8ce2e33 100644 (file)
@@ -35,37 +35,12 @@ static int wil_stop(struct net_device *ndev)
        return wil_down(wil);
 }
 
-/*
- * AC to queue mapping
- *
- * AC_VO -> queue 3
- * AC_VI -> queue 2
- * AC_BE -> queue 1
- * AC_BK -> queue 0
- */
-static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb)
-{
-       static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
-       struct wil6210_priv *wil = ndev_to_wil(ndev);
-       u16 rc;
-
-       skb->priority = cfg80211_classify8021d(skb);
-
-       rc = wil_1d_to_queue[skb->priority];
-
-       wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority,
-                    (int)rc);
-
-       return rc;
-}
-
 static const struct net_device_ops wil_netdev_ops = {
        .ndo_open               = wil_open,
        .ndo_stop               = wil_stop,
        .ndo_start_xmit         = wil_start_xmit,
-       .ndo_select_queue       = wil_select_queue,
-       .ndo_set_mac_address    = eth_mac_addr,
-       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
 };
 
 void *wil_if_alloc(struct device *dev, void __iomem *csr)
@@ -97,7 +72,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
        ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels;
        cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT);
 
-       ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1);
+       ndev = alloc_netdev(0, "wlan%d", ether_setup);
        if (!ndev) {
                dev_err(dev, "alloc_netdev_mqs failed\n");
                rc = -ENOMEM;
index 0fc83ed..81c35c6 100644 (file)
@@ -53,7 +53,7 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
        }
        wil->n_msi = use_msi;
        if (wil->n_msi) {
-               wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi);
+               wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi);
                rc = pci_enable_msi_block(pdev, wil->n_msi);
                if (rc && (wil->n_msi == 3)) {
                        wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
@@ -65,7 +65,7 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
                        wil->n_msi = 0;
                }
        } else {
-               wil_dbg(wil, "MSI interrupts disabled, use INTx\n");
+               wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n");
        }
 
        rc = wil6210_init_irq(wil, pdev->irq);
index f29c294..64b971f 100644 (file)
@@ -100,8 +100,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
                d->dma.status = TX_DMA_STATUS_DU;
        }
 
-       wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
-               vring->va, (unsigned long long)vring->pa, vring->ctx);
+       wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
+                    vring->va, (unsigned long long)vring->pa, vring->ctx);
 
        return 0;
 }
@@ -353,8 +353,8 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
        if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
                wil_rx_add_radiotap_header(wil, skb, d);
 
-       wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
-       wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4,
+       wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
+       wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
        wil_vring_advance_head(vring, 1);
@@ -369,7 +369,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
         */
        ftype = wil_rxdesc_ftype(d) << 2;
        if (ftype != IEEE80211_FTYPE_DATA) {
-               wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype);
+               wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
                /* TODO: process it */
                kfree_skb(skb);
                return NULL;
@@ -430,6 +430,8 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
        int rc;
        unsigned int len = skb->len;
 
+       skb_orphan(skb);
+
        if (in_interrupt())
                rc = netif_rx(skb);
        else
@@ -459,13 +461,11 @@ void wil_rx_handle(struct wil6210_priv *wil)
                wil_err(wil, "Rx IRQ while Rx not yet initialized\n");
                return;
        }
-       wil_dbg_TXRX(wil, "%s()\n", __func__);
+       wil_dbg_txrx(wil, "%s()\n", __func__);
        while (NULL != (skb = wil_vring_reap_rx(wil, v))) {
-               wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
+               wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
                                  skb->data, skb_headlen(skb), false);
 
-               skb_orphan(skb);
-
                if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
                        skb->dev = ndev;
                        skb_reset_mac_header(skb);
@@ -484,53 +484,18 @@ void wil_rx_handle(struct wil6210_priv *wil)
 
 int wil_rx_init(struct wil6210_priv *wil)
 {
-       struct net_device *ndev = wil_to_ndev(wil);
-       struct wireless_dev *wdev = wil->wdev;
        struct vring *vring = &wil->vring_rx;
        int rc;
-       struct wmi_cfg_rx_chain_cmd cmd = {
-               .action = WMI_RX_CHAIN_ADD,
-               .rx_sw_ring = {
-                       .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
-               },
-               .mid = 0, /* TODO - what is it? */
-               .decap_trans_type = WMI_DECAP_TYPE_802_3,
-       };
-       struct {
-               struct wil6210_mbox_hdr_wmi wmi;
-               struct wmi_cfg_rx_chain_done_event evt;
-       } __packed evt;
 
        vring->size = WIL6210_RX_RING_SIZE;
        rc = wil_vring_alloc(wil, vring);
        if (rc)
                return rc;
 
-       cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa);
-       cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size);
-       if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
-               struct ieee80211_channel *ch = wdev->preset_chandef.chan;
-
-               cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON);
-               if (ch)
-                       cmd.sniffer_cfg.channel = ch->hw_value - 1;
-               cmd.sniffer_cfg.phy_info_mode =
-                       cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
-               cmd.sniffer_cfg.phy_support =
-                       cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
-                                   ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
-       }
-       /* typical time for secure PCP is 840ms */
-       rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
-                     WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
+       rc = wmi_rx_chain_add(wil, vring);
        if (rc)
                goto err_free;
 
-       vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr);
-
-       wil_dbg(wil, "Rx init: status %d tail 0x%08x\n",
-               le32_to_cpu(evt.evt.status), vring->hwtail);
-
        rc = wil_rx_refill(wil, vring->size);
        if (rc)
                goto err_free;
@@ -546,25 +511,8 @@ void wil_rx_fini(struct wil6210_priv *wil)
 {
        struct vring *vring = &wil->vring_rx;
 
-       if (vring->va) {
-               int rc;
-               struct wmi_cfg_rx_chain_cmd cmd = {
-                       .action = cpu_to_le32(WMI_RX_CHAIN_DEL),
-                       .rx_sw_ring = {
-                               .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
-                       },
-               };
-               struct {
-                       struct wil6210_mbox_hdr_wmi wmi;
-                       struct wmi_cfg_rx_chain_done_event cfg;
-               } __packed wmi_rx_cfg_reply;
-
-               rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
-                             WMI_CFG_RX_CHAIN_DONE_EVENTID,
-                             &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply),
-                             100);
+       if (vring->va)
                wil_vring_free(wil, vring, 0);
-       }
 }
 
 int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
@@ -617,6 +565,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
        if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) {
                wil_err(wil, "Tx config failed, status 0x%02x\n",
                        reply.cmd.status);
+               rc = -EINVAL;
                goto out_free;
        }
        vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
@@ -689,7 +638,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        uint i = swhead;
        dma_addr_t pa;
 
-       wil_dbg_TXRX(wil, "%s()\n", __func__);
+       wil_dbg_txrx(wil, "%s()\n", __func__);
 
        if (avail < vring->size/8)
                netif_tx_stop_all_queues(wil_to_ndev(wil));
@@ -706,9 +655,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        pa = dma_map_single(dev, skb->data,
                        skb_headlen(skb), DMA_TO_DEVICE);
 
-       wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb),
+       wil_dbg_txrx(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb),
                     skb->data, (unsigned long long)pa);
-       wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
+       wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
                          skb->data, skb_headlen(skb), false);
 
        if (unlikely(dma_mapping_error(dev, pa)))
@@ -737,12 +686,12 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
        d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
 
-       wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4,
+       wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
        /* advance swhead */
        wil_vring_advance_head(vring, nr_frags + 1);
-       wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+       wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
        iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
        /* hold reference to skb
         * to prevent skb release before accounting
@@ -775,7 +724,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        struct vring *vring;
        int rc;
 
-       wil_dbg_TXRX(wil, "%s()\n", __func__);
+       wil_dbg_txrx(wil, "%s()\n", __func__);
        if (!test_bit(wil_status_fwready, &wil->status)) {
                wil_err(wil, "FW not ready\n");
                goto drop;
@@ -802,15 +751,13 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        }
        switch (rc) {
        case 0:
-               ndev->stats.tx_packets++;
-               ndev->stats.tx_bytes += skb->len;
+               /* statistics will be updated on the tx_complete */
                dev_kfree_skb_any(skb);
                return NETDEV_TX_OK;
        case -ENOMEM:
                return NETDEV_TX_BUSY;
        default:
-               ; /* goto drop; */
-               break;
+               break; /* goto drop; */
        }
  drop:
        netif_tx_stop_all_queues(ndev);
@@ -827,6 +774,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
  */
 void wil_tx_complete(struct wil6210_priv *wil, int ringid)
 {
+       struct net_device *ndev = wil_to_ndev(wil);
        struct device *dev = wil_to_dev(wil);
        struct vring *vring = &wil->vring_tx[ringid];
 
@@ -835,7 +783,7 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
                return;
        }
 
-       wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid);
+       wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
 
        while (!wil_vring_is_empty(vring)) {
                volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx;
@@ -844,16 +792,23 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
                if (!(d->dma.status & TX_DMA_STATUS_DU))
                        break;
 
-               wil_dbg_TXRX(wil,
+               wil_dbg_txrx(wil,
                             "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
                             vring->swtail, d->dma.length, d->dma.status,
                             d->dma.error);
-               wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4,
+               wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
                                  (const void *)d, sizeof(*d), false);
 
                pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
                skb = vring->ctx[vring->swtail];
                if (skb) {
+                       if (d->dma.error == 0) {
+                               ndev->stats.tx_packets++;
+                               ndev->stats.tx_bytes += skb->len;
+                       } else {
+                               ndev->stats.tx_errors++;
+                       }
+
                        dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
                        dev_kfree_skb_any(skb);
                        vring->ctx[vring->swtail] = NULL;
index 9bcfffa..aea961f 100644 (file)
@@ -36,8 +36,6 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 
 #define WIL6210_MEM_SIZE (2*1024*1024UL)
 
-#define WIL6210_TX_QUEUES (4)
-
 #define WIL6210_RX_RING_SIZE (128)
 #define WIL6210_TX_RING_SIZE (128)
 #define WIL6210_MAX_TX_RINGS (24)
@@ -101,8 +99,7 @@ struct RGF_ICR {
 #define RGF_DMA_EP_MISC_ICR            (0x881bec) /* struct RGF_ICR */
        #define BIT_DMA_EP_MISC_ICR_RX_HTRSH    BIT(0)
        #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT   BIT(1)
-       #define BIT_DMA_EP_MISC_ICR_FW_INT0     BIT(28)
-       #define BIT_DMA_EP_MISC_ICR_FW_INT1     BIT(29)
+       #define BIT_DMA_EP_MISC_ICR_FW_INT(n)   BIT(28+n) /* n = [0..3] */
 
 /* Interrupt moderation control */
 #define RGF_DMA_ITR_CNT_TRSH           (0x881c5c)
@@ -121,8 +118,9 @@ struct RGF_ICR {
 #define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2
 
 /* ISR register bits */
-#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0
-#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1
+#define ISR_MISC_FW_READY      BIT_DMA_EP_MISC_ICR_FW_INT(0)
+#define ISR_MISC_MBOX_EVT      BIT_DMA_EP_MISC_ICR_FW_INT(1)
+#define ISR_MISC_FW_ERROR      BIT_DMA_EP_MISC_ICR_FW_INT(3)
 
 /* Hardware definitions end */
 
@@ -272,17 +270,18 @@ struct wil6210_priv {
 #define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
 #define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
 
-#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
-#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
-#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
+#define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
+#define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
+#define wil_dbg_wmi(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
+#define wil_dbg_misc(wil, fmt, arg...) wil_dbg(wil, "DBG[MISC]" fmt, ##arg)
 
-#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize,    \
+#define wil_hex_dump_txrx(prefix_str, prefix_type, rowsize,    \
                          groupsize, buf, len, ascii)           \
                          wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\
                                         prefix_type, rowsize,  \
                                         groupsize, buf, len, ascii)
 
-#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize,     \
+#define wil_hex_dump_wmi(prefix_str, prefix_type, rowsize,     \
                         groupsize, buf, len, ascii)            \
                         wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\
                                        prefix_type, rowsize,   \
@@ -328,6 +327,7 @@ int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
                       const void *mac_addr, int key_len, const void *key);
 int wmi_echo(struct wil6210_priv *wil);
 int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
+int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);
 
 int wil6210_init_irq(struct wil6210_priv *wil, int irq);
 void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
index 12915f6..0b70e17 100644 (file)
 #include <linux/io.h>
 #include <linux/list.h>
 #include <linux/etherdevice.h>
+#include <linux/if_arp.h>
 
 #include "wil6210.h"
+#include "txrx.h"
 #include "wmi.h"
 
 /**
@@ -186,7 +188,6 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
                wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
                        (int)(sizeof(cmd) + len), r->entry_size);
                return -ERANGE;
-
        }
 
        might_sleep();
@@ -213,7 +214,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        }
        /* next head */
        next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size);
-       wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
+       wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head);
        /* wait till FW finish with previous command */
        for (retry = 5; retry > 0; retry--) {
                r->tail = ioread32(wil->csr + HOST_MBOX +
@@ -234,10 +235,10 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
        }
        cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq);
        /* set command */
-       wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
-       wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
+       wil_dbg_wmi(wil, "WMI command 0x%04x [%d]\n", cmdid, len);
+       wil_hex_dump_wmi("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd,
                         sizeof(cmd), true);
-       wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
+       wil_hex_dump_wmi("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf,
                         len, true);
        wil_memcpy_toio_32(dst, &cmd, sizeof(cmd));
        wil_memcpy_toio_32(dst + sizeof(cmd), buf, len);
@@ -273,7 +274,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
        struct wmi_ready_event *evt = d;
        u32 ver = le32_to_cpu(evt->sw_version);
 
-       wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac);
+       wil_dbg_wmi(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac);
 
        if (!is_valid_ether_addr(ndev->dev_addr)) {
                memcpy(ndev->dev_addr, evt->mac, ETH_ALEN);
@@ -286,7 +287,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len)
 static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d,
                             int len)
 {
-       wil_dbg_WMI(wil, "WMI: FW ready\n");
+       wil_dbg_wmi(wil, "WMI: FW ready\n");
 
        set_bit(wil_status_fwready, &wil->status);
        /* reuse wmi_ready for the firmware ready indication */
@@ -309,11 +310,11 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
        u32 d_len = le32_to_cpu(data->info.len);
        u16 d_status = le16_to_cpu(data->info.status);
 
-       wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n",
+       wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n",
                    data->info.channel, data->info.mcs, data->info.snr);
-       wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
+       wil_dbg_wmi(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len,
                    le16_to_cpu(data->info.stype));
-       wil_dbg_WMI(wil, "qid %d mid %d cid %d\n",
+       wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
                    data->info.qid, data->info.mid, data->info.cid);
 
        if (!channel) {
@@ -329,13 +330,13 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
                const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
                size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
                                                 u.beacon.variable);
-               wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap);
+               wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
 
                bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid,
                                          tsf, cap, bi, ie_buf, ie_len,
                                          signal, GFP_KERNEL);
                if (bss) {
-                       wil_dbg_WMI(wil, "Added BSS %pM\n",
+                       wil_dbg_wmi(wil, "Added BSS %pM\n",
                                    rx_mgmt_frame->bssid);
                        cfg80211_put_bss(bss);
                } else {
@@ -351,7 +352,7 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
                struct wmi_scan_complete_event *data = d;
                bool aborted = (data->status != 0);
 
-               wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+               wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
                cfg80211_scan_done(wil->scan_request, aborted);
                wil->scan_request = NULL;
        } else {
@@ -386,9 +387,9 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
                return;
        }
        ch = evt->channel + 1;
-       wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n",
+       wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n",
                    evt->bssid, ch, evt->cid);
-       wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
+       wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
                         evt->assoc_info, len - sizeof(*evt), true);
 
        /* figure out IE's */
@@ -450,14 +451,13 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
 {
        struct wmi_disconnect_event *evt = d;
 
-       wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n",
+       wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n",
                    evt->bssid,
                    evt->protocol_reason_status, evt->disconnect_reason);
 
        wil->sinfo_gen++;
 
        wil6210_disconnect(wil, evt->bssid);
-       clear_bit(wil_status_dontscan, &wil->status);
 }
 
 static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
@@ -476,7 +476,7 @@ static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)
        wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector);
        wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector);
        wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector);
-       wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n"
+       wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n"
                    "BF status 0x%08x SNR 0x%08x\n"
                    "Tx Tpt %d goodput %d Rx goodput %d\n"
                    "Sectors(rx:tx) my %d:%d peer %d:%d\n",
@@ -501,7 +501,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id,
        struct sk_buff *skb;
        struct ethhdr *eth;
 
-       wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len,
+       wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len,
                    evt->src_mac);
 
        if (eapol_len > 196) { /* TODO: revisit size limit */
@@ -599,15 +599,15 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                iowrite32(0, wil->csr + HOSTADDR(r->tail) +
                          offsetof(struct wil6210_mbox_ring_desc, sync));
                /* indicate */
-               wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n",
+               wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
                            le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type),
                            hdr.flags);
                if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
                    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
-                       wil_dbg_WMI(wil, "WMI event 0x%04x\n",
+                       wil_dbg_wmi(wil, "WMI event 0x%04x\n",
                                    evt->event.wmi.id);
                }
-               wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1,
+               wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
                                 &evt->event.hdr, sizeof(hdr) + len, true);
 
                /* advance tail */
@@ -623,7 +623,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                {
                        int q = queue_work(wil->wmi_wq,
                                           &wil->wmi_event_worker);
-                       wil_dbg_WMI(wil, "queue_work -> %d\n", q);
+                       wil_dbg_wmi(wil, "queue_work -> %d\n", q);
                }
        }
 }
@@ -650,7 +650,7 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
                        cmdid, reply_id, to_msec);
                rc = -ETIME;
        } else {
-               wil_dbg_WMI(wil,
+               wil_dbg_wmi(wil,
                            "wmi_call(0x%04x->0x%04x) completed in %d msec\n",
                            cmdid, reply_id,
                            to_msec - jiffies_to_msecs(remain));
@@ -680,7 +680,7 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
 
        memcpy(cmd.mac, addr, ETH_ALEN);
 
-       wil_dbg_WMI(wil, "Set MAC %pM\n", addr);
+       wil_dbg_wmi(wil, "Set MAC %pM\n", addr);
 
        return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
 }
@@ -778,7 +778,7 @@ int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb)
 
        skb_set_mac_header(skb, 0);
        eth = eth_hdr(skb);
-       wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
+       wil_dbg_wmi(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest);
        for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
                if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0)
                        goto found_dest;
@@ -853,11 +853,60 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
        return rc;
 }
 
+int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
+{
+       struct wireless_dev *wdev = wil->wdev;
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wmi_cfg_rx_chain_cmd cmd = {
+               .action = WMI_RX_CHAIN_ADD,
+               .rx_sw_ring = {
+                       .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
+                       .ring_mem_base = cpu_to_le64(vring->pa),
+                       .ring_size = cpu_to_le16(vring->size),
+               },
+               .mid = 0, /* TODO - what is it? */
+               .decap_trans_type = WMI_DECAP_TYPE_802_3,
+       };
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_cfg_rx_chain_done_event evt;
+       } __packed evt;
+       int rc;
+
+       if (wdev->iftype == NL80211_IFTYPE_MONITOR) {
+               struct ieee80211_channel *ch = wdev->preset_chandef.chan;
+
+               cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON);
+               if (ch)
+                       cmd.sniffer_cfg.channel = ch->hw_value - 1;
+               cmd.sniffer_cfg.phy_info_mode =
+                       cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP);
+               cmd.sniffer_cfg.phy_support =
+                       cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
+                                   ? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
+       }
+       /* typical time for secure PCP is 840ms */
+       rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
+                     WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000);
+       if (rc)
+               return rc;
+
+       vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr);
+
+       wil_dbg_misc(wil, "Rx init: status %d tail 0x%08x\n",
+                    le32_to_cpu(evt.evt.status), vring->hwtail);
+
+       if (le32_to_cpu(evt.evt.status) != WMI_CFG_RX_CHAIN_SUCCESS)
+               rc = -EINVAL;
+
+       return rc;
+}
+
 void wmi_event_flush(struct wil6210_priv *wil)
 {
        struct pending_wmi_event *evt, *t;
 
-       wil_dbg_WMI(wil, "%s()\n", __func__);
+       wil_dbg_wmi(wil, "%s()\n", __func__);
 
        list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) {
                list_del(&evt->list);
@@ -899,7 +948,7 @@ static void wmi_event_handle(struct wil6210_priv *wil,
                                wmi_evt_call_handler(wil, id, evt_data,
                                                     len - sizeof(*wmi));
                        }
-                       wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id);
+                       wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id);
                        complete(&wil->wmi_ready);
                        return;
                }
@@ -964,7 +1013,7 @@ void wmi_connect_worker(struct work_struct *work)
                return;
        }
 
-       wil_dbg_WMI(wil, "Configure for connection CID %d\n",
+       wil_dbg_wmi(wil, "Configure for connection CID %d\n",
                    wil->pending_connect_cid);
 
        rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE,
index 7fc49ca..b1dd560 100644 (file)
@@ -542,9 +542,8 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_ARP_FILTER) {
                /* Hardware ARP filter address list or state changed */
-               brcms_err(core, "%s: arp filtering: enabled %s, count %d"
-                         " (implement)\n", __func__, info->arp_filter_enabled ?
-                         "true" : "false", info->arp_addr_cnt);
+               brcms_err(core, "%s: arp filtering: %d addresses"
+                         " (implement)\n", __func__, info->arp_addr_cnt);
        }
 
        if (changed & BSS_CHANGED_QOS) {
index 050ce7c..83856d1 100644 (file)
@@ -1001,12 +1001,12 @@ il3945_rx_allocate(struct il_priv *il, gfp_t priority)
        struct list_head *element;
        struct il_rx_buf *rxb;
        struct page *page;
+       dma_addr_t page_dma;
        unsigned long flags;
        gfp_t gfp_mask = priority;
 
        while (1) {
                spin_lock_irqsave(&rxq->lock, flags);
-
                if (list_empty(&rxq->rx_used)) {
                        spin_unlock_irqrestore(&rxq->lock, flags);
                        return;
@@ -1035,26 +1035,34 @@ il3945_rx_allocate(struct il_priv *il, gfp_t priority)
                        break;
                }
 
+               /* Get physical address of RB/SKB */
+               page_dma =
+                   pci_map_page(il->pci_dev, page, 0,
+                                PAGE_SIZE << il->hw_params.rx_page_order,
+                                PCI_DMA_FROMDEVICE);
+
+               if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) {
+                       __free_pages(page, il->hw_params.rx_page_order);
+                       break;
+               }
+
                spin_lock_irqsave(&rxq->lock, flags);
+
                if (list_empty(&rxq->rx_used)) {
                        spin_unlock_irqrestore(&rxq->lock, flags);
+                       pci_unmap_page(il->pci_dev, page_dma,
+                                      PAGE_SIZE << il->hw_params.rx_page_order,
+                                      PCI_DMA_FROMDEVICE);
                        __free_pages(page, il->hw_params.rx_page_order);
                        return;
                }
+
                element = rxq->rx_used.next;
                rxb = list_entry(element, struct il_rx_buf, list);
                list_del(element);
-               spin_unlock_irqrestore(&rxq->lock, flags);
 
                rxb->page = page;
-               /* Get physical address of RB/SKB */
-               rxb->page_dma =
-                   pci_map_page(il->pci_dev, page, 0,
-                                PAGE_SIZE << il->hw_params.rx_page_order,
-                                PCI_DMA_FROMDEVICE);
-
-               spin_lock_irqsave(&rxq->lock, flags);
-
+               rxb->page_dma = page_dma;
                list_add_tail(&rxb->list, &rxq->rx_free);
                rxq->free_count++;
                il->alloc_rxb_page++;
@@ -1284,8 +1292,15 @@ il3945_rx_handle(struct il_priv *il)
                            pci_map_page(il->pci_dev, rxb->page, 0,
                                         PAGE_SIZE << il->hw_params.
                                         rx_page_order, PCI_DMA_FROMDEVICE);
-                       list_add_tail(&rxb->list, &rxq->rx_free);
-                       rxq->free_count++;
+                       if (unlikely(pci_dma_mapping_error(il->pci_dev,
+                                                          rxb->page_dma))) {
+                               __il_free_pages(il, rxb->page);
+                               rxb->page = NULL;
+                               list_add_tail(&rxb->list, &rxq->rx_used);
+                       } else {
+                               list_add_tail(&rxb->list, &rxq->rx_free);
+                               rxq->free_count++;
+                       }
                } else
                        list_add_tail(&rxb->list, &rxq->rx_used);
 
index f1dc040..9741ac1 100644 (file)
@@ -319,6 +319,7 @@ il4965_rx_allocate(struct il_priv *il, gfp_t priority)
        struct list_head *element;
        struct il_rx_buf *rxb;
        struct page *page;
+       dma_addr_t page_dma;
        unsigned long flags;
        gfp_t gfp_mask = priority;
 
@@ -356,33 +357,35 @@ il4965_rx_allocate(struct il_priv *il, gfp_t priority)
                        return;
                }
 
+               /* Get physical address of the RB */
+               page_dma =
+                   pci_map_page(il->pci_dev, page, 0,
+                                PAGE_SIZE << il->hw_params.rx_page_order,
+                                PCI_DMA_FROMDEVICE);
+               if (unlikely(pci_dma_mapping_error(il->pci_dev, page_dma))) {
+                       __free_pages(page, il->hw_params.rx_page_order);
+                       break;
+               }
+
                spin_lock_irqsave(&rxq->lock, flags);
 
                if (list_empty(&rxq->rx_used)) {
                        spin_unlock_irqrestore(&rxq->lock, flags);
+                       pci_unmap_page(il->pci_dev, page_dma,
+                                      PAGE_SIZE << il->hw_params.rx_page_order,
+                                      PCI_DMA_FROMDEVICE);
                        __free_pages(page, il->hw_params.rx_page_order);
                        return;
                }
+
                element = rxq->rx_used.next;
                rxb = list_entry(element, struct il_rx_buf, list);
                list_del(element);
 
-               spin_unlock_irqrestore(&rxq->lock, flags);
-
                BUG_ON(rxb->page);
-               rxb->page = page;
-               /* Get physical address of the RB */
-               rxb->page_dma =
-                   pci_map_page(il->pci_dev, page, 0,
-                                PAGE_SIZE << il->hw_params.rx_page_order,
-                                PCI_DMA_FROMDEVICE);
-               /* dma address must be no more than 36 bits */
-               BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36));
-               /* and also 256 byte aligned! */
-               BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
-
-               spin_lock_irqsave(&rxq->lock, flags);
 
+               rxb->page = page;
+               rxb->page_dma = page_dma;
                list_add_tail(&rxb->list, &rxq->rx_free);
                rxq->free_count++;
                il->alloc_rxb_page++;
@@ -725,6 +728,16 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb)
        if (rate_n_flags & RATE_MCS_SGI_MSK)
                rx_status.flag |= RX_FLAG_SHORT_GI;
 
+       if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) {
+               /* We know which subframes of an A-MPDU belong
+                * together since we get a single PHY response
+                * from the firmware for all of them.
+                */
+
+               rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
+               rx_status.ampdu_reference = il->_4965.ampdu_ref;
+       }
+
        il4965_pass_packet_to_mac80211(il, header, len, ampdu_status, rxb,
                                       &rx_status);
 }
@@ -736,6 +749,7 @@ il4965_hdl_rx_phy(struct il_priv *il, struct il_rx_buf *rxb)
 {
        struct il_rx_pkt *pkt = rxb_addr(rxb);
        il->_4965.last_phy_res_valid = true;
+       il->_4965.ampdu_ref++;
        memcpy(&il->_4965.last_phy_res, pkt->u.raw,
               sizeof(struct il_rx_phy_res));
 }
@@ -4281,8 +4295,16 @@ il4965_rx_handle(struct il_priv *il)
                            pci_map_page(il->pci_dev, rxb->page, 0,
                                         PAGE_SIZE << il->hw_params.
                                         rx_page_order, PCI_DMA_FROMDEVICE);
-                       list_add_tail(&rxb->list, &rxq->rx_free);
-                       rxq->free_count++;
+
+                       if (unlikely(pci_dma_mapping_error(il->pci_dev,
+                                                          rxb->page_dma))) {
+                               __il_free_pages(il, rxb->page);
+                               rxb->page = NULL;
+                               list_add_tail(&rxb->list, &rxq->rx_used);
+                       } else {
+                               list_add_tail(&rxb->list, &rxq->rx_free);
+                               rxq->free_count++;
+                       }
                } else
                        list_add_tail(&rxb->list, &rxq->rx_used);
 
@@ -6573,9 +6595,6 @@ il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto out_free_eeprom;
 
-       if (err)
-               goto out_free_eeprom;
-
        /* extract MAC Address */
        il4965_eeprom_get_mac(il, il->addresses[0].addr);
        D_INFO("MAC address: %pM\n", il->addresses[0].addr);
index 5db1171..91eb2d0 100644 (file)
@@ -1748,7 +1748,6 @@ static void
 il4965_post_associate(struct il_priv *il)
 {
        struct ieee80211_vif *vif = il->vif;
-       struct ieee80211_conf *conf = NULL;
        int ret = 0;
 
        if (!vif || !il->is_open)
@@ -1759,8 +1758,6 @@ il4965_post_associate(struct il_priv *il)
 
        il_scan_cancel_timeout(il, 200);
 
-       conf = &il->hw->conf;
-
        il->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
        il_commit_rxon(il);
 
index 25dd7d2..3b6c994 100644 (file)
@@ -1134,8 +1134,9 @@ struct il_wep_cmd {
 #define RX_RES_PHY_FLAGS_MOD_CCK_MSK           cpu_to_le16(1 << 1)
 #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK    cpu_to_le16(1 << 2)
 #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK       cpu_to_le16(1 << 3)
-#define RX_RES_PHY_FLAGS_ANTENNA_MSK           0xf0
+#define RX_RES_PHY_FLAGS_ANTENNA_MSK           0x70
 #define RX_RES_PHY_FLAGS_ANTENNA_POS           4
+#define RX_RES_PHY_FLAGS_AGG_MSK       cpu_to_le16(1 << 7)
 
 #define RX_RES_STATUS_SEC_TYPE_MSK     (0x7 << 8)
 #define RX_RES_STATUS_SEC_TYPE_NONE    (0x0 << 8)
index 37fe553..96f2025 100644 (file)
@@ -1356,6 +1356,7 @@ struct il_priv {
                struct {
                        struct il_rx_phy_res last_phy_res;
                        bool last_phy_res_valid;
+                       u32 ampdu_ref;
 
                        struct completion firmware_loading_complete;
 
index 5cf4323..ba319cb 100644 (file)
@@ -43,8 +43,20 @@ config IWLWIFI
          module will be called iwlwifi.
 
 config IWLDVM
-       tristate "Intel Wireless WiFi"
+       tristate "Intel Wireless WiFi DVM Firmware support"
        depends on IWLWIFI
+       help
+         This is the driver supporting the DVM firmware which is
+         currently the only firmware available for existing devices.
+
+config IWLMVM
+       tristate "Intel Wireless WiFi MVM Firmware support"
+       depends on IWLWIFI
+       help
+         This is the driver supporting the MVM firmware which is
+         currently only available for 7000 series devices.
+
+         Say yes if you have such a device.
 
 menu "Debugging Options"
        depends on IWLWIFI
index 170ec33..6c78000 100644 (file)
@@ -5,8 +5,10 @@ iwlwifi-objs           += iwl-drv.o
 iwlwifi-objs           += iwl-debug.o
 iwlwifi-objs           += iwl-notif-wait.o
 iwlwifi-objs           += iwl-eeprom-read.o iwl-eeprom-parse.o
+iwlwifi-objs           += iwl-phy-db.o iwl-nvm-parse.o
 iwlwifi-objs           += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
 iwlwifi-objs           += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o
+iwlwifi-objs           += pcie/7000.o
 
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
 iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o
@@ -15,5 +17,6 @@ ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
 
 
 obj-$(CONFIG_IWLDVM)   += dvm/
+obj-$(CONFIG_IWLMVM)   += mvm/
 
 CFLAGS_iwl-devtrace.o := -I$(src)
index 33b3ad2..f41ae79 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index de54713..6468de8 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 2349f39..65e920c 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 0ca99c1..8bce4b0 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 72c74af..20806ca 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index 2653a89..71ea775 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 8c72be3..15cca2e 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 844a17f..33c7e15 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index b02a853..8749dcf 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 6ff4660..86ea5f4 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index 0353e1c..c2f03ec 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -459,14 +459,12 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
 
        base = priv->device_pointers.error_event_table;
        if (iwlagn_hw_valid_rtc_data_addr(base)) {
-               spin_lock_irqsave(&priv->trans->reg_lock, flags);
-               if (iwl_trans_grab_nic_access(priv->trans, true)) {
+               if (iwl_trans_grab_nic_access(priv->trans, true, &flags)) {
                        iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, base);
                        status = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
-                       iwl_trans_release_nic_access(priv->trans);
+                       iwl_trans_release_nic_access(priv->trans, &flags);
                        ret = 0;
                }
-               spin_unlock_irqrestore(&priv->trans->reg_lock, flags);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
                if (ret == 0) {
@@ -1154,6 +1152,7 @@ static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
 }
 
 static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
                                     enum ieee80211_rssi_event rssi_event)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
index a64f361..b9e3517 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -353,11 +353,8 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
                ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32));
 
        /* Make sure device is powered up for SRAM reads */
-       spin_lock_irqsave(&priv->trans->reg_lock, reg_flags);
-       if (!iwl_trans_grab_nic_access(priv->trans, false)) {
-               spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags);
+       if (!iwl_trans_grab_nic_access(priv->trans, false, &reg_flags))
                return;
-       }
 
        /* Set starting address; reads will auto-increment */
        iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr);
@@ -388,8 +385,7 @@ static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base,
                }
        }
        /* Allow device to power down */
-       iwl_trans_release_nic_access(priv->trans);
-       spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags);
+       iwl_trans_release_nic_access(priv->trans, &reg_flags);
 }
 
 static void iwl_continuous_event_trace(struct iwl_priv *priv)
@@ -1717,9 +1713,8 @@ static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
        ptr = base + EVENT_START_OFFSET + (start_idx * event_size);
 
        /* Make sure device is powered up for SRAM reads */
-       spin_lock_irqsave(&trans->reg_lock, reg_flags);
-       if (!iwl_trans_grab_nic_access(trans, false))
-               goto out_unlock;
+       if (!iwl_trans_grab_nic_access(trans, false, &reg_flags))
+               return pos;
 
        /* Set starting address; reads will auto-increment */
        iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr);
@@ -1757,9 +1752,7 @@ static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
        }
 
        /* Allow device to power down */
-       iwl_trans_release_nic_access(trans);
-out_unlock:
-       spin_unlock_irqrestore(&trans->reg_lock, reg_flags);
+       iwl_trans_release_nic_access(trans, &reg_flags);
        return pos;
 }
 
@@ -1991,13 +1984,13 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode)
        struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
 
        /* SKU Control */
-       iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
-                         CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
-                         CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP,
-                         (CSR_HW_REV_STEP(priv->trans->hw_rev) <<
-                               CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) |
-                         (CSR_HW_REV_DASH(priv->trans->hw_rev) <<
-                               CSR_HW_IF_CONFIG_REG_POS_MAC_DASH));
+       iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
+                               CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
+                               CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP,
+                               (CSR_HW_REV_STEP(priv->trans->hw_rev) <<
+                                       CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) |
+                               (CSR_HW_REV_DASH(priv->trans->hw_rev) <<
+                                       CSR_HW_IF_CONFIG_REG_POS_MAC_DASH));
 
        /* write radio config values to register */
        if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) {
@@ -2009,10 +2002,11 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode)
                        priv->nvm_data->radio_cfg_dash <<
                                CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
 
-               iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
-                                 CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
-                                 CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
-                                 CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, reg_val);
+               iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG,
+                                       CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
+                                       CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
+                                       CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH,
+                                       reg_val);
 
                IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n",
                         priv->nvm_data->radio_cfg_type,
index 518cf37..bd69018 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index a2cee7f..7b03e13 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index f3dd0da..a131227 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -411,8 +411,9 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv,
         * BT traffic, as they would just be disrupted by BT.
         */
        if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) {
-               IWL_ERR(priv, "BT traffic (%d), no aggregation allowed\n",
-                       priv->bt_traffic_load);
+               IWL_DEBUG_COEX(priv,
+                              "BT traffic (%d), no aggregation allowed\n",
+                              priv->bt_traffic_load);
                return ret;
        }
 
index ad3aea8..5d83cab 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index cac4f37..e8d5b90 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portionhelp of the ieee80211 subsystem header files.
index 9a891e6..9fabd26 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 610ed22..3a4aa52 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index bdba954..ab76804 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index 57b918c..dc6f965 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index b28cfc8..67e2e13 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -185,10 +185,8 @@ static void iwl_tt_check_exit_ct_kill(unsigned long data)
                        priv->thermal_throttle.ct_kill_toggle = true;
                }
                iwl_read32(priv->trans, CSR_UCODE_DRV_GP1);
-               spin_lock_irqsave(&priv->trans->reg_lock, flags);
-               if (iwl_trans_grab_nic_access(priv->trans, false))
-                       iwl_trans_release_nic_access(priv->trans);
-               spin_unlock_irqrestore(&priv->trans->reg_lock, flags);
+               if (iwl_trans_grab_nic_access(priv->trans, false, &flags))
+                       iwl_trans_release_nic_access(priv->trans, &flags);
 
                /* Reschedule the ct_kill timer to occur in
                 * CT_KILL_EXIT_DURATION seconds to ensure we get a
index 44c7c8f..9356c4b 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index 191b9d4..f4a0136 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index ebec13a..736fe9b 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index 7960a52..e9975c5 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 864219d..743b483 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -83,6 +83,7 @@ enum iwl_device_family {
        IWL_DEVICE_FAMILY_6030,
        IWL_DEVICE_FAMILY_6050,
        IWL_DEVICE_FAMILY_6150,
+       IWL_DEVICE_FAMILY_7000,
 };
 
 /*
index b419a1e..df3463a 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 42b20b0..8cf5db7 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
@@ -116,6 +116,7 @@ do {                                                                \
 #define IWL_DL_HCMD            0x00000004
 #define IWL_DL_STATE           0x00000008
 /* 0x000000F0 - 0x00000010 */
+#define IWL_DL_TE              0x00000020
 #define IWL_DL_EEPROM          0x00000040
 #define IWL_DL_RADIO           0x00000080
 /* 0x00000F00 - 0x00000100 */
@@ -156,6 +157,7 @@ do {                                                                \
 #define IWL_DEBUG_LED(p, f, a...)      IWL_DEBUG(p, IWL_DL_LED, f, ## a)
 #define IWL_DEBUG_WEP(p, f, a...)      IWL_DEBUG(p, IWL_DL_WEP, f, ## a)
 #define IWL_DEBUG_HC(p, f, a...)       IWL_DEBUG(p, IWL_DL_HCMD, f, ## a)
+#define IWL_DEBUG_TE(p, f, a...)       IWL_DEBUG(p, IWL_DL_TE, f, ## a)
 #define IWL_DEBUG_EEPROM(d, f, a...)   IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a)
 #define IWL_DEBUG_CALIB(p, f, a...)    IWL_DEBUG(p, IWL_DL_CALIB, f, ## a)
 #define IWL_DEBUG_FW(p, f, a...)       IWL_DEBUG(p, IWL_DL_FW, f, ## a)
index 70191dd..8f61c71 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2009 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2009 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index dc7e26b..9a0f45e 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2009 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2009 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index d3549f4..6f228bb 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -139,8 +139,10 @@ struct iwl_drv {
 #endif
 };
 
-#define DVM_OP_MODE    0
-#define MVM_OP_MODE    1
+enum {
+       DVM_OP_MODE =   0,
+       MVM_OP_MODE =   1,
+};
 
 /* Protects the table contents, i.e. the ops pointer & drv list */
 static struct mutex iwlwifi_opmode_table_mtx;
@@ -149,8 +151,8 @@ static struct iwlwifi_opmode_table {
        const struct iwl_op_mode_ops *ops;      /* pointer to op_mode ops */
        struct list_head drv;           /* list of devices using this op_mode */
 } iwlwifi_opmode_table[] = {           /* ops set when driver is initialized */
-       { .name = "iwldvm", .ops = NULL },
-       { .name = "iwlmvm", .ops = NULL },
+       [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL },
+       [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
 };
 
 /*
@@ -268,7 +270,7 @@ struct fw_sec_parsing {
  */
 struct iwl_tlv_calib_data {
        __le32 ucode_type;
-       __le64 calib;
+       struct iwl_tlv_calib_ctrl calib;
 } __packed;
 
 struct iwl_firmware_pieces {
@@ -358,7 +360,11 @@ static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data)
                        ucode_type);
                return -EINVAL;
        }
-       drv->fw.default_calib[ucode_type] = le64_to_cpu(def_calib->calib);
+       drv->fw.default_calib[ucode_type].flow_trigger =
+               def_calib->calib.flow_trigger;
+       drv->fw.default_calib[ucode_type].event_trigger =
+               def_calib->calib.event_trigger;
+
        return 0;
 }
 
@@ -959,7 +965,10 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        release_firmware(ucode_raw);
 
        mutex_lock(&iwlwifi_opmode_table_mtx);
-       op = &iwlwifi_opmode_table[DVM_OP_MODE];
+       if (fw->mvm_fw)
+               op = &iwlwifi_opmode_table[MVM_OP_MODE];
+       else
+               op = &iwlwifi_opmode_table[DVM_OP_MODE];
 
        /* add this device to the list of devices using this op_mode */
        list_add_tail(&drv->list, &op->drv);
index 285de5f..594a5c7 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -66,7 +66,7 @@
 /* for all modules */
 #define DRV_NAME        "iwlwifi"
 #define IWLWIFI_VERSION "in-tree:"
-#define DRV_COPYRIGHT  "Copyright(c) 2003-2012 Intel Corporation"
+#define DRV_COPYRIGHT  "Copyright(c) 2003-2013 Intel Corporation"
 #define DRV_AUTHOR     "<ilw@linux.intel.com>"
 
 
index 4719866..034f2ff 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -703,9 +703,9 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
        return n_channels;
 }
 
-static int iwl_init_sband_channels(struct iwl_nvm_data *data,
-                                  struct ieee80211_supported_band *sband,
-                                  int n_channels, enum ieee80211_band band)
+int iwl_init_sband_channels(struct iwl_nvm_data *data,
+                           struct ieee80211_supported_band *sband,
+                           int n_channels, enum ieee80211_band band)
 {
        struct ieee80211_channel *chan = &data->channels[0];
        int n = 0, idx = 0;
@@ -728,10 +728,10 @@ static int iwl_init_sband_channels(struct iwl_nvm_data *data,
 #define MAX_BIT_RATE_40_MHZ    150 /* Mbps */
 #define MAX_BIT_RATE_20_MHZ    72 /* Mbps */
 
-static void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
-                                struct iwl_nvm_data *data,
-                                struct ieee80211_sta_ht_cap *ht_info,
-                                enum ieee80211_band band)
+void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
+                         struct iwl_nvm_data *data,
+                         struct ieee80211_sta_ht_cap *ht_info,
+                         enum ieee80211_band band)
 {
        int max_bit_rate = 0;
        u8 rx_chains;
index 555f0eb..683fe6a 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -126,4 +126,13 @@ static inline void iwl_free_nvm_data(struct iwl_nvm_data *data)
 int iwl_nvm_check_version(struct iwl_nvm_data *data,
                          struct iwl_trans *trans);
 
+int iwl_init_sband_channels(struct iwl_nvm_data *data,
+                           struct ieee80211_supported_band *sband,
+                           int n_channels, enum ieee80211_band band);
+
+void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
+                         struct iwl_nvm_data *data,
+                         struct ieee80211_sta_ht_cap *ht_info,
+                         enum ieee80211_band band);
+
 #endif /* __iwl_eeprom_parse_h__ */
index 27c7da3..ef4806f 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 1337c9d..b2588c5 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index c646a90..f5592fb 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -414,6 +414,7 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
  *     uCode/driver must write "1" in order to clear this flag
  */
 #define FH_TSSR_TX_ERROR_REG           (FH_TSSR_LOWER_BOUND + 0x018)
+#define FH_TSSR_TX_MSG_CONFIG_REG      (FH_TSSR_LOWER_BOUND + 0x008)
 
 #define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16)
 
index e715640..90873ec 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 715291e..b545178 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -139,6 +139,19 @@ struct fw_img {
 #define IWL_UCODE_API(ver)     (((ver) & 0x0000FF00) >> 8)
 #define IWL_UCODE_SERIAL(ver)  ((ver) & 0x000000FF)
 
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ *             flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ *             event triggers.
+ */
+struct iwl_tlv_calib_ctrl {
+       __le32 flow_trigger;
+       __le32 event_trigger;
+} __packed;
+
 /**
  * struct iwl_fw - variables associated with the firmware
  *
@@ -153,6 +166,7 @@ struct fw_img {
  * @inst_evtlog_ptr: event log offset for runtime ucode.
  * @inst_evtlog_size: event log size for runtime ucode.
  * @inst_errlog_ptr: error log offfset for runtime ucode.
+ * @mvm_fw: indicates this is MVM firmware
  */
 struct iwl_fw {
        u32 ucode_ver;
@@ -168,7 +182,7 @@ struct iwl_fw {
        u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
        u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
 
-       u64 default_calib[IWL_UCODE_TYPE_MAX];
+       struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX];
        u32 phy_config;
 
        bool mvm_fw;
index bff3ac9..276410d 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
 
 #define IWL_POLL_INTERVAL 10   /* microseconds */
 
-void __iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
-       iwl_write32(trans, reg, iwl_read32(trans, reg) | mask);
-}
-
-void __iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
-       iwl_write32(trans, reg, iwl_read32(trans, reg) & ~mask);
-}
-
-void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       __iwl_set_bit(trans, reg, mask);
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
-}
-EXPORT_SYMBOL_GPL(iwl_set_bit);
-
-void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       __iwl_clear_bit(trans, reg, mask);
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
-}
-EXPORT_SYMBOL_GPL(iwl_clear_bit);
-
-void iwl_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value)
-{
-       unsigned long flags;
-       u32 v;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-       WARN_ON_ONCE(value & ~mask);
-#endif
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       v = iwl_read32(trans, reg);
-       v &= ~mask;
-       v |= value;
-       iwl_write32(trans, reg, v);
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
-}
-EXPORT_SYMBOL_GPL(iwl_set_bits_mask);
-
 int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
                 u32 bits, u32 mask, int timeout)
 {
@@ -103,13 +55,10 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg)
 {
        u32 value = 0x5a5a5a5a;
        unsigned long flags;
-
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                value = iwl_read32(trans, reg);
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 
        return value;
 }
@@ -119,12 +68,10 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                iwl_write32(trans, reg, value);
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_write_direct32);
 
@@ -162,12 +109,10 @@ u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
        unsigned long flags;
        u32 val = 0x5a5a5a5a;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                val = __iwl_read_prph(trans, ofs);
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
        return val;
 }
 EXPORT_SYMBOL_GPL(iwl_read_prph);
@@ -176,12 +121,10 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                __iwl_write_prph(trans, ofs, val);
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_write_prph);
 
@@ -189,13 +132,11 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                __iwl_write_prph(trans, ofs,
                                 __iwl_read_prph(trans, ofs) | mask);
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_set_bits_prph);
 
@@ -204,13 +145,11 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                __iwl_write_prph(trans, ofs,
                                 (__iwl_read_prph(trans, ofs) & mask) | bits);
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_set_bits_mask_prph);
 
@@ -219,12 +158,10 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
        unsigned long flags;
        u32 val;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                val = __iwl_read_prph(trans, ofs);
                __iwl_write_prph(trans, ofs, (val & ~mask));
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
 }
 EXPORT_SYMBOL_GPL(iwl_clear_bits_prph);
index dc47806..fd9f5b9 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
@@ -51,12 +51,15 @@ static inline u32 iwl_read32(struct iwl_trans *trans, u32 ofs)
        return val;
 }
 
-void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask);
-void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask);
-void __iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask);
-void __iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask);
+static inline void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask)
+{
+       iwl_trans_set_bits_mask(trans, reg, mask, mask);
+}
 
-void iwl_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value);
+static inline void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask)
+{
+       iwl_trans_set_bits_mask(trans, reg, mask, 0);
+}
 
 int iwl_poll_bit(struct iwl_trans *trans, u32 addr,
                 u32 bits, u32 mask, int timeout);
index d9a86d6..e5e3a79 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index c61f207..c3affbc 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 8215231..c2ce764 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
new file mode 100644 (file)
index 0000000..a70213b
--- /dev/null
@@ -0,0 +1,346 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include "iwl-modparams.h"
+#include "iwl-nvm-parse.h"
+
+/* NVM offsets (in words) definitions */
+enum wkp_nvm_offsets {
+       /* NVM HW-Section offset (in words) definitions */
+       HW_ADDR = 0x15,
+
+/* NVM SW-Section offset (in words) definitions */
+       NVM_SW_SECTION = 0x1C0,
+       NVM_VERSION = 0,
+       RADIO_CFG = 1,
+       SKU = 2,
+       N_HW_ADDRS = 3,
+       NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION,
+
+/* NVM calibration section offset (in words) definitions */
+       NVM_CALIB_SECTION = 0x2B8,
+       XTAL_CALIB = 0x316 - NVM_CALIB_SECTION
+};
+
+/* SKU Capabilities (actual values from NVM definition) */
+enum nvm_sku_bits {
+       NVM_SKU_CAP_BAND_24GHZ  = BIT(0),
+       NVM_SKU_CAP_BAND_52GHZ  = BIT(1),
+       NVM_SKU_CAP_11N_ENABLE  = BIT(2),
+};
+
+/* radio config bits (actual values from NVM definition) */
+#define NVM_RF_CFG_DASH_MSK(x)   (x & 0x3)         /* bits 0-1   */
+#define NVM_RF_CFG_STEP_MSK(x)   ((x >> 2)  & 0x3) /* bits 2-3   */
+#define NVM_RF_CFG_TYPE_MSK(x)   ((x >> 4)  & 0x3) /* bits 4-5   */
+#define NVM_RF_CFG_PNUM_MSK(x)   ((x >> 6)  & 0x3) /* bits 6-7   */
+#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8)  & 0xF) /* bits 8-11  */
+#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */
+
+/*
+ * These are the channel numbers in the order that they are stored in the NVM
+ */
+static const u8 iwl_nvm_channels[] = {
+       /* 2.4 GHz */
+       1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+       /* 5 GHz */
+       36, 40, 44 , 48, 52, 56, 60, 64,
+       100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
+       149, 153, 157, 161, 165
+};
+
+#define IWL_NUM_CHANNELS       ARRAY_SIZE(iwl_nvm_channels)
+#define NUM_2GHZ_CHANNELS      14
+#define FIRST_2GHZ_HT_MINUS    5
+#define LAST_2GHZ_HT_PLUS      9
+#define LAST_5GHZ_HT           161
+
+
+/* rate data (static) */
+static struct ieee80211_rate iwl_cfg80211_rates[] = {
+       { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, },
+       { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1,
+         .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+       { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2,
+         .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+       { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3,
+         .flags = IEEE80211_RATE_SHORT_PREAMBLE, },
+       { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, },
+       { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, },
+       { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, },
+       { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, },
+       { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, },
+       { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, },
+       { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, },
+       { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, },
+};
+#define RATES_24_OFFS  0
+#define N_RATES_24     ARRAY_SIZE(iwl_cfg80211_rates)
+#define RATES_52_OFFS  4
+#define N_RATES_52     (N_RATES_24 - RATES_52_OFFS)
+
+/**
+ * enum iwl_nvm_channel_flags - channel flags in NVM
+ * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo
+ * @NVM_CHANNEL_IBSS: usable as an IBSS channel
+ * @NVM_CHANNEL_ACTIVE: active scanning allowed
+ * @NVM_CHANNEL_RADAR: radar detection required
+ * @NVM_CHANNEL_DFS: dynamic freq selection candidate
+ * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
+ * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
+ */
+enum iwl_nvm_channel_flags {
+       NVM_CHANNEL_VALID = BIT(0),
+       NVM_CHANNEL_IBSS = BIT(1),
+       NVM_CHANNEL_ACTIVE = BIT(3),
+       NVM_CHANNEL_RADAR = BIT(4),
+       NVM_CHANNEL_DFS = BIT(7),
+       NVM_CHANNEL_WIDE = BIT(8),
+       NVM_CHANNEL_40MHZ = BIT(9),
+};
+
+#define CHECK_AND_PRINT_I(x)   \
+       ((ch_flags & NVM_CHANNEL_##x) ? # x " " : "")
+
+static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
+                               struct iwl_nvm_data *data,
+                               const __le16 * const nvm_ch_flags)
+{
+       int ch_idx;
+       int n_channels = 0;
+       struct ieee80211_channel *channel;
+       u16 ch_flags;
+       bool is_5ghz;
+
+       for (ch_idx = 0; ch_idx < IWL_NUM_CHANNELS; ch_idx++) {
+               ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
+               if (!(ch_flags & NVM_CHANNEL_VALID)) {
+                       IWL_DEBUG_EEPROM(dev,
+                                        "Ch. %d Flags %x [%sGHz] - No traffic\n",
+                                        iwl_nvm_channels[ch_idx],
+                                        ch_flags,
+                                        (ch_idx >= NUM_2GHZ_CHANNELS) ?
+                                        "5.2" : "2.4");
+                       continue;
+               }
+
+               channel = &data->channels[n_channels];
+               n_channels++;
+
+               channel->hw_value = iwl_nvm_channels[ch_idx];
+               channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ?
+                               IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+               channel->center_freq =
+                       ieee80211_channel_to_frequency(
+                               channel->hw_value, channel->band);
+
+               /* TODO: Need to be dependent to the NVM */
+               channel->flags = IEEE80211_CHAN_NO_HT40;
+               if (ch_idx < NUM_2GHZ_CHANNELS &&
+                   (ch_flags & NVM_CHANNEL_40MHZ)) {
+                       if (iwl_nvm_channels[ch_idx] <= LAST_2GHZ_HT_PLUS)
+                               channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+                       if (iwl_nvm_channels[ch_idx] >= FIRST_2GHZ_HT_MINUS)
+                               channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+               } else if (iwl_nvm_channels[ch_idx] <= LAST_5GHZ_HT &&
+                          (ch_flags & NVM_CHANNEL_40MHZ)) {
+                       if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0)
+                               channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS;
+                       else
+                               channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS;
+               }
+
+               if (!(ch_flags & NVM_CHANNEL_IBSS))
+                       channel->flags |= IEEE80211_CHAN_NO_IBSS;
+
+               if (!(ch_flags & NVM_CHANNEL_ACTIVE))
+                       channel->flags |= IEEE80211_CHAN_PASSIVE_SCAN;
+
+               if (ch_flags & NVM_CHANNEL_RADAR)
+                       channel->flags |= IEEE80211_CHAN_RADAR;
+
+               /* Initialize regulatory-based run-time data */
+
+               /* TODO: read the real value from the NVM */
+               channel->max_power = 0;
+               is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
+               IWL_DEBUG_EEPROM(dev,
+                                "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
+                                channel->hw_value,
+                                is_5ghz ? "5.2" : "2.4",
+                                CHECK_AND_PRINT_I(VALID),
+                                CHECK_AND_PRINT_I(IBSS),
+                                CHECK_AND_PRINT_I(ACTIVE),
+                                CHECK_AND_PRINT_I(RADAR),
+                                CHECK_AND_PRINT_I(WIDE),
+                                CHECK_AND_PRINT_I(DFS),
+                                ch_flags,
+                                channel->max_power,
+                                ((ch_flags & NVM_CHANNEL_IBSS) &&
+                                 !(ch_flags & NVM_CHANNEL_RADAR))
+                                       ? "" : "not ");
+       }
+
+       return n_channels;
+}
+
+static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
+                           struct iwl_nvm_data *data, const __le16 *nvm_sw)
+{
+       int n_channels = iwl_init_channel_map(dev, cfg, data,
+                       &nvm_sw[NVM_CHANNELS]);
+       int n_used = 0;
+       struct ieee80211_supported_band *sband;
+
+       sband = &data->bands[IEEE80211_BAND_2GHZ];
+       sband->band = IEEE80211_BAND_2GHZ;
+       sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS];
+       sband->n_bitrates = N_RATES_24;
+       n_used += iwl_init_sband_channels(data, sband, n_channels,
+                                         IEEE80211_BAND_2GHZ);
+       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ);
+
+       sband = &data->bands[IEEE80211_BAND_5GHZ];
+       sband->band = IEEE80211_BAND_5GHZ;
+       sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS];
+       sband->n_bitrates = N_RATES_52;
+       n_used += iwl_init_sband_channels(data, sband, n_channels,
+                                         IEEE80211_BAND_5GHZ);
+       iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ);
+
+       if (n_channels != n_used)
+               IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n",
+                           n_used, n_channels);
+}
+
+struct iwl_nvm_data *
+iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
+                  const __le16 *nvm_hw, const __le16 *nvm_sw,
+                  const __le16 *nvm_calib)
+{
+       struct iwl_nvm_data *data;
+       u8 hw_addr[ETH_ALEN];
+       u16 radio_cfg, sku;
+
+       data = kzalloc(sizeof(*data) +
+                      sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS,
+                      GFP_KERNEL);
+       if (!data)
+               return NULL;
+
+       data->nvm_version = le16_to_cpup(nvm_sw + NVM_VERSION);
+
+       radio_cfg = le16_to_cpup(nvm_sw + RADIO_CFG);
+       data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg);
+       data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg);
+       data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg);
+       data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg);
+       data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK(radio_cfg);
+       data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK(radio_cfg);
+
+       sku = le16_to_cpup(nvm_sw + SKU);
+       data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ;
+       data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ;
+       data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE;
+       if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
+               data->sku_cap_11n_enable = false;
+
+       /* check overrides (some devices have wrong NVM) */
+       if (cfg->valid_tx_ant)
+               data->valid_tx_ant = cfg->valid_tx_ant;
+       if (cfg->valid_rx_ant)
+               data->valid_rx_ant = cfg->valid_rx_ant;
+
+       if (!data->valid_tx_ant || !data->valid_rx_ant) {
+               IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n",
+                           data->valid_tx_ant, data->valid_rx_ant);
+               kfree(data);
+               return NULL;
+       }
+
+       data->n_hw_addrs = le16_to_cpup(nvm_sw + N_HW_ADDRS);
+
+       data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB);
+       data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1);
+
+       /* The byte order is little endian 16 bit, meaning 214365 */
+       memcpy(hw_addr, nvm_hw + HW_ADDR, ETH_ALEN);
+       data->hw_addr[0] = hw_addr[1];
+       data->hw_addr[1] = hw_addr[0];
+       data->hw_addr[2] = hw_addr[3];
+       data->hw_addr[3] = hw_addr[2];
+       data->hw_addr[4] = hw_addr[5];
+       data->hw_addr[5] = hw_addr[4];
+
+       iwl_init_sbands(dev, cfg, data, nvm_sw);
+
+       data->calib_version = 255;   /* TODO:
+                                       this value will prevent some checks from
+                                       failing, we need to check if this
+                                       field is still needed, and if it does,
+                                       where is it in the NVM*/
+
+       return data;
+}
+EXPORT_SYMBOL_GPL(iwl_parse_nvm_data);
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
new file mode 100644 (file)
index 0000000..b2692bd
--- /dev/null
@@ -0,0 +1,80 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#ifndef __iwl_nvm_parse_h__
+#define __iwl_nvm_parse_h__
+
+#include "iwl-eeprom-parse.h"
+
+/**
+ * iwl_parse_nvm_data - parse NVM data and return values
+ *
+ * This function parses all NVM values we need and then
+ * returns a (newly allocated) struct containing all the
+ * relevant values for driver use. The struct must be freed
+ * later with iwl_free_nvm_data().
+ */
+struct iwl_nvm_data *
+iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
+                  const __le16 *nvm_hw, const __le16 *nvm_sw,
+                  const __le16 *nvm_calib);
+
+#endif /* __iwl_nvm_parse_h__ */
index c8d9b95..dc79258 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -63,6 +63,8 @@
 #ifndef __iwl_op_mode_h__
 #define __iwl_op_mode_h__
 
+#include <linux/debugfs.h>
+
 struct iwl_op_mode;
 struct iwl_trans;
 struct sk_buff;
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
new file mode 100644 (file)
index 0000000..14fc8d3
--- /dev/null
@@ -0,0 +1,514 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/export.h>
+
+#include "iwl-phy-db.h"
+#include "iwl-debug.h"
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+
+#define CHANNEL_NUM_SIZE       4       /* num of channels in calib_ch size */
+#define IWL_NUM_PAPD_CH_GROUPS 4
+#define IWL_NUM_TXP_CH_GROUPS  9
+
+struct iwl_phy_db_entry {
+       u16     size;
+       u8      *data;
+};
+
+/**
+ * struct iwl_phy_db - stores phy configuration and calibration data.
+ *
+ * @cfg: phy configuration.
+ * @calib_nch: non channel specific calibration data.
+ * @calib_ch: channel specific calibration data.
+ * @calib_ch_group_papd: calibration data related to papd channel group.
+ * @calib_ch_group_txp: calibration data related to tx power chanel group.
+ */
+struct iwl_phy_db {
+       struct iwl_phy_db_entry cfg;
+       struct iwl_phy_db_entry calib_nch;
+       struct iwl_phy_db_entry calib_ch;
+       struct iwl_phy_db_entry calib_ch_group_papd[IWL_NUM_PAPD_CH_GROUPS];
+       struct iwl_phy_db_entry calib_ch_group_txp[IWL_NUM_TXP_CH_GROUPS];
+
+       u32 channel_num;
+       u32 channel_size;
+
+       struct iwl_trans *trans;
+};
+
+enum iwl_phy_db_section_type {
+       IWL_PHY_DB_CFG = 1,
+       IWL_PHY_DB_CALIB_NCH,
+       IWL_PHY_DB_CALIB_CH,
+       IWL_PHY_DB_CALIB_CHG_PAPD,
+       IWL_PHY_DB_CALIB_CHG_TXP,
+       IWL_PHY_DB_MAX
+};
+
+#define PHY_DB_CMD 0x6c /* TEMP API - The actual is 0x8c */
+
+/*
+ * phy db - configure operational ucode
+ */
+struct iwl_phy_db_cmd {
+       __le16 type;
+       __le16 length;
+       u8 data[];
+} __packed;
+
+/* for parsing of tx power channel group data that comes from the firmware*/
+struct iwl_phy_db_chg_txp {
+       __le32 space;
+       __le16 max_channel_idx;
+} __packed;
+
+/*
+ * phy db - Receieve phy db chunk after calibrations
+ */
+struct iwl_calib_res_notif_phy_db {
+       __le16 type;
+       __le16 length;
+       u8 data[];
+} __packed;
+
+#define IWL_PHY_DB_STATIC_PIC cpu_to_le32(0x21436587)
+static inline void iwl_phy_db_test_pic(__le32 pic)
+{
+       WARN_ON(IWL_PHY_DB_STATIC_PIC != pic);
+}
+
+struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans)
+{
+       struct iwl_phy_db *phy_db = kzalloc(sizeof(struct iwl_phy_db),
+                                           GFP_KERNEL);
+
+       if (!phy_db)
+               return phy_db;
+
+       phy_db->trans = trans;
+
+       /* TODO: add default values of the phy db. */
+       return phy_db;
+}
+EXPORT_SYMBOL(iwl_phy_db_init);
+
+/*
+ * get phy db section: returns a pointer to a phy db section specified by
+ * type and channel group id.
+ */
+static struct iwl_phy_db_entry *
+iwl_phy_db_get_section(struct iwl_phy_db *phy_db,
+                      enum iwl_phy_db_section_type type,
+                      u16 chg_id)
+{
+       if (!phy_db || type >= IWL_PHY_DB_MAX)
+               return NULL;
+
+       switch (type) {
+       case IWL_PHY_DB_CFG:
+               return &phy_db->cfg;
+       case IWL_PHY_DB_CALIB_NCH:
+               return &phy_db->calib_nch;
+       case IWL_PHY_DB_CALIB_CH:
+               return &phy_db->calib_ch;
+       case IWL_PHY_DB_CALIB_CHG_PAPD:
+               if (chg_id >= IWL_NUM_PAPD_CH_GROUPS)
+                       return NULL;
+               return &phy_db->calib_ch_group_papd[chg_id];
+       case IWL_PHY_DB_CALIB_CHG_TXP:
+               if (chg_id >= IWL_NUM_TXP_CH_GROUPS)
+                       return NULL;
+               return &phy_db->calib_ch_group_txp[chg_id];
+       default:
+               return NULL;
+       }
+       return NULL;
+}
+
+static void iwl_phy_db_free_section(struct iwl_phy_db *phy_db,
+                                   enum iwl_phy_db_section_type type,
+                                   u16 chg_id)
+{
+       struct iwl_phy_db_entry *entry =
+                               iwl_phy_db_get_section(phy_db, type, chg_id);
+       if (!entry)
+               return;
+
+       kfree(entry->data);
+       entry->data = NULL;
+       entry->size = 0;
+}
+
+void iwl_phy_db_free(struct iwl_phy_db *phy_db)
+{
+       int i;
+
+       if (!phy_db)
+               return;
+
+       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CFG, 0);
+       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_NCH, 0);
+       iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CH, 0);
+       for (i = 0; i < IWL_NUM_PAPD_CH_GROUPS; i++)
+               iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_PAPD, i);
+       for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++)
+               iwl_phy_db_free_section(phy_db, IWL_PHY_DB_CALIB_CHG_TXP, i);
+
+       kfree(phy_db);
+}
+EXPORT_SYMBOL(iwl_phy_db_free);
+
+int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
+                          gfp_t alloc_ctx)
+{
+       struct iwl_calib_res_notif_phy_db *phy_db_notif =
+                       (struct iwl_calib_res_notif_phy_db *)pkt->data;
+       enum iwl_phy_db_section_type type = le16_to_cpu(phy_db_notif->type);
+       u16 size  = le16_to_cpu(phy_db_notif->length);
+       struct iwl_phy_db_entry *entry;
+       u16 chg_id = 0;
+
+       if (!phy_db)
+               return -EINVAL;
+
+       if (type == IWL_PHY_DB_CALIB_CHG_PAPD ||
+           type == IWL_PHY_DB_CALIB_CHG_TXP)
+               chg_id = le16_to_cpup((__le16 *)phy_db_notif->data);
+
+       entry = iwl_phy_db_get_section(phy_db, type, chg_id);
+       if (!entry)
+               return -EINVAL;
+
+       kfree(entry->data);
+       entry->data = kmemdup(phy_db_notif->data, size, alloc_ctx);
+       if (!entry->data) {
+               entry->size = 0;
+               return -ENOMEM;
+       }
+
+       entry->size = size;
+
+       if (type == IWL_PHY_DB_CALIB_CH) {
+               phy_db->channel_num =
+                       le32_to_cpup((__le32 *)phy_db_notif->data);
+               phy_db->channel_size =
+                       (size - CHANNEL_NUM_SIZE) / phy_db->channel_num;
+       }
+
+       /* Test PIC */
+       if (type != IWL_PHY_DB_CFG)
+               iwl_phy_db_test_pic(*(((__le32 *)phy_db_notif->data) +
+                                     (size / sizeof(__le32)) - 1));
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "%s(%d): [PHYDB]SET: Type %d , Size: %d\n",
+                      __func__, __LINE__, type, size);
+
+       return 0;
+}
+EXPORT_SYMBOL(iwl_phy_db_set_section);
+
+static int is_valid_channel(u16 ch_id)
+{
+       if (ch_id <= 14 ||
+           (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) ||
+           (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) ||
+           (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1))
+               return 1;
+       return 0;
+}
+
+static u8 ch_id_to_ch_index(u16 ch_id)
+{
+       if (WARN_ON(!is_valid_channel(ch_id)))
+               return 0xff;
+
+       if (ch_id <= 14)
+               return ch_id - 1;
+       if (ch_id <= 64)
+               return (ch_id + 20) / 4;
+       if (ch_id <= 140)
+               return (ch_id - 12) / 4;
+       return (ch_id - 13) / 4;
+}
+
+
+static u16 channel_id_to_papd(u16 ch_id)
+{
+       if (WARN_ON(!is_valid_channel(ch_id)))
+               return 0xff;
+
+       if (1 <= ch_id && ch_id <= 14)
+               return 0;
+       if (36 <= ch_id && ch_id <= 64)
+               return 1;
+       if (100 <= ch_id && ch_id <= 140)
+               return 2;
+       return 3;
+}
+
+static u16 channel_id_to_txp(struct iwl_phy_db *phy_db, u16 ch_id)
+{
+       struct iwl_phy_db_chg_txp *txp_chg;
+       int i;
+       u8 ch_index = ch_id_to_ch_index(ch_id);
+       if (ch_index == 0xff)
+               return 0xff;
+
+       for (i = 0; i < IWL_NUM_TXP_CH_GROUPS; i++) {
+               txp_chg = (void *)phy_db->calib_ch_group_txp[i].data;
+               if (!txp_chg)
+                       return 0xff;
+               /*
+                * Looking for the first channel group that its max channel is
+                * higher then wanted channel.
+                */
+               if (le16_to_cpu(txp_chg->max_channel_idx) >= ch_index)
+                       return i;
+       }
+       return 0xff;
+}
+static
+int iwl_phy_db_get_section_data(struct iwl_phy_db *phy_db,
+                               u32 type, u8 **data, u16 *size, u16 ch_id)
+{
+       struct iwl_phy_db_entry *entry;
+       u32 channel_num;
+       u32 channel_size;
+       u16 ch_group_id = 0;
+       u16 index;
+
+       if (!phy_db)
+               return -EINVAL;
+
+       /* find wanted channel group */
+       if (type == IWL_PHY_DB_CALIB_CHG_PAPD)
+               ch_group_id = channel_id_to_papd(ch_id);
+       else if (type == IWL_PHY_DB_CALIB_CHG_TXP)
+               ch_group_id = channel_id_to_txp(phy_db, ch_id);
+
+       entry = iwl_phy_db_get_section(phy_db, type, ch_group_id);
+       if (!entry)
+               return -EINVAL;
+
+       if (type == IWL_PHY_DB_CALIB_CH) {
+               index = ch_id_to_ch_index(ch_id);
+               channel_num = phy_db->channel_num;
+               channel_size = phy_db->channel_size;
+               if (index >= channel_num) {
+                       IWL_ERR(phy_db->trans, "Wrong channel number %d\n",
+                               ch_id);
+                       return -EINVAL;
+               }
+               *data = entry->data + CHANNEL_NUM_SIZE + index * channel_size;
+               *size = channel_size;
+       } else {
+               *data = entry->data;
+               *size = entry->size;
+       }
+
+       /* Test PIC */
+       if (type != IWL_PHY_DB_CFG)
+               iwl_phy_db_test_pic(*(((__le32 *)*data) +
+                                     (*size / sizeof(__le32)) - 1));
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "%s(%d): [PHYDB] GET: Type %d , Size: %d\n",
+                      __func__, __LINE__, type, *size);
+
+       return 0;
+}
+
+static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type,
+                              u16 length, void *data)
+{
+       struct iwl_phy_db_cmd phy_db_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = PHY_DB_CMD,
+               .flags = CMD_SYNC,
+       };
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "Sending PHY-DB hcmd of type %d, of length %d\n",
+                      type, length);
+
+       /* Set phy db cmd variables */
+       phy_db_cmd.type = cpu_to_le16(type);
+       phy_db_cmd.length = cpu_to_le16(length);
+
+       /* Set hcmd variables */
+       cmd.data[0] = &phy_db_cmd;
+       cmd.len[0] = sizeof(struct iwl_phy_db_cmd);
+       cmd.data[1] = data;
+       cmd.len[1] = length;
+       cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
+
+       return iwl_trans_send_cmd(phy_db->trans, &cmd);
+}
+
+static int iwl_phy_db_send_all_channel_groups(
+                                       struct iwl_phy_db *phy_db,
+                                       enum iwl_phy_db_section_type type,
+                                       u8 max_ch_groups)
+{
+       u16 i;
+       int err;
+       struct iwl_phy_db_entry *entry;
+
+       /* Send all the  channel specific groups to operational fw */
+       for (i = 0; i < max_ch_groups; i++) {
+               entry = iwl_phy_db_get_section(phy_db,
+                                              type,
+                                              i);
+               if (!entry)
+                       return -EINVAL;
+
+               /* Send the requested PHY DB section */
+               err = iwl_send_phy_db_cmd(phy_db,
+                                         type,
+                                         entry->size,
+                                         entry->data);
+               if (err) {
+                       IWL_ERR(phy_db->trans,
+                               "Can't SEND phy_db section %d (%d), err %d",
+                               type, i, err);
+                       return err;
+               }
+
+               IWL_DEBUG_INFO(phy_db->trans,
+                              "Sent PHY_DB HCMD, type = %d num = %d",
+                              type, i);
+       }
+
+       return 0;
+}
+
+int iwl_send_phy_db_data(struct iwl_phy_db *phy_db)
+{
+       u8 *data = NULL;
+       u16 size = 0;
+       int err;
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "Sending phy db data and configuration to runtime image\n");
+
+       /* Send PHY DB CFG section */
+       err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CFG,
+                                         &data, &size, 0);
+       if (err) {
+               IWL_ERR(phy_db->trans, "Cannot get Phy DB cfg section\n");
+               return err;
+       }
+
+       err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CFG, size, data);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot send HCMD of  Phy DB cfg section\n");
+               return err;
+       }
+
+       err = iwl_phy_db_get_section_data(phy_db, IWL_PHY_DB_CALIB_NCH,
+                                         &data, &size, 0);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot get Phy DB non specific channel section\n");
+               return err;
+       }
+
+       err = iwl_send_phy_db_cmd(phy_db, IWL_PHY_DB_CALIB_NCH, size, data);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot send HCMD of Phy DB non specific channel section\n");
+               return err;
+       }
+
+       /* Send all the TXP channel specific data */
+       err = iwl_phy_db_send_all_channel_groups(phy_db,
+                                                IWL_PHY_DB_CALIB_CHG_PAPD,
+                                                IWL_NUM_PAPD_CH_GROUPS);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot send channel specific PAPD groups");
+               return err;
+       }
+
+       /* Send all the TXP channel specific data */
+       err = iwl_phy_db_send_all_channel_groups(phy_db,
+                                                IWL_PHY_DB_CALIB_CHG_TXP,
+                                                IWL_NUM_TXP_CH_GROUPS);
+       if (err) {
+               IWL_ERR(phy_db->trans,
+                       "Cannot send channel specific TX power groups");
+               return err;
+       }
+
+       IWL_DEBUG_INFO(phy_db->trans,
+                      "Finished sending phy db non channel data\n");
+       return 0;
+}
+EXPORT_SYMBOL(iwl_send_phy_db_data);
diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h
new file mode 100644 (file)
index 0000000..d0e43d9
--- /dev/null
@@ -0,0 +1,82 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_PHYDB_H__
+#define __IWL_PHYDB_H__
+
+#include <linux/types.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+
+struct iwl_phy_db *iwl_phy_db_init(struct iwl_trans *trans);
+
+void iwl_phy_db_free(struct iwl_phy_db *phy_db);
+
+int iwl_phy_db_set_section(struct iwl_phy_db *phy_db, struct iwl_rx_packet *pkt,
+                          gfp_t alloc_ctx);
+
+
+int iwl_send_phy_db_data(struct iwl_phy_db *phy_db);
+
+#endif /* __IWL_PHYDB_H__ */
index c3a4bb4..f76e9ca 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -97,6 +97,9 @@
 
 #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS         (0x00000800)
 
+/* Device system time */
+#define DEVICE_SYSTEM_TIME_REG 0xA0206C
+
 /**
  * Tx Scheduler
  *
index 1a22611..ce0c67b 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -466,9 +466,7 @@ static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size)
        /* Hard-coded periphery absolute address */
        if (IWL_ABS_PRPH_START <= addr &&
            addr < IWL_ABS_PRPH_START + PRPH_END) {
-                       spin_lock_irqsave(&trans->reg_lock, flags);
-                       if (!iwl_trans_grab_nic_access(trans, false)) {
-                               spin_unlock_irqrestore(&trans->reg_lock, flags);
+                       if (!iwl_trans_grab_nic_access(trans, false, &flags)) {
                                return -EIO;
                        }
                        iwl_write32(trans, HBUS_TARG_PRPH_RADDR,
@@ -476,8 +474,7 @@ static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size)
                        for (i = 0; i < size; i += 4)
                                *(u32 *)(tst->mem.addr + i) =
                                        iwl_read32(trans, HBUS_TARG_PRPH_RDAT);
-                       iwl_trans_release_nic_access(trans);
-                       spin_unlock_irqrestore(&trans->reg_lock, flags);
+                       iwl_trans_release_nic_access(trans, &flags);
        } else { /* target memory (SRAM) */
                iwl_trans_read_mem(trans, addr, tst->mem.addr,
                                   tst->mem.size / 4);
@@ -506,19 +503,13 @@ static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr,
                /* Periphery writes can be 1-3 bytes long, or DWORDs */
                if (size < 4) {
                        memcpy(&val, buf, size);
-                       spin_lock_irqsave(&trans->reg_lock, flags);
-                       if (!iwl_trans_grab_nic_access(trans, false)) {
-                               spin_unlock_irqrestore(&trans->reg_lock, flags);
+                       if (!iwl_trans_grab_nic_access(trans, false, &flags))
                                        return -EIO;
-                       }
                        iwl_write32(trans, HBUS_TARG_PRPH_WADDR,
                                    (addr & 0x0000FFFF) |
                                    ((size - 1) << 24));
                        iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val);
-                       iwl_trans_release_nic_access(trans);
-                       /* needed after consecutive writes w/o read */
-                       mmiowb();
-                       spin_unlock_irqrestore(&trans->reg_lock, flags);
+                       iwl_trans_release_nic_access(trans, &flags);
                } else {
                        if (size % 4)
                                return -EINVAL;
index e13ffa8..7fbf4d7 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 6ba211b..a963f45 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 0f85eb3..0a3d4df 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -193,11 +193,11 @@ struct iwl_rx_packet {
  * @CMD_ON_DEMAND: This command is sent by the test mode pipe.
  */
 enum CMD_MODE {
-       CMD_SYNC = 0,
-       CMD_ASYNC = BIT(0),
-       CMD_WANT_SKB = BIT(1),
-       CMD_WANT_HCMD = BIT(2),
-       CMD_ON_DEMAND = BIT(3),
+       CMD_SYNC                = 0,
+       CMD_ASYNC               = BIT(0),
+       CMD_WANT_SKB            = BIT(1),
+       CMD_WANT_HCMD           = BIT(2),
+       CMD_ON_DEMAND           = BIT(3),
 };
 
 #define DEF_CMD_PAYLOAD_SIZE 320
@@ -274,6 +274,7 @@ struct iwl_rx_cmd_buffer {
        struct page *_page;
        int _offset;
        bool _page_stolen;
+       u32 _rx_page_order;
        unsigned int truesize;
 };
 
@@ -294,6 +295,11 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
        return r->_page;
 }
 
+static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r)
+{
+       __free_pages(r->_page, r->_rx_page_order);
+}
+
 #define MAX_NO_RECLAIM_CMDS    6
 
 #define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo))))
@@ -410,8 +416,12 @@ struct iwl_trans;
  *     the op_mode. May be called several times before start_fw, can't be
  *     called after that.
  * @set_pmi: set the power pmi state
- * @grab_nic_access: wake the NIC to be able to access non-HBUS regs
- * @release_nic_access: let the NIC go to sleep
+ * @grab_nic_access: wake the NIC to be able to access non-HBUS regs.
+ *     Sleeping is not allowed between grab_nic_access and
+ *     release_nic_access.
+ * @release_nic_access: let the NIC go to sleep. The "flags" parameter
+ *     must be the same one that was sent before to the grab_nic_access.
+ * @set_bits_mask - set SRAM register according to value and mask.
  */
 struct iwl_trans_ops {
 
@@ -454,8 +464,12 @@ struct iwl_trans_ops {
        void (*configure)(struct iwl_trans *trans,
                          const struct iwl_trans_config *trans_cfg);
        void (*set_pmi)(struct iwl_trans *trans, bool state);
-       bool (*grab_nic_access)(struct iwl_trans *trans, bool silent);
-       void (*release_nic_access)(struct iwl_trans *trans);
+       bool (*grab_nic_access)(struct iwl_trans *trans, bool silent,
+                               unsigned long *flags);
+       void (*release_nic_access)(struct iwl_trans *trans,
+                                  unsigned long *flags);
+       void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask,
+                             u32 value);
 };
 
 /**
@@ -475,7 +489,6 @@ enum iwl_trans_state {
  * @ops - pointer to iwl_trans_ops
  * @op_mode - pointer to the op_mode
  * @cfg - pointer to the configuration
- * @reg_lock - protect hw register access
  * @dev - pointer to struct device * that represents the device
  * @hw_id: a u32 with the ID of the device / subdevice.
  *     Set during transport allocation.
@@ -496,7 +509,6 @@ struct iwl_trans {
        struct iwl_op_mode *op_mode;
        const struct iwl_cfg *cfg;
        enum iwl_trans_state state;
-       spinlock_t reg_lock;
 
        struct device *dev;
        u32 hw_rev;
@@ -756,14 +768,20 @@ static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state)
        trans->ops->set_pmi(trans, state);
 }
 
-#define iwl_trans_grab_nic_access(trans, silent)       \
+static inline void
+iwl_trans_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value)
+{
+       trans->ops->set_bits_mask(trans, reg, mask, value);
+}
+
+#define iwl_trans_grab_nic_access(trans, silent, flags)        \
        __cond_lock(nic_access,                         \
-                   likely((trans)->ops->grab_nic_access(trans, silent)))
+                   likely((trans)->ops->grab_nic_access(trans, silent, flags)))
 
 static inline void __releases(nic_access)
-iwl_trans_release_nic_access(struct iwl_trans *trans)
+iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
 {
-       trans->ops->release_nic_access(trans);
+       trans->ops->release_nic_access(trans, flags);
        __release(nic_access);
 }
 
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
new file mode 100644 (file)
index 0000000..807b250
--- /dev/null
@@ -0,0 +1,10 @@
+obj-$(CONFIG_IWLMVM)   += iwlmvm.o
+iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
+iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
+iwlmvm-y += scan.o time-event.o rs.o
+iwlmvm-y += power.o
+iwlmvm-y += led.o
+iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
+iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
+
+ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../
diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c
new file mode 100644 (file)
index 0000000..73d24aa
--- /dev/null
@@ -0,0 +1,197 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+struct iwl_mvm_iface_iterator_data {
+       struct ieee80211_vif *ignore_vif;
+       int idx;
+
+       struct iwl_mvm_phy_ctxt *phyctxt;
+
+       u16 ids[MAX_MACS_IN_BINDING];
+       u16 colors[MAX_MACS_IN_BINDING];
+};
+
+static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action,
+                              struct iwl_mvm_iface_iterator_data *data)
+{
+       struct iwl_binding_cmd cmd;
+       struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt;
+       int i, ret;
+       u32 status;
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
+                                                          phyctxt->color));
+       cmd.action = cpu_to_le32(action);
+       cmd.phy = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id,
+                                                 phyctxt->color));
+
+       for (i = 0; i < MAX_MACS_IN_BINDING; i++)
+               cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
+       for (i = 0; i < data->idx; i++)
+               cmd.macs[i] = cpu_to_le32(FW_CMD_ID_AND_COLOR(data->ids[i],
+                                                             data->colors[i]));
+
+       status = 0;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
+                                         sizeof(cmd), &cmd, &status);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n",
+                       action, ret);
+               return ret;
+       }
+
+       if (status) {
+               IWL_ERR(mvm, "Binding command failed: %u\n", status);
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+static void iwl_mvm_iface_iterator(void *_data, u8 *mac,
+                                  struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_iface_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif == data->ignore_vif)
+               return;
+
+       if (mvmvif->phy_ctxt != data->phyctxt)
+               return;
+
+       if (WARN_ON_ONCE(data->idx >= MAX_MACS_IN_BINDING))
+               return;
+
+       data->ids[data->idx] = mvmvif->id;
+       data->colors[data->idx] = mvmvif->color;
+       data->idx++;
+}
+
+static int iwl_mvm_binding_update(struct iwl_mvm *mvm,
+                                 struct ieee80211_vif *vif,
+                                 struct iwl_mvm_phy_ctxt *phyctxt,
+                                 bool add)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_iface_iterator_data data = {
+               .ignore_vif = vif,
+               .phyctxt = phyctxt,
+       };
+       u32 action = FW_CTXT_ACTION_MODIFY;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_iface_iterator,
+                                                  &data);
+
+       /*
+        * If there are no other interfaces yet we
+        * need to create a new binding.
+        */
+       if (data.idx == 0) {
+               if (add)
+                       action = FW_CTXT_ACTION_ADD;
+               else
+                       action = FW_CTXT_ACTION_REMOVE;
+       }
+
+       if (add) {
+               if (WARN_ON_ONCE(data.idx >= MAX_MACS_IN_BINDING))
+                       return -EINVAL;
+
+               data.ids[data.idx] = mvmvif->id;
+               data.colors[data.idx] = mvmvif->color;
+               data.idx++;
+       }
+
+       return iwl_mvm_binding_cmd(mvm, action, &data);
+}
+
+int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+               return -EINVAL;
+
+       return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
+}
+
+int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+               return -EINVAL;
+
+       return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
new file mode 100644 (file)
index 0000000..9a95c37
--- /dev/null
@@ -0,0 +1,841 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/cfg80211.h>
+#include <net/ipv6.h>
+#include "iwl-modparams.h"
+#include "fw-api.h"
+#include "mvm.h"
+
+void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct cfg80211_gtk_rekey_data *data)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (iwlwifi_mod_params.sw_crypto)
+               return;
+
+       mutex_lock(&mvm->mutex);
+
+       memcpy(mvmvif->rekey_data.kek, data->kek, NL80211_KEK_LEN);
+       memcpy(mvmvif->rekey_data.kck, data->kck, NL80211_KCK_LEN);
+       mvmvif->rekey_data.replay_ctr =
+               cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr));
+       mvmvif->rekey_data.valid = true;
+
+       mutex_unlock(&mvm->mutex);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct inet6_dev *idev)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct inet6_ifaddr *ifa;
+       int idx = 0;
+
+       read_lock(&idev->lock);
+       list_for_each_entry(ifa, &idev->addr_list, if_list) {
+               mvmvif->target_ipv6_addrs[idx] = ifa->addr;
+               idx++;
+               if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS)
+                       break;
+       }
+       read_unlock(&idev->lock);
+
+       mvmvif->num_target_ipv6_addrs = idx;
+}
+#endif
+
+void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif, int idx)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->tx_key_idx = idx;
+}
+
+static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out)
+{
+       int i;
+
+       for (i = 0; i < IWL_P1K_SIZE; i++)
+               out[i] = cpu_to_le16(p1k[i]);
+}
+
+struct wowlan_key_data {
+       struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc;
+       struct iwl_wowlan_tkip_params_cmd *tkip;
+       bool error, use_rsc_tsc, use_tkip;
+       int gtk_key_idx;
+};
+
+static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_sta *sta,
+                                       struct ieee80211_key_conf *key,
+                                       void *_data)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct wowlan_key_data *data = _data;
+       struct aes_sc *aes_sc, *aes_tx_sc = NULL;
+       struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
+       struct iwl_p1k_cache *rx_p1ks;
+       u8 *rx_mic_key;
+       struct ieee80211_key_seq seq;
+       u32 cur_rx_iv32 = 0;
+       u16 p1k[IWL_P1K_SIZE];
+       int ret, i;
+
+       mutex_lock(&mvm->mutex);
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */
+               struct {
+                       struct iwl_mvm_wep_key_cmd wep_key_cmd;
+                       struct iwl_mvm_wep_key wep_key;
+               } __packed wkc = {
+                       .wep_key_cmd.mac_id_n_color =
+                               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                               mvmvif->color)),
+                       .wep_key_cmd.num_keys = 1,
+                       /* firmware sets STA_KEY_FLG_WEP_13BYTES */
+                       .wep_key_cmd.decryption_type = STA_KEY_FLG_WEP,
+                       .wep_key.key_index = key->keyidx,
+                       .wep_key.key_size = key->keylen,
+               };
+
+               /*
+                * This will fail -- the key functions don't set support
+                * pairwise WEP keys. However, that's better than silently
+                * failing WoWLAN. Or maybe not?
+                */
+               if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+                       break;
+
+               memcpy(&wkc.wep_key.key[3], key->key, key->keylen);
+               if (key->keyidx == mvmvif->tx_key_idx) {
+                       /* TX key must be at offset 0 */
+                       wkc.wep_key.key_offset = 0;
+               } else {
+                       /* others start at 1 */
+                       data->gtk_key_idx++;
+                       wkc.wep_key.key_offset = data->gtk_key_idx;
+               }
+
+               ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, CMD_SYNC,
+                                          sizeof(wkc), &wkc);
+               data->error = ret != 0;
+
+               /* don't upload key again */
+               goto out_unlock;
+       }
+       default:
+               data->error = true;
+               goto out_unlock;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               /*
+                * Ignore CMAC keys -- the WoWLAN firmware doesn't support them
+                * but we also shouldn't abort suspend due to that. It does have
+                * support for the IGTK key renewal, but doesn't really use the
+                * IGTK for anything. This means we could spuriously wake up or
+                * be deauthenticated, but that was considered acceptable.
+                */
+               goto out_unlock;
+       case WLAN_CIPHER_SUITE_TKIP:
+               if (sta) {
+                       tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
+                       tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
+
+                       rx_p1ks = data->tkip->rx_uni;
+
+                       ieee80211_get_key_tx_seq(key, &seq);
+                       tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16);
+                       tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32);
+
+                       ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
+                       iwl_mvm_convert_p1k(p1k, data->tkip->tx.p1k);
+
+                       memcpy(data->tkip->mic_keys.tx,
+                              &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
+                              IWL_MIC_KEY_SIZE);
+
+                       rx_mic_key = data->tkip->mic_keys.rx_unicast;
+               } else {
+                       tkip_sc =
+                               data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
+                       rx_p1ks = data->tkip->rx_multi;
+                       rx_mic_key = data->tkip->mic_keys.rx_mcast;
+               }
+
+               /*
+                * For non-QoS this relies on the fact that both the uCode and
+                * mac80211 use TID 0 (as they need to to avoid replay attacks)
+                * for checking the IV in the frames.
+                */
+               for (i = 0; i < IWL_NUM_RSC; i++) {
+                       ieee80211_get_key_rx_seq(key, i, &seq);
+                       tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
+                       tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
+                       /* wrapping isn't allowed, AP must rekey */
+                       if (seq.tkip.iv32 > cur_rx_iv32)
+                               cur_rx_iv32 = seq.tkip.iv32;
+               }
+
+               ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid,
+                                         cur_rx_iv32, p1k);
+               iwl_mvm_convert_p1k(p1k, rx_p1ks[0].p1k);
+               ieee80211_get_tkip_rx_p1k(key, vif->bss_conf.bssid,
+                                         cur_rx_iv32 + 1, p1k);
+               iwl_mvm_convert_p1k(p1k, rx_p1ks[1].p1k);
+
+               memcpy(rx_mic_key,
+                      &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
+                      IWL_MIC_KEY_SIZE);
+
+               data->use_tkip = true;
+               data->use_rsc_tsc = true;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               if (sta) {
+                       u8 *pn = seq.ccmp.pn;
+
+                       aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
+                       aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
+
+                       ieee80211_get_key_tx_seq(key, &seq);
+                       aes_tx_sc->pn = cpu_to_le64((u64)pn[5] |
+                                                   ((u64)pn[4] << 8) |
+                                                   ((u64)pn[3] << 16) |
+                                                   ((u64)pn[2] << 24) |
+                                                   ((u64)pn[1] << 32) |
+                                                   ((u64)pn[0] << 40));
+               } else {
+                       aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
+               }
+
+               /*
+                * For non-QoS this relies on the fact that both the uCode and
+                * mac80211 use TID 0 for checking the IV in the frames.
+                */
+               for (i = 0; i < IWL_NUM_RSC; i++) {
+                       u8 *pn = seq.ccmp.pn;
+
+                       ieee80211_get_key_rx_seq(key, i, &seq);
+                       aes_sc->pn = cpu_to_le64((u64)pn[5] |
+                                                ((u64)pn[4] << 8) |
+                                                ((u64)pn[3] << 16) |
+                                                ((u64)pn[2] << 24) |
+                                                ((u64)pn[1] << 32) |
+                                                ((u64)pn[0] << 40));
+               }
+               data->use_rsc_tsc = true;
+               break;
+       }
+
+       /*
+        * The D3 firmware hardcodes the key offset 0 as the key it uses
+        * to transmit packets to the AP, i.e. the PTK.
+        */
+       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+               key->hw_key_idx = 0;
+       } else {
+               data->gtk_key_idx++;
+               key->hw_key_idx = data->gtk_key_idx;
+       }
+
+       ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true);
+       data->error = ret != 0;
+out_unlock:
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
+                                struct cfg80211_wowlan *wowlan)
+{
+       struct iwl_wowlan_patterns_cmd *pattern_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = WOWLAN_PATTERNS,
+               .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+               .flags = CMD_SYNC,
+       };
+       int i, err;
+
+       if (!wowlan->n_patterns)
+               return 0;
+
+       cmd.len[0] = sizeof(*pattern_cmd) +
+               wowlan->n_patterns * sizeof(struct iwl_wowlan_pattern);
+
+       pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
+       if (!pattern_cmd)
+               return -ENOMEM;
+
+       pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
+
+       for (i = 0; i < wowlan->n_patterns; i++) {
+               int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
+
+               memcpy(&pattern_cmd->patterns[i].mask,
+                      wowlan->patterns[i].mask, mask_len);
+               memcpy(&pattern_cmd->patterns[i].pattern,
+                      wowlan->patterns[i].pattern,
+                      wowlan->patterns[i].pattern_len);
+               pattern_cmd->patterns[i].mask_size = mask_len;
+               pattern_cmd->patterns[i].pattern_size =
+                       wowlan->patterns[i].pattern_len;
+       }
+
+       cmd.data[0] = pattern_cmd;
+       err = iwl_mvm_send_cmd(mvm, &cmd);
+       kfree(pattern_cmd);
+       return err;
+}
+
+static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_proto_offload_cmd cmd = {};
+#if IS_ENABLED(CONFIG_IPV6)
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int i;
+
+       if (mvmvif->num_target_ipv6_addrs) {
+               cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_NS);
+               memcpy(cmd.ndp_mac_addr, vif->addr, ETH_ALEN);
+       }
+
+       BUILD_BUG_ON(sizeof(cmd.target_ipv6_addr[i]) !=
+                    sizeof(mvmvif->target_ipv6_addrs[i]));
+
+       for (i = 0; i < mvmvif->num_target_ipv6_addrs; i++)
+               memcpy(cmd.target_ipv6_addr[i],
+                      &mvmvif->target_ipv6_addrs[i],
+                      sizeof(cmd.target_ipv6_addr[i]));
+#endif
+
+       if (vif->bss_conf.arp_addr_cnt) {
+               cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_ARP);
+               cmd.host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
+               memcpy(cmd.arp_mac_addr, vif->addr, ETH_ALEN);
+       }
+
+       if (!cmd.enabled)
+               return 0;
+
+       return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+struct iwl_d3_iter_data {
+       struct iwl_mvm *mvm;
+       struct ieee80211_vif *vif;
+       bool error;
+};
+
+static void iwl_mvm_d3_iface_iterator(void *_data, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_d3_iter_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return;
+
+       if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+               return;
+
+       if (data->vif) {
+               IWL_ERR(data->mvm, "More than one managed interface active!\n");
+               data->error = true;
+               return;
+       }
+
+       data->vif = vif;
+}
+
+static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               struct ieee80211_sta *ap_sta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_chanctx_conf *ctx;
+       u8 chains_static, chains_dynamic;
+       struct cfg80211_chan_def chandef;
+       int ret, i;
+       struct iwl_binding_cmd binding_cmd = {};
+       struct iwl_time_quota_cmd quota_cmd = {};
+       u32 status;
+
+       /* add back the PHY */
+       if (WARN_ON(!mvmvif->phy_ctxt))
+               return -EINVAL;
+
+       rcu_read_lock();
+       ctx = rcu_dereference(vif->chanctx_conf);
+       if (WARN_ON(!ctx)) {
+               rcu_read_unlock();
+               return -EINVAL;
+       }
+       chandef = ctx->def;
+       chains_static = ctx->rx_chains_static;
+       chains_dynamic = ctx->rx_chains_dynamic;
+       rcu_read_unlock();
+
+       ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, &chandef,
+                                  chains_static, chains_dynamic);
+       if (ret)
+               return ret;
+
+       /* add back the MAC */
+       mvmvif->uploaded = false;
+
+       if (WARN_ON(!vif->bss_conf.assoc))
+               return -EINVAL;
+       /* hack */
+       vif->bss_conf.assoc = false;
+       ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+       vif->bss_conf.assoc = true;
+       if (ret)
+               return ret;
+
+       /* add back binding - XXX refactor? */
+       binding_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+                                               mvmvif->phy_ctxt->color));
+       binding_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+       binding_cmd.phy =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+                                               mvmvif->phy_ctxt->color));
+       binding_cmd.macs[0] = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                             mvmvif->color));
+       for (i = 1; i < MAX_MACS_IN_BINDING; i++)
+               binding_cmd.macs[i] = cpu_to_le32(FW_CTXT_INVALID);
+
+       status = 0;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD,
+                                         sizeof(binding_cmd), &binding_cmd,
+                                         &status);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to add binding: %d\n", ret);
+               return ret;
+       }
+
+       if (status) {
+               IWL_ERR(mvm, "Binding command failed: %u\n", status);
+               return -EIO;
+       }
+
+       ret = iwl_mvm_sta_add_to_fw(mvm, ap_sta);
+       if (ret)
+               return ret;
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta);
+
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+       if (ret)
+               return ret;
+
+       /* and some quota */
+       quota_cmd.quotas[0].id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->phy_ctxt->id,
+                                               mvmvif->phy_ctxt->color));
+       quota_cmd.quotas[0].quota = cpu_to_le32(100);
+       quota_cmd.quotas[0].max_duration = cpu_to_le32(1000);
+
+       for (i = 1; i < MAX_BINDINGS; i++)
+               quota_cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+                                  sizeof(quota_cmd), &quota_cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
+
+       return 0;
+}
+
+int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_d3_iter_data suspend_iter_data = {
+               .mvm = mvm,
+       };
+       struct ieee80211_vif *vif;
+       struct iwl_mvm_vif *mvmvif;
+       struct ieee80211_sta *ap_sta;
+       struct iwl_mvm_sta *mvm_ap_sta;
+       struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+       struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
+       struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
+       struct iwl_d3_manager_config d3_cfg_cmd = {};
+       struct wowlan_key_data key_data = {
+               .use_rsc_tsc = false,
+               .tkip = &tkip_cmd,
+               .use_tkip = false,
+       };
+       int ret, i;
+       u16 seq;
+       u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT;
+
+       if (WARN_ON(!wowlan))
+               return -EINVAL;
+
+       key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
+       if (!key_data.rsc_tsc)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+
+       old_aux_sta_id = mvm->aux_sta.sta_id;
+
+       /* see if there's only a single BSS vif and it's associated */
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_d3_iface_iterator, &suspend_iter_data);
+
+       if (suspend_iter_data.error || !suspend_iter_data.vif) {
+               ret = 1;
+               goto out_noreset;
+       }
+
+       vif = suspend_iter_data.vif;
+       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       ap_sta = rcu_dereference_protected(
+                       mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+                       lockdep_is_held(&mvm->mutex));
+       if (IS_ERR_OR_NULL(ap_sta)) {
+               ret = -EINVAL;
+               goto out_noreset;
+       }
+
+       mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
+
+       /*
+        * The D3 firmware still hardcodes the AP station ID for the
+        * BSS we're associated with as 0. Store the real STA ID here
+        * and assign 0. When we leave this function, we'll restore
+        * the original value for the resume code.
+        */
+       old_ap_sta_id = mvm_ap_sta->sta_id;
+       mvm_ap_sta->sta_id = 0;
+       mvmvif->ap_sta_id = 0;
+
+       /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */
+
+       wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported;
+
+       /*
+        * We know the last used seqno, and the uCode expects to know that
+        * one, it will increment before TX.
+        */
+       seq = mvm_ap_sta->last_seq_ctl & IEEE80211_SCTL_SEQ;
+       wowlan_config_cmd.non_qos_seq = cpu_to_le16(seq);
+
+       /*
+        * For QoS counters, we store the one to use next, so subtract 0x10
+        * since the uCode will add 0x10 *before* using the value while we
+        * increment after using the value (i.e. store the next value to use).
+        */
+       for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
+               seq = mvm_ap_sta->tid_data[i].seq_number;
+               seq -= 0x10;
+               wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq);
+       }
+
+       if (wowlan->disconnect)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
+                                   IWL_WOWLAN_WAKEUP_LINK_CHANGE);
+       if (wowlan->magic_pkt)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
+       if (wowlan->gtk_rekey_failure)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
+       if (wowlan->eap_identity_req)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
+       if (wowlan->four_way_handshake)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
+       if (wowlan->n_patterns)
+               wowlan_config_cmd.wakeup_filter |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
+
+       if (wowlan->rfkill_release)
+               d3_cfg_cmd.wakeup_flags |=
+                       cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+       iwl_mvm_cancel_scan(mvm);
+
+       iwl_trans_stop_device(mvm->trans);
+
+       /*
+        * Set the HW restart bit -- this is mostly true as we're
+        * going to load new firmware and reprogram that, though
+        * the reprogramming is going to be manual to avoid adding
+        * all the MACs that aren't support.
+        * We don't have to clear up everything though because the
+        * reprogramming is manual. When we resume, we'll actually
+        * go through a proper restart sequence again to switch
+        * back to the runtime firmware image.
+        */
+       set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+
+       /* We reprogram keys and shouldn't allocate new key indices */
+       memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
+
+       /*
+        * The D3 firmware still hardcodes the AP station ID for the
+        * BSS we're associated with as 0. As a result, we have to move
+        * the auxiliary station to ID 1 so the ID 0 remains free for
+        * the AP station for later.
+        * We set the sta_id to 1 here, and reset it to its previous
+        * value (that we stored above) later.
+        */
+       mvm->aux_sta.sta_id = 1;
+
+       ret = iwl_mvm_load_d3_fw(mvm);
+       if (ret)
+               goto out;
+
+       ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta);
+       if (ret)
+               goto out;
+
+       if (!iwlwifi_mod_params.sw_crypto) {
+               /*
+                * This needs to be unlocked due to lock ordering
+                * constraints. Since we're in the suspend path
+                * that isn't really a problem though.
+                */
+               mutex_unlock(&mvm->mutex);
+               ieee80211_iter_keys(mvm->hw, vif,
+                                   iwl_mvm_wowlan_program_keys,
+                                   &key_data);
+               mutex_lock(&mvm->mutex);
+               if (key_data.error) {
+                       ret = -EIO;
+                       goto out;
+               }
+
+               if (key_data.use_rsc_tsc) {
+                       struct iwl_host_cmd rsc_tsc_cmd = {
+                               .id = WOWLAN_TSC_RSC_PARAM,
+                               .flags = CMD_SYNC,
+                               .data[0] = key_data.rsc_tsc,
+                               .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+                               .len[0] = sizeof(*key_data.rsc_tsc),
+                       };
+
+                       ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd);
+                       if (ret)
+                               goto out;
+               }
+
+               if (key_data.use_tkip) {
+                       ret = iwl_mvm_send_cmd_pdu(mvm,
+                                                  WOWLAN_TKIP_PARAM,
+                                                  CMD_SYNC, sizeof(tkip_cmd),
+                                                  &tkip_cmd);
+                       if (ret)
+                               goto out;
+               }
+
+               if (mvmvif->rekey_data.valid) {
+                       memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
+                       memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck,
+                              NL80211_KCK_LEN);
+                       kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
+                       memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek,
+                              NL80211_KEK_LEN);
+                       kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
+                       kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
+
+                       ret = iwl_mvm_send_cmd_pdu(mvm,
+                                                  WOWLAN_KEK_KCK_MATERIAL,
+                                                  CMD_SYNC,
+                                                  sizeof(kek_kck_cmd),
+                                                  &kek_kck_cmd);
+                       if (ret)
+                               goto out;
+               }
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION,
+                                  CMD_SYNC, sizeof(wowlan_config_cmd),
+                                  &wowlan_config_cmd);
+       if (ret)
+               goto out;
+
+       ret = iwl_mvm_send_patterns(mvm, wowlan);
+       if (ret)
+               goto out;
+
+       ret = iwl_mvm_send_proto_offload(mvm, vif);
+       if (ret)
+               goto out;
+
+       /* must be last -- this switches firmware state */
+       ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC,
+                                  sizeof(d3_cfg_cmd), &d3_cfg_cmd);
+       if (ret)
+               goto out;
+
+       clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+
+       iwl_trans_d3_suspend(mvm->trans);
+ out:
+       mvm->aux_sta.sta_id = old_aux_sta_id;
+       mvm_ap_sta->sta_id = old_ap_sta_id;
+       mvmvif->ap_sta_id = old_ap_sta_id;
+ out_noreset:
+       kfree(key_data.rsc_tsc);
+       if (ret < 0)
+               ieee80211_restart_hw(mvm->hw);
+
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+int iwl_mvm_resume(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_d3_iter_data resume_iter_data = {
+               .mvm = mvm,
+       };
+       struct ieee80211_vif *vif = NULL;
+       u32 base;
+       int ret;
+       enum iwl_d3_status d3_status;
+       struct error_table_start {
+               /* cf. struct iwl_error_event_table */
+               u32 valid;
+               u32 error_id;
+       } err_info;
+
+       mutex_lock(&mvm->mutex);
+
+       /* get the BSS vif pointer again */
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_d3_iface_iterator, &resume_iter_data);
+
+       if (WARN_ON(resume_iter_data.error || !resume_iter_data.vif))
+               goto out_unlock;
+
+       vif = resume_iter_data.vif;
+
+       ret = iwl_trans_d3_resume(mvm->trans, &d3_status);
+       if (ret)
+               goto out_unlock;
+
+       if (d3_status != IWL_D3_STATUS_ALIVE) {
+               IWL_INFO(mvm, "Device was reset during suspend\n");
+               goto out_unlock;
+       }
+
+       base = mvm->error_event_table;
+
+       iwl_trans_read_mem_bytes(mvm->trans, base,
+                                &err_info, sizeof(err_info));
+
+       if (err_info.valid) {
+               IWL_INFO(mvm, "error table is valid (%d)\n",
+                        err_info.valid);
+               if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN)
+                       IWL_ERR(mvm, "this was due to RF-kill\n");
+               goto out_unlock;
+       }
+
+       /* TODO: get status and whatever else ... */
+       ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_GET_STATUSES, CMD_SYNC, 0, NULL);
+       if (ret)
+               IWL_ERR(mvm, "failed to query status (%d)\n", ret);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL);
+       if (ret)
+               IWL_ERR(mvm, "failed to query offloads (%d)\n", ret);
+
+ out_unlock:
+       mutex_unlock(&mvm->mutex);
+
+       if (vif)
+               ieee80211_resume_disconnect(vif);
+
+       /* return 1 to reconfigure the device */
+       set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+       return 1;
+}
+
+void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       device_set_wakeup_enable(mvm->trans->dev, enabled);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
new file mode 100644 (file)
index 0000000..c1bdb55
--- /dev/null
@@ -0,0 +1,378 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "mvm.h"
+#include "sta.h"
+#include "iwl-io.h"
+
+struct iwl_dbgfs_mvm_ctx {
+       struct iwl_mvm *mvm;
+       struct ieee80211_vif *vif;
+};
+
+static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t iwl_dbgfs_tx_flush_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+
+       char buf[16];
+       int buf_size, ret;
+       u32 scd_q_msk;
+
+       if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
+               return -EIO;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (sscanf(buf, "%x", &scd_q_msk) != 1)
+               return -EINVAL;
+
+       IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk);
+
+       mutex_lock(&mvm->mutex);
+       ret =  iwl_mvm_flush_tx_path(mvm, scd_q_msk, true) ? : count;
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_sta_drain_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct ieee80211_sta *sta;
+
+       char buf[8];
+       int buf_size, sta_id, drain, ret;
+
+       if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR)
+               return -EIO;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (sscanf(buf, "%d %d", &sta_id, &drain) != 2)
+               return -EINVAL;
+
+       mutex_lock(&mvm->mutex);
+
+       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                       lockdep_is_held(&mvm->mutex));
+       if (IS_ERR_OR_NULL(sta))
+               ret = -ENOENT;
+       else
+               ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? :
+                       count;
+
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       const struct fw_img *img;
+       int ofs, len, pos = 0;
+       size_t bufsz, ret;
+       char *buf;
+       u8 *ptr;
+
+       /* default is to dump the entire data segment */
+       if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) {
+               mvm->dbgfs_sram_offset = 0x800000;
+               if (!mvm->ucode_loaded)
+                       return -EINVAL;
+               img = &mvm->fw->img[mvm->cur_ucode];
+               mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       }
+       len = mvm->dbgfs_sram_len;
+
+       bufsz = len * 4 + 256;
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ptr = kzalloc(len, GFP_KERNEL);
+       if (!ptr) {
+               kfree(buf);
+               return -ENOMEM;
+       }
+
+       pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len);
+       pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n",
+                        mvm->dbgfs_sram_offset);
+
+       iwl_trans_read_mem_bytes(mvm->trans,
+                                mvm->dbgfs_sram_offset,
+                                ptr, len);
+       for (ofs = 0; ofs < len; ofs += 16) {
+               pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs);
+               hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
+                                  bufsz - pos, false);
+               pos += strlen(buf + pos);
+               if (bufsz - pos > 0)
+                       buf[pos++] = '\n';
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+
+       kfree(buf);
+       kfree(ptr);
+
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_sram_write(struct file *file,
+                                   const char __user *user_buf, size_t count,
+                                   loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[64];
+       int buf_size;
+       u32 offset, len;
+
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) -  1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
+               if ((offset & 0x3) || (len & 0x3))
+                       return -EINVAL;
+               mvm->dbgfs_sram_offset = offset;
+               mvm->dbgfs_sram_len = len;
+       } else {
+               mvm->dbgfs_sram_offset = 0;
+               mvm->dbgfs_sram_len = 0;
+       }
+
+       return count;
+}
+
+static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct ieee80211_sta *sta;
+       char buf[400];
+       int i, pos = 0, bufsz = sizeof(buf);
+
+       mutex_lock(&mvm->mutex);
+
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i);
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (!sta)
+                       pos += scnprintf(buf + pos, bufsz - pos, "N/A\n");
+               else if (IS_ERR(sta))
+                       pos += scnprintf(buf + pos, bufsz - pos, "%ld\n",
+                                        PTR_ERR(sta));
+               else
+                       pos += scnprintf(buf + pos, bufsz - pos, "%pM\n",
+                                        sta->addr);
+       }
+
+       mutex_unlock(&mvm->mutex);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+static ssize_t iwl_dbgfs_power_down_allow_write(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[8] = {};
+       int allow;
+
+       if (!mvm->ucode_loaded)
+               return -EIO;
+
+       if (copy_from_user(buf, user_buf, sizeof(buf)))
+               return -EFAULT;
+
+       if (sscanf(buf, "%d", &allow) != 1)
+               return -EINVAL;
+
+       IWL_DEBUG_POWER(mvm, "%s device power down\n",
+                       allow ? "allow" : "prevent");
+
+       /*
+        * TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it
+        */
+
+       return count;
+}
+
+static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file,
+                                                  const char __user *user_buf,
+                                                  size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char buf[8] = {};
+       int allow;
+
+       if (copy_from_user(buf, user_buf, sizeof(buf)))
+               return -EFAULT;
+
+       if (sscanf(buf, "%d", &allow) != 1)
+               return -EINVAL;
+
+       IWL_DEBUG_POWER(mvm, "%s device power down in d3\n",
+                       allow ? "allow" : "prevent");
+
+       /*
+        * TODO: When WoWLAN FW alive notification happens, driver will send
+        * REPLY_DEBUG_CMD setting power_down_allow flag according to
+        * mvm->prevent_power_down_d3
+        */
+       mvm->prevent_power_down_d3 = !allow;
+
+       return count;
+}
+
+#define MVM_DEBUGFS_READ_FILE_OPS(name)                                        \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+       .read = iwl_dbgfs_##name##_read,                                \
+       .open = iwl_dbgfs_open_file_generic,                            \
+       .llseek = generic_file_llseek,                                  \
+}
+
+#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name)                          \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+       .write = iwl_dbgfs_##name##_write,                              \
+       .read = iwl_dbgfs_##name##_read,                                \
+       .open = iwl_dbgfs_open_file_generic,                            \
+       .llseek = generic_file_llseek,                                  \
+};
+
+#define MVM_DEBUGFS_WRITE_FILE_OPS(name)                               \
+static const struct file_operations iwl_dbgfs_##name##_ops = { \
+       .write = iwl_dbgfs_##name##_write,                              \
+       .open = iwl_dbgfs_open_file_generic,                            \
+       .llseek = generic_file_llseek,                                  \
+};
+
+#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do {                  \
+               if (!debugfs_create_file(#name, mode, parent, mvm,      \
+                                        &iwl_dbgfs_##name##_ops))      \
+                       goto err;                                       \
+       } while (0)
+
+#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do {              \
+               if (!debugfs_create_file(#name, mode, parent, vif,      \
+                                        &iwl_dbgfs_##name##_ops))      \
+                       goto err;                                       \
+       } while (0)
+
+/* Device wide debugfs entries */
+MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush);
+MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram);
+MVM_DEBUGFS_READ_FILE_OPS(stations);
+MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow);
+MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow);
+
+int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
+{
+       char buf[100];
+
+       mvm->debugfs_dir = dbgfs_dir;
+
+       MVM_DEBUGFS_ADD_FILE(tx_flush, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(sta_drain, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
+       MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
+       MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR);
+
+       /*
+        * Create a symlink with mac80211. It will be removed when mac80211
+        * exists (before the opmode exists which removes the target.)
+        */
+       snprintf(buf, 100, "../../%s/%s",
+                dbgfs_dir->d_parent->d_parent->d_name.name,
+                dbgfs_dir->d_parent->d_name.name);
+       if (!debugfs_create_symlink("iwlwifi", mvm->hw->wiphy->debugfsdir, buf))
+               goto err;
+
+       return 0;
+err:
+       IWL_ERR(mvm, "Can't create the mvm debugfs directory\n");
+       return -ENOMEM;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
new file mode 100644 (file)
index 0000000..cf6f9a0
--- /dev/null
@@ -0,0 +1,282 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_d3_h__
+#define __fw_api_d3_h__
+
+/**
+ * enum iwl_d3_wakeup_flags - D3 manager wakeup flags
+ * @IWL_WAKEUP_D3_CONFIG_FW_ERROR: wake up on firmware sysassert
+ */
+enum iwl_d3_wakeup_flags {
+       IWL_WAKEUP_D3_CONFIG_FW_ERROR = BIT(0),
+}; /* D3_MANAGER_WAKEUP_CONFIG_API_E_VER_3 */
+
+/**
+ * struct iwl_d3_manager_config - D3 manager configuration command
+ * @min_sleep_time: minimum sleep time (in usec)
+ * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags
+ *
+ * The structure is used for the D3_CONFIG_CMD command.
+ */
+struct iwl_d3_manager_config {
+       __le32 min_sleep_time;
+       __le32 wakeup_flags;
+} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_3 */
+
+
+/* TODO: OFFLOADS_QUERY_API_S_VER_1 */
+
+/**
+ * enum iwl_d3_proto_offloads - enabled protocol offloads
+ * @IWL_D3_PROTO_OFFLOAD_ARP: ARP data is enabled
+ * @IWL_D3_PROTO_OFFLOAD_NS: NS (Neighbor Solicitation) is enabled
+ */
+enum iwl_proto_offloads {
+       IWL_D3_PROTO_OFFLOAD_ARP = BIT(0),
+       IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
+};
+
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS       2
+
+/**
+ * struct iwl_proto_offload_cmd - ARP/NS offload configuration
+ * @enabled: enable flags
+ * @remote_ipv4_addr: remote address to answer to (or zero if all)
+ * @host_ipv4_addr: our IPv4 address to respond to queries for
+ * @arp_mac_addr: our MAC address for ARP responses
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ *     for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor soliciation response MAC address
+ */
+struct iwl_proto_offload_cmd {
+       __le32 enabled;
+       __be32 remote_ipv4_addr;
+       __be32 host_ipv4_addr;
+       u8 arp_mac_addr[ETH_ALEN];
+       __le16 reserved1;
+
+       u8 remote_ipv6_addr[16];
+       u8 solicited_node_ipv6_addr[16];
+       u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS][16];
+       u8 ndp_mac_addr[ETH_ALEN];
+       __le16 reserved2;
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */
+
+
+/*
+ * WOWLAN_PATTERNS
+ */
+#define IWL_WOWLAN_MIN_PATTERN_LEN     16
+#define IWL_WOWLAN_MAX_PATTERN_LEN     128
+
+struct iwl_wowlan_pattern {
+       u8 mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
+       u8 pattern[IWL_WOWLAN_MAX_PATTERN_LEN];
+       u8 mask_size;
+       u8 pattern_size;
+       __le16 reserved;
+} __packed; /* WOWLAN_PATTERN_API_S_VER_1 */
+
+#define IWL_WOWLAN_MAX_PATTERNS        20
+
+struct iwl_wowlan_patterns_cmd {
+       __le32 n_patterns;
+       struct iwl_wowlan_pattern patterns[];
+} __packed; /* WOWLAN_PATTERN_ARRAY_API_S_VER_1 */
+
+enum iwl_wowlan_wakeup_filters {
+       IWL_WOWLAN_WAKEUP_MAGIC_PACKET                  = BIT(0),
+       IWL_WOWLAN_WAKEUP_PATTERN_MATCH                 = BIT(1),
+       IWL_WOWLAN_WAKEUP_BEACON_MISS                   = BIT(2),
+       IWL_WOWLAN_WAKEUP_LINK_CHANGE                   = BIT(3),
+       IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL                = BIT(4),
+       IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ                 = BIT(5),
+       IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE                = BIT(6),
+       IWL_WOWLAN_WAKEUP_ENABLE_NET_DETECT             = BIT(7),
+       IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT              = BIT(8),
+       IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS              = BIT(9),
+       IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE        = BIT(10),
+       /* BIT(11) reserved */
+       IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET          = BIT(12),
+}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
+
+struct iwl_wowlan_config_cmd {
+       __le32 wakeup_filter;
+       __le16 non_qos_seq;
+       __le16 qos_seq[8];
+       u8 wowlan_ba_teardown_tids;
+       u8 is_11n_connection;
+} __packed; /* WOWLAN_CONFIG_API_S_VER_2 */
+
+/*
+ * WOWLAN_TSC_RSC_PARAMS
+ */
+#define IWL_NUM_RSC    16
+
+struct tkip_sc {
+       __le16 iv16;
+       __le16 pad;
+       __le32 iv32;
+} __packed; /* TKIP_SC_API_U_VER_1 */
+
+struct iwl_tkip_rsc_tsc {
+       struct tkip_sc unicast_rsc[IWL_NUM_RSC];
+       struct tkip_sc multicast_rsc[IWL_NUM_RSC];
+       struct tkip_sc tsc;
+} __packed; /* TKIP_TSC_RSC_API_S_VER_1 */
+
+struct aes_sc {
+       __le64 pn;
+} __packed; /* TKIP_AES_SC_API_U_VER_1 */
+
+struct iwl_aes_rsc_tsc {
+       struct aes_sc unicast_rsc[IWL_NUM_RSC];
+       struct aes_sc multicast_rsc[IWL_NUM_RSC];
+       struct aes_sc tsc;
+} __packed; /* AES_TSC_RSC_API_S_VER_1 */
+
+union iwl_all_tsc_rsc {
+       struct iwl_tkip_rsc_tsc tkip;
+       struct iwl_aes_rsc_tsc aes;
+}; /* ALL_TSC_RSC_API_S_VER_2 */
+
+struct iwl_wowlan_rsc_tsc_params_cmd {
+       union iwl_all_tsc_rsc all_tsc_rsc;
+} __packed; /* ALL_TSC_RSC_API_S_VER_2 */
+
+#define IWL_MIC_KEY_SIZE       8
+struct iwl_mic_keys {
+       u8 tx[IWL_MIC_KEY_SIZE];
+       u8 rx_unicast[IWL_MIC_KEY_SIZE];
+       u8 rx_mcast[IWL_MIC_KEY_SIZE];
+} __packed; /* MIC_KEYS_API_S_VER_1 */
+
+#define IWL_P1K_SIZE           5
+struct iwl_p1k_cache {
+       __le16 p1k[IWL_P1K_SIZE];
+} __packed;
+
+#define IWL_NUM_RX_P1K_CACHE   2
+
+struct iwl_wowlan_tkip_params_cmd {
+       struct iwl_mic_keys mic_keys;
+       struct iwl_p1k_cache tx;
+       struct iwl_p1k_cache rx_uni[IWL_NUM_RX_P1K_CACHE];
+       struct iwl_p1k_cache rx_multi[IWL_NUM_RX_P1K_CACHE];
+} __packed; /* WOWLAN_TKIP_SETTING_API_S_VER_1 */
+
+#define IWL_KCK_MAX_SIZE       32
+#define IWL_KEK_MAX_SIZE       32
+
+struct iwl_wowlan_kek_kck_material_cmd {
+       u8      kck[IWL_KCK_MAX_SIZE];
+       u8      kek[IWL_KEK_MAX_SIZE];
+       __le16  kck_len;
+       __le16  kek_len;
+       __le64  replay_ctr;
+} __packed; /* KEK_KCK_MATERIAL_API_S_VER_2 */
+
+#define RF_KILL_INDICATOR_FOR_WOWLAN   0x87
+
+enum iwl_wowlan_rekey_status {
+       IWL_WOWLAN_REKEY_POST_REKEY = 0,
+       IWL_WOWLAN_REKEY_WHILE_REKEY = 1,
+}; /* WOWLAN_REKEY_STATUS_API_E_VER_1 */
+
+enum iwl_wowlan_wakeup_reason {
+       IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS                       = 0,
+       IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET                       = BIT(0),
+       IWL_WOWLAN_WAKEUP_BY_PATTERN                            = BIT(1),
+       IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON     = BIT(2),
+       IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH            = BIT(3),
+       IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE                  = BIT(4),
+       IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED                  = BIT(5),
+       IWL_WOWLAN_WAKEUP_BY_UCODE_ERROR                        = BIT(6),
+       IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST                      = BIT(7),
+       IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE                 = BIT(8),
+       IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS                 = BIT(9),
+       IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE           = BIT(10),
+       IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL              = BIT(11),
+       IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET             = BIT(12),
+}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
+
+struct iwl_wowlan_status {
+       __le64 replay_ctr;
+       __le16 pattern_number;
+       __le16 non_qos_seq_ctr;
+       __le16 qos_seq_ctr[8];
+       __le32 wakeup_reasons;
+       __le32 rekey_status;
+       __le32 num_of_gtk_rekeys;
+       __le32 transmitted_ndps;
+       __le32 received_beacons;
+       __le32 wake_packet_length;
+       __le32 wake_packet_bufsize;
+       u8 wake_packet[]; /* can be truncated from _length to _bufsize */
+} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
+
+/* TODO: NetDetect API */
+
+#endif /* __fw_api_d3_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
new file mode 100644 (file)
index 0000000..ae39b7d
--- /dev/null
@@ -0,0 +1,369 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_mac_h__
+#define __fw_api_mac_h__
+
+/*
+ * The first MAC indices (starting from 0)
+ * are available to the driver, AUX follows
+ */
+#define MAC_INDEX_AUX          4
+#define MAC_INDEX_MIN_DRIVER   0
+#define NUM_MAC_INDEX_DRIVER   MAC_INDEX_AUX
+
+#define AC_NUM 4 /* Number of access categories */
+
+/**
+ * enum iwl_mac_protection_flags - MAC context flags
+ * @MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames,
+ *     this will require CCK RTS/CTS2self.
+ *     RTS/CTS will protect full burst time.
+ * @MAC_PROT_FLG_HT_PROT: enable HT protection
+ * @MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions
+ * @MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self
+ */
+enum iwl_mac_protection_flags {
+       MAC_PROT_FLG_TGG_PROTECT        = BIT(3),
+       MAC_PROT_FLG_HT_PROT            = BIT(23),
+       MAC_PROT_FLG_FAT_PROT           = BIT(24),
+       MAC_PROT_FLG_SELF_CTS_EN        = BIT(30),
+};
+
+#define MAC_FLG_SHORT_SLOT             BIT(4)
+#define MAC_FLG_SHORT_PREAMBLE         BIT(5)
+
+/**
+ * enum iwl_mac_types - Supported MAC types
+ * @FW_MAC_TYPE_FIRST: lowest supported MAC type
+ * @FW_MAC_TYPE_AUX: Auxiliary MAC (internal)
+ * @FW_MAC_TYPE_LISTENER: monitor MAC type (?)
+ * @FW_MAC_TYPE_PIBSS: Pseudo-IBSS
+ * @FW_MAC_TYPE_IBSS: IBSS
+ * @FW_MAC_TYPE_BSS_STA: BSS (managed) station
+ * @FW_MAC_TYPE_P2P_DEVICE: P2P Device
+ * @FW_MAC_TYPE_P2P_STA: P2P client
+ * @FW_MAC_TYPE_GO: P2P GO
+ * @FW_MAC_TYPE_TEST: ?
+ * @FW_MAC_TYPE_MAX: highest support MAC type
+ */
+enum iwl_mac_types {
+       FW_MAC_TYPE_FIRST = 1,
+       FW_MAC_TYPE_AUX = FW_MAC_TYPE_FIRST,
+       FW_MAC_TYPE_LISTENER,
+       FW_MAC_TYPE_PIBSS,
+       FW_MAC_TYPE_IBSS,
+       FW_MAC_TYPE_BSS_STA,
+       FW_MAC_TYPE_P2P_DEVICE,
+       FW_MAC_TYPE_P2P_STA,
+       FW_MAC_TYPE_GO,
+       FW_MAC_TYPE_TEST,
+       FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST
+}; /* MAC_CONTEXT_TYPE_API_E_VER_1 */
+
+/**
+ * enum iwl_tsf_id - TSF hw timer ID
+ * @TSF_ID_A: use TSF A
+ * @TSF_ID_B: use TSF B
+ * @TSF_ID_C: use TSF C
+ * @TSF_ID_D: use TSF D
+ * @NUM_TSF_IDS: number of TSF timers available
+ */
+enum iwl_tsf_id {
+       TSF_ID_A = 0,
+       TSF_ID_B = 1,
+       TSF_ID_C = 2,
+       TSF_ID_D = 3,
+       NUM_TSF_IDS = 4,
+}; /* TSF_ID_API_E_VER_1 */
+
+/**
+ * struct iwl_mac_data_ap - configuration data for AP MAC context
+ * @beacon_time: beacon transmit time in system time
+ * @beacon_tsf: beacon transmit time in TSF
+ * @bi: beacon interval in TU
+ * @bi_reciprocal: 2^32 / bi
+ * @dtim_interval: dtim transmit time in TU
+ * @dtim_reciprocal: 2^32 / dtim_interval
+ * @mcast_qid: queue ID for multicast traffic
+ * @beacon_template: beacon template ID
+ */
+struct iwl_mac_data_ap {
+       __le32 beacon_time;
+       __le64 beacon_tsf;
+       __le32 bi;
+       __le32 bi_reciprocal;
+       __le32 dtim_interval;
+       __le32 dtim_reciprocal;
+       __le32 mcast_qid;
+       __le32 beacon_template;
+} __packed; /* AP_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_ibss - configuration data for IBSS MAC context
+ * @beacon_time: beacon transmit time in system time
+ * @beacon_tsf: beacon transmit time in TSF
+ * @bi: beacon interval in TU
+ * @bi_reciprocal: 2^32 / bi
+ */
+struct iwl_mac_data_ibss {
+       __le32 beacon_time;
+       __le64 beacon_tsf;
+       __le32 bi;
+       __le32 bi_reciprocal;
+} __packed; /* IBSS_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_sta - configuration data for station MAC context
+ * @is_assoc: 1 for associated state, 0 otherwise
+ * @dtim_time: DTIM arrival time in system time
+ * @dtim_tsf: DTIM arrival time in TSF
+ * @bi: beacon interval in TU, applicable only when associated
+ * @bi_reciprocal: 2^32 / bi , applicable only when associated
+ * @dtim_interval: DTIM interval in TU, applicable only when associated
+ * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated
+ * @listen_interval: in beacon intervals, applicable only when associated
+ * @assoc_id: unique ID assigned by the AP during association
+ */
+struct iwl_mac_data_sta {
+       __le32 is_assoc;
+       __le32 dtim_time;
+       __le64 dtim_tsf;
+       __le32 bi;
+       __le32 bi_reciprocal;
+       __le32 dtim_interval;
+       __le32 dtim_reciprocal;
+       __le32 listen_interval;
+       __le32 assoc_id;
+       __le32 assoc_beacon_arrive_time;
+} __packed; /* STA_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_go - configuration data for P2P GO MAC context
+ * @ap: iwl_mac_data_ap struct with most config data
+ * @ctwin: client traffic window in TU (period after TBTT when GO is present).
+ *     0 indicates that there is no CT window.
+ * @opp_ps_enabled: indicate that opportunistic PS allowed
+ */
+struct iwl_mac_data_go {
+       struct iwl_mac_data_ap ap;
+       __le32 ctwin;
+       __le32 opp_ps_enabled;
+} __packed; /* GO_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_p2p_sta - configuration data for P2P client MAC context
+ * @sta: iwl_mac_data_sta struct with most config data
+ * @ctwin: client traffic window in TU (period after TBTT when GO is present).
+ *     0 indicates that there is no CT window.
+ */
+struct iwl_mac_data_p2p_sta {
+       struct iwl_mac_data_sta sta;
+       __le32 ctwin;
+} __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_data_pibss - Pseudo IBSS config data
+ * @stats_interval: interval in TU between statistics notifications to host.
+ */
+struct iwl_mac_data_pibss {
+       __le32 stats_interval;
+} __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */
+
+/*
+ * struct iwl_mac_data_p2p_dev - configuration data for the P2P Device MAC
+ * context.
+ * @is_disc_extended: if set to true, P2P Device discoverability is enabled on
+ *     other channels as well. This should be to true only in case that the
+ *     device is discoverable and there is an active GO. Note that setting this
+ *     field when not needed, will increase the number of interrupts and have
+ *     effect on the platform power, as this setting opens the Rx filters on
+ *     all macs.
+ */
+struct iwl_mac_data_p2p_dev {
+       __le32 is_disc_extended;
+} __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */
+
+/**
+ * enum iwl_mac_filter_flags - MAC context filter flags
+ * @MAC_FILTER_IN_PROMISC: accept all data frames
+ * @MAC_FILTER_IN_CONTROL_AND_MGMT: pass all mangement and
+ *     control frames to the host
+ * @MAC_FILTER_ACCEPT_GRP: accept multicast frames
+ * @MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames
+ * @MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames
+ * @MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host
+ *     (in station mode when associated)
+ * @MAC_FILTER_OUT_BCAST: filter out all broadcast frames
+ * @MAC_FILTER_IN_CRC32: extract FCS and append it to frames
+ * @MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host
+ */
+enum iwl_mac_filter_flags {
+       MAC_FILTER_IN_PROMISC           = BIT(0),
+       MAC_FILTER_IN_CONTROL_AND_MGMT  = BIT(1),
+       MAC_FILTER_ACCEPT_GRP           = BIT(2),
+       MAC_FILTER_DIS_DECRYPT          = BIT(3),
+       MAC_FILTER_DIS_GRP_DECRYPT      = BIT(4),
+       MAC_FILTER_IN_BEACON            = BIT(6),
+       MAC_FILTER_OUT_BCAST            = BIT(8),
+       MAC_FILTER_IN_CRC32             = BIT(11),
+       MAC_FILTER_IN_PROBE_REQUEST     = BIT(12),
+};
+
+/**
+ * enum iwl_mac_qos_flags - QoS flags
+ * @MAC_QOS_FLG_UPDATE_EDCA: ?
+ * @MAC_QOS_FLG_TGN: HT is enabled
+ * @MAC_QOS_FLG_TXOP_TYPE: ?
+ *
+ */
+enum iwl_mac_qos_flags {
+       MAC_QOS_FLG_UPDATE_EDCA = BIT(0),
+       MAC_QOS_FLG_TGN         = BIT(1),
+       MAC_QOS_FLG_TXOP_TYPE   = BIT(4),
+};
+
+/**
+ * struct iwl_ac_qos - QOS timing params for MAC_CONTEXT_CMD
+ * @cw_min: Contention window, start value in numbers of slots.
+ *     Should be a power-of-2, minus 1.  Device's default is 0x0f.
+ * @cw_max: Contention window, max value in numbers of slots.
+ *     Should be a power-of-2, minus 1.  Device's default is 0x3f.
+ * @aifsn:  Number of slots in Arbitration Interframe Space (before
+ *     performing random backoff timing prior to Tx).  Device default 1.
+ * @fifos_mask: FIFOs used by this MAC for this AC
+ * @edca_txop:  Length of Tx opportunity, in uSecs.  Device default is 0.
+ *
+ * One instance of this config struct for each of 4 EDCA access categories
+ * in struct iwl_qosparam_cmd.
+ *
+ * Device will automatically increase contention window by (2*CW) + 1 for each
+ * transmission retry.  Device uses cw_max as a bit mask, ANDed with new CW
+ * value, to cap the CW value.
+ */
+struct iwl_ac_qos {
+       __le16 cw_min;
+       __le16 cw_max;
+       u8 aifsn;
+       u8 fifos_mask;
+       __le16 edca_txop;
+} __packed; /* AC_QOS_API_S_VER_2 */
+
+/**
+ * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts
+ * ( MAC_CONTEXT_CMD = 0x28 )
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @mac_type: one of FW_MAC_TYPE_*
+ * @tsd_id: TSF HW timer, one of TSF_ID_*
+ * @node_addr: MAC address
+ * @bssid_addr: BSSID
+ * @cck_rates: basic rates available for CCK
+ * @ofdm_rates: basic rates available for OFDM
+ * @protection_flags: combination of MAC_PROT_FLG_FLAG_*
+ * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise
+ * @short_slot: 0x10 for enabling short slots, 0 otherwise
+ * @filter_flags: combination of MAC_FILTER_*
+ * @qos_flags: from MAC_QOS_FLG_*
+ * @ac: one iwl_mac_qos configuration for each AC
+ * @mac_specific: one of struct iwl_mac_data_*, according to mac_type
+ */
+struct iwl_mac_ctx_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       /* MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */
+       __le32 mac_type;
+       __le32 tsf_id;
+       u8 node_addr[6];
+       __le16 reserved_for_node_addr;
+       u8 bssid_addr[6];
+       __le16 reserved_for_bssid_addr;
+       __le32 cck_rates;
+       __le32 ofdm_rates;
+       __le32 protection_flags;
+       __le32 cck_short_preamble;
+       __le32 short_slot;
+       __le32 filter_flags;
+       /* MAC_QOS_PARAM_API_S_VER_1 */
+       __le32 qos_flags;
+       struct iwl_ac_qos ac[AC_NUM+1];
+       /* MAC_CONTEXT_COMMON_DATA_API_S */
+       union {
+               struct iwl_mac_data_ap ap;
+               struct iwl_mac_data_go go;
+               struct iwl_mac_data_sta sta;
+               struct iwl_mac_data_p2p_sta p2p_sta;
+               struct iwl_mac_data_p2p_dev p2p_dev;
+               struct iwl_mac_data_pibss pibss;
+               struct iwl_mac_data_ibss ibss;
+       };
+} __packed; /* MAC_CONTEXT_CMD_API_S_VER_1 */
+
+static inline u32 iwl_mvm_reciprocal(u32 v)
+{
+       if (!v)
+               return 0;
+       return 0xFFFFFFFF / v;
+}
+
+#endif /* __fw_api_mac_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
new file mode 100644 (file)
index 0000000..be36b76
--- /dev/null
@@ -0,0 +1,140 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_power_h__
+#define __fw_api_power_h__
+
+/* Power Management Commands, Responses, Notifications */
+
+/**
+ * enum iwl_scan_flags - masks for power table command flags
+ * @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
+ *             '1' Driver enables PM (use rest of parameters)
+ * @POWER_FLAGS_SLEEP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
+ *             '1' PM could sleep over DTIM till listen Interval.
+ * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
+ * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
+ *             access categories are both delivery and trigger enabled.
+ * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
+ *             PBW Snoozing enabled
+ * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
+*/
+enum iwl_power_flags {
+       POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK    = BIT(0),
+       POWER_FLAGS_SLEEP_OVER_DTIM_MSK         = BIT(1),
+       POWER_FLAGS_LPRX_ENA_MSK                = BIT(2),
+       POWER_FLAGS_SNOOZE_ENA_MSK              = BIT(3),
+       POWER_FLAGS_BT_SCO_ENA                  = BIT(4),
+       POWER_FLAGS_ADVANCE_PM_ENA_MSK          = BIT(5)
+};
+
+/**
+ * struct iwl_powertable_cmd - Power Table Command
+ * POWER_TABLE_CMD = 0x77 (command, has simple generic response)
+ *
+ * @id_and_color:      MAC contex identifier
+ * @action:            Action on context - no action, add new,
+ *                     modify existent, remove
+ * @flags:             Power table command flags from POWER_FLAGS_*
+ * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
+ *                     Minimum allowed:- 3 * DTIM
+ * @rx_data_timeout:    Minimum time (usec) from last Rx packet for AM to
+ *                     PSM transition - legacy PM
+ * @tx_data_timeout:    Minimum time (usec) from last Tx packet for AM to
+ *                     PSM transition - legacy PM
+ * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
+ *                     PSM transition - uAPSD
+ * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
+ *                     PSM transition - uAPSD
+ * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
+ *                     Default: 80dbm
+ * @num_skip_dtim:      Number of DTIMs to skip if Skip over DTIM flag is set
+ * @snooze_interval:    TBD
+ * @snooze_window:      TBD
+ * @snooze_step:        TBD
+ * @qndp_tid:           TBD
+ * @uapsd_ac_flags:     TBD
+ * @uapsd_max_sp:       TBD
+ */
+struct iwl_powertable_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       __le16 flags;
+       u8 reserved;
+       __le16 keep_alive_seconds;
+       __le32 rx_data_timeout;
+       __le32 tx_data_timeout;
+       __le32 rx_data_timeout_uapsd;
+       __le32 tx_data_timeout_uapsd;
+       u8 lprx_rssi_threshold;
+       u8 num_skip_dtim;
+       __le16 snooze_interval;
+       __le16 snooze_window;
+       u8 snooze_step;
+       u8 qndp_tid;
+       u8 uapsd_ac_flags;
+       u8 uapsd_max_sp;
+} __packed;
+
+#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
new file mode 100644 (file)
index 0000000..aa3474d
--- /dev/null
@@ -0,0 +1,312 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_rs_h__
+#define __fw_api_rs_h__
+
+#include "fw-api-mac.h"
+
+/*
+ * These serve as indexes into
+ * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT];
+ */
+enum {
+       IWL_RATE_1M_INDEX = 0,
+       IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX,
+       IWL_RATE_2M_INDEX,
+       IWL_RATE_5M_INDEX,
+       IWL_RATE_11M_INDEX,
+       IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX,
+       IWL_RATE_6M_INDEX,
+       IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX,
+       IWL_RATE_9M_INDEX,
+       IWL_RATE_12M_INDEX,
+       IWL_RATE_18M_INDEX,
+       IWL_RATE_24M_INDEX,
+       IWL_RATE_36M_INDEX,
+       IWL_RATE_48M_INDEX,
+       IWL_RATE_54M_INDEX,
+       IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX,
+       IWL_RATE_60M_INDEX,
+       IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX,
+       IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1,
+       IWL_RATE_COUNT,
+};
+
+#define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX)
+
+/* fw API values for legacy bit rates, both OFDM and CCK */
+enum {
+       IWL_RATE_6M_PLCP  = 13,
+       IWL_RATE_9M_PLCP  = 15,
+       IWL_RATE_12M_PLCP = 5,
+       IWL_RATE_18M_PLCP = 7,
+       IWL_RATE_24M_PLCP = 9,
+       IWL_RATE_36M_PLCP = 11,
+       IWL_RATE_48M_PLCP = 1,
+       IWL_RATE_54M_PLCP = 3,
+       IWL_RATE_1M_PLCP  = 10,
+       IWL_RATE_2M_PLCP  = 20,
+       IWL_RATE_5M_PLCP  = 55,
+       IWL_RATE_11M_PLCP = 110,
+};
+
+/*
+ * rate_n_flags bit fields
+ *
+ * The 32-bit value has different layouts in the low 8 bites depending on the
+ * format. There are three formats, HT, VHT and legacy (11abg, with subformats
+ * for CCK and OFDM).
+ *
+ * High-throughput (HT) rate format
+ *     bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM)
+ * Very High-throughput (VHT) rate format
+ *     bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM)
+ * Legacy OFDM rate format for bits 7:0
+ *     bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM)
+ * Legacy CCK rate format for bits 7:0:
+ *     bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK)
+ */
+
+/* Bit 8: (1) HT format, (0) legacy or VHT format */
+#define RATE_MCS_HT_POS 8
+#define RATE_MCS_HT_MSK (1 << RATE_MCS_HT_POS)
+
+/* Bit 9: (1) CCK, (0) OFDM.  HT (bit 8) must be "0" for this bit to be valid */
+#define RATE_MCS_CCK_POS 9
+#define RATE_MCS_CCK_MSK (1 << RATE_MCS_CCK_POS)
+
+/* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */
+#define RATE_MCS_VHT_POS 26
+#define RATE_MCS_VHT_MSK (1 << RATE_MCS_VHT_POS)
+
+
+/*
+ * High-throughput (HT) rate format for bits 7:0
+ *
+ *  2-0:  MCS rate base
+ *        0)   6 Mbps
+ *        1)  12 Mbps
+ *        2)  18 Mbps
+ *        3)  24 Mbps
+ *        4)  36 Mbps
+ *        5)  48 Mbps
+ *        6)  54 Mbps
+ *        7)  60 Mbps
+ *  4-3:  0)  Single stream (SISO)
+ *        1)  Dual stream (MIMO)
+ *        2)  Triple stream (MIMO)
+ *    5:  Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data
+ *  (bits 7-6 are zero)
+ *
+ * Together the low 5 bits work out to the MCS index because we don't
+ * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two
+ * streams and 16-23 have three streams. We could also support MCS 32
+ * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.)
+ */
+#define RATE_HT_MCS_RATE_CODE_MSK      0x7
+
+/* Bit 10: (1) Use Green Field preamble */
+#define RATE_HT_MCS_GF_POS             10
+#define RATE_HT_MCS_GF_MSK             (1 << RATE_HT_MCS_GF_POS)
+
+#define RATE_HT_MCS_INDEX_MSK          0x3f
+
+/*
+ * Very High-throughput (VHT) rate format for bits 7:0
+ *
+ *  3-0:  VHT MCS (0-9)
+ *  5-4:  number of streams - 1:
+ *        0)  Single stream (SISO)
+ *        1)  Dual stream (MIMO)
+ *        2)  Triple stream (MIMO)
+ */
+
+/* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */
+#define RATE_VHT_MCS_RATE_CODE_MSK     0xf
+#define RATE_VHT_MCS_NSS_POS           4
+#define RATE_VHT_MCS_NSS_MSK           (3 << RATE_VHT_MCS_NSS_POS)
+
+/*
+ * Legacy OFDM rate format for bits 7:0
+ *
+ *  3-0:  0xD)   6 Mbps
+ *        0xF)   9 Mbps
+ *        0x5)  12 Mbps
+ *        0x7)  18 Mbps
+ *        0x9)  24 Mbps
+ *        0xB)  36 Mbps
+ *        0x1)  48 Mbps
+ *        0x3)  54 Mbps
+ * (bits 7-4 are 0)
+ *
+ * Legacy CCK rate format for bits 7:0:
+ * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK):
+ *
+ *  6-0:   10)  1 Mbps
+ *         20)  2 Mbps
+ *         55)  5.5 Mbps
+ *        110)  11 Mbps
+ * (bit 7 is 0)
+ */
+#define RATE_LEGACY_RATE_MSK 0xff
+
+
+/*
+ * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz
+ * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT
+ */
+#define RATE_MCS_CHAN_WIDTH_POS                11
+#define RATE_MCS_CHAN_WIDTH_MSK                (3 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_20         (0 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_40         (1 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_80         (2 << RATE_MCS_CHAN_WIDTH_POS)
+#define RATE_MCS_CHAN_WIDTH_160                (3 << RATE_MCS_CHAN_WIDTH_POS)
+
+/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */
+#define RATE_MCS_SGI_POS               13
+#define RATE_MCS_SGI_MSK               (1 << RATE_MCS_SGI_POS)
+
+/* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */
+#define RATE_MCS_ANT_POS               14
+#define RATE_MCS_ANT_A_MSK             (1 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_B_MSK             (2 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_C_MSK             (4 << RATE_MCS_ANT_POS)
+#define RATE_MCS_ANT_AB_MSK            (RATE_MCS_ANT_A_MSK | \
+                                        RATE_MCS_ANT_B_MSK)
+#define RATE_MCS_ANT_ABC_MSK           (RATE_MCS_ANT_AB_MSK | \
+                                        RATE_MCS_ANT_C_MSK)
+#define RATE_MCS_ANT_MSK               RATE_MCS_ANT_ABC_MSK
+#define RATE_MCS_ANT_NUM 3
+
+/* Bit 17-18: (0) SS, (1) SS*2 */
+#define RATE_MCS_STBC_POS              17
+#define RATE_MCS_STBC_MSK              (1 << RATE_MCS_STBC_POS)
+
+/* Bit 19: (0) Beamforming is off, (1) Beamforming is on */
+#define RATE_MCS_BF_POS                        19
+#define RATE_MCS_BF_MSK                        (1 << RATE_MCS_BF_POS)
+
+/* Bit 20: (0) ZLF is off, (1) ZLF is on */
+#define RATE_MCS_ZLF_POS               20
+#define RATE_MCS_ZLF_MSK               (1 << RATE_MCS_ZLF_POS)
+
+/* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */
+#define RATE_MCS_DUP_POS               24
+#define RATE_MCS_DUP_MSK               (3 << RATE_MCS_DUP_POS)
+
+/* Bit 27: (1) LDPC enabled, (0) LDPC disabled */
+#define RATE_MCS_LDPC_POS              27
+#define RATE_MCS_LDPC_MSK              (1 << RATE_MCS_LDPC_POS)
+
+
+/* Link Quality definitions */
+
+/* # entries in rate scale table to support Tx retries */
+#define  LQ_MAX_RETRY_NUM 16
+
+/* Link quality command flags, only this one is available */
+#define  LQ_FLAG_SET_STA_TLC_RTS_MSK   BIT(0)
+
+/**
+ * struct iwl_lq_cmd - link quality command
+ * @sta_id: station to update
+ * @control: not used
+ * @flags: combination of LQ_FLAG_*
+ * @mimo_delim: the first SISO index in rs_table, which separates MIMO
+ *     and SISO rates
+ * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD).
+ *     Should be ANT_[ABC]
+ * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC]
+ * @initial_rate_index: first index from rs_table per AC category
+ * @agg_time_limit: aggregation max time threshold in usec/100, meaning
+ *     value of 100 is one usec. Range is 100 to 8000
+ * @agg_disable_start_th: try-count threshold for starting aggregation.
+ *     If a frame has higher try-count, it should not be selected for
+ *     starting an aggregation sequence.
+ * @agg_frame_cnt_limit: max frame count in an aggregation.
+ *     0: no limit
+ *     1: no aggregation (one frame per aggregation)
+ *     2 - 0x3f: maximal number of frames (up to 3f == 63)
+ * @rs_table: array of rates for each TX try, each is rate_n_flags,
+ *     meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP
+ * @bf_params: beam forming params, currently not used
+ */
+struct iwl_lq_cmd {
+       u8 sta_id;
+       u8 reserved1;
+       u16 control;
+       /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */
+       u8 flags;
+       u8 mimo_delim;
+       u8 single_stream_ant_msk;
+       u8 dual_stream_ant_msk;
+       u8 initial_rate_index[AC_NUM];
+       /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */
+       __le16 agg_time_limit;
+       u8 agg_disable_start_th;
+       u8 agg_frame_cnt_limit;
+       __le32 reserved2;
+       __le32 rs_table[LQ_MAX_RETRY_NUM];
+       __le32 bf_params;
+}; /* LINK_QUALITY_CMD_API_S_VER_1 */
+#endif /* __fw_api_rs_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
new file mode 100644 (file)
index 0000000..670ac8f
--- /dev/null
@@ -0,0 +1,561 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_scan_h__
+#define __fw_api_scan_h__
+
+#include "fw-api.h"
+
+/* Scan Commands, Responses, Notifications */
+
+/* Masks for iwl_scan_channel.type flags */
+#define SCAN_CHANNEL_TYPE_PASSIVE      0
+#define SCAN_CHANNEL_TYPE_ACTIVE       BIT(0)
+#define SCAN_CHANNEL_NARROW_BAND       BIT(22)
+
+/* Max number of IEs for direct SSID scans in a command */
+#define PROBE_OPTION_MAX               20
+
+/**
+ * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table
+ * @channel: band is selected by iwl_scan_cmd "flags" field
+ * @tx_gain: gain for analog radio
+ * @dsp_atten: gain for DSP
+ * @active_dwell: dwell time for active scan in TU, typically 5-50
+ * @passive_dwell: dwell time for passive scan in TU, typically 20-500
+ * @type: type is broken down to these bits:
+ *     bit 0: 0 = passive, 1 = active
+ *     bits 1-20: SSID direct bit map. If any of these bits is set then
+ *             the corresponding SSID IE is transmitted in probe request
+ *             (bit i adds IE in position i to the probe request)
+ *     bit 22: channel width, 0 = regular, 1 = TGj narrow channel
+ *
+ * @iteration_count:
+ * @iteration_interval:
+ * This struct is used once for each channel in the scan list.
+ * Each channel can independently select:
+ * 1)  SSID for directed active scans
+ * 2)  Txpower setting (for rate specified within Tx command)
+ * 3)  How long to stay on-channel (behavior may be modified by quiet_time,
+ *     quiet_plcp_th, good_CRC_th)
+ *
+ * To avoid uCode errors, make sure the following are true (see comments
+ * under struct iwl_scan_cmd about max_out_time and quiet_time):
+ * 1)  If using passive_dwell (i.e. passive_dwell != 0):
+ *     active_dwell <= passive_dwell (< max_out_time if max_out_time != 0)
+ * 2)  quiet_time <= active_dwell
+ * 3)  If restricting off-channel time (i.e. max_out_time !=0):
+ *     passive_dwell < max_out_time
+ *     active_dwell < max_out_time
+ */
+struct iwl_scan_channel {
+       __le32 type;
+       __le16 channel;
+       __le16 iteration_count;
+       __le32 iteration_interval;
+       __le16 active_dwell;
+       __le16 passive_dwell;
+} __packed; /* SCAN_CHANNEL_CONTROL_API_S_VER_1 */
+
+/**
+ * struct iwl_ssid_ie - directed scan network information element
+ *
+ * Up to 20 of these may appear in REPLY_SCAN_CMD,
+ * selected by "type" bit field in struct iwl_scan_channel;
+ * each channel may select different ssids from among the 20 entries.
+ * SSID IEs get transmitted in reverse order of entry.
+ */
+struct iwl_ssid_ie {
+       u8 id;
+       u8 len;
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
+
+/**
+ * iwl_scan_flags - masks for scan command flags
+ *@SCAN_FLAGS_PERIODIC_SCAN:
+ *@SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX:
+ *@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
+ *@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
+ *@SCAN_FLAGS_FRAGMENTED_SCAN:
+ */
+enum iwl_scan_flags {
+       SCAN_FLAGS_PERIODIC_SCAN                = BIT(0),
+       SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX   = BIT(1),
+       SCAN_FLAGS_DELAYED_SCAN_LOWBAND         = BIT(2),
+       SCAN_FLAGS_DELAYED_SCAN_HIGHBAND        = BIT(3),
+       SCAN_FLAGS_FRAGMENTED_SCAN              = BIT(4),
+};
+
+/**
+ * enum iwl_scan_type - Scan types for scan command
+ * @SCAN_TYPE_FORCED:
+ * @SCAN_TYPE_BACKGROUND:
+ * @SCAN_TYPE_OS:
+ * @SCAN_TYPE_ROAMING:
+ * @SCAN_TYPE_ACTION:
+ * @SCAN_TYPE_DISCOVERY:
+ * @SCAN_TYPE_DISCOVERY_FORCED:
+ */
+enum iwl_scan_type {
+       SCAN_TYPE_FORCED                = 0,
+       SCAN_TYPE_BACKGROUND            = 1,
+       SCAN_TYPE_OS                    = 2,
+       SCAN_TYPE_ROAMING               = 3,
+       SCAN_TYPE_ACTION                = 4,
+       SCAN_TYPE_DISCOVERY             = 5,
+       SCAN_TYPE_DISCOVERY_FORCED      = 6,
+}; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
+
+/* Maximal number of channels to scan */
+#define MAX_NUM_SCAN_CHANNELS 0x24
+
+/**
+ * struct iwl_scan_cmd - scan request command
+ * ( SCAN_REQUEST_CMD = 0x80 )
+ * @len: command length in bytes
+ * @scan_flags: scan flags from SCAN_FLAGS_*
+ * @channel_count: num of channels in channel list (1 - MAX_NUM_SCAN_CHANNELS)
+ * @quiet_time: in msecs, dwell this time for active scan on quiet channels
+ * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
+ *     this number of packets were received (typically 1)
+ * @passive2active: is auto switching from passive to active allowed (0 or 1)
+ * @rxchain_sel_flags: RXON_RX_CHAIN_*
+ * @max_out_time: in usecs, max out of serving channel time
+ * @suspend_time: how long to pause scan when returning to service channel:
+ *     bits 0-19: beacon interal in usecs (suspend before executing)
+ *     bits 20-23: reserved
+ *     bits 24-31: number of beacons (suspend between channels)
+ * @rxon_flags: RXON_FLG_*
+ * @filter_flags: RXON_FILTER_*
+ * @tx_cmd: for active scans (zero for passive), w/o payload,
+ *     no RS so specify TX rate
+ * @direct_scan: direct scan SSIDs
+ * @type: one of SCAN_TYPE_*
+ * @repeats: how many time to repeat the scan
+ */
+struct iwl_scan_cmd {
+       __le16 len;
+       u8 scan_flags;
+       u8 channel_count;
+       __le16 quiet_time;
+       __le16 quiet_plcp_th;
+       __le16 passive2active;
+       __le16 rxchain_sel_flags;
+       __le32 max_out_time;
+       __le32 suspend_time;
+       /* RX_ON_FLAGS_API_S_VER_1 */
+       __le32 rxon_flags;
+       __le32 filter_flags;
+       struct iwl_tx_cmd tx_cmd;
+       struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+       __le32 type;
+       __le32 repeats;
+
+       /*
+        * Probe request frame, followed by channel list.
+        *
+        * Size of probe request frame is specified by byte count in tx_cmd.
+        * Channel list follows immediately after probe request frame.
+        * Number of channels in list is specified by channel_count.
+        * Each channel in list is of type:
+        *
+        * struct iwl_scan_channel channels[0];
+        *
+        * NOTE:  Only one band of channels can be scanned per pass.  You
+        * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait
+        * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION)
+        * before requesting another scan.
+        */
+       u8 data[0];
+} __packed; /* SCAN_REQUEST_FIXED_PART_API_S_VER_5 */
+
+/* Response to scan request contains only status with one of these values */
+#define SCAN_RESPONSE_OK       0x1
+#define SCAN_RESPONSE_ERROR    0x2
+
+/*
+ * SCAN_ABORT_CMD = 0x81
+ * When scan abort is requested, the command has no fields except the common
+ * header. The response contains only a status with one of these values.
+ */
+#define SCAN_ABORT_POSSIBLE    0x1
+#define SCAN_ABORT_IGNORED     0x2 /* no pending scans */
+
+/* TODO: complete documentation */
+#define  SCAN_OWNER_STATUS 0x1
+#define  MEASURE_OWNER_STATUS 0x2
+
+/**
+ * struct iwl_scan_start_notif - notifies start of scan in the device
+ * ( SCAN_START_NOTIFICATION = 0x82 )
+ * @tsf_low: TSF timer (lower half) in usecs
+ * @tsf_high: TSF timer (higher half) in usecs
+ * @beacon_timer: structured as follows:
+ *     bits 0:19 - beacon interval in usecs
+ *     bits 20:23 - reserved (0)
+ *     bits 24:31 - number of beacons
+ * @channel: which channel is scanned
+ * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
+ * @status: one of *_OWNER_STATUS
+ */
+struct iwl_scan_start_notif {
+       __le32 tsf_low;
+       __le32 tsf_high;
+       __le32 beacon_timer;
+       u8 channel;
+       u8 band;
+       u8 reserved[2];
+       __le32 status;
+} __packed; /* SCAN_START_NTF_API_S_VER_1 */
+
+/* scan results probe_status first bit indicates success */
+#define SCAN_PROBE_STATUS_OK           0
+#define SCAN_PROBE_STATUS_TX_FAILED    BIT(0)
+/* error statuses combined with TX_FAILED */
+#define SCAN_PROBE_STATUS_FAIL_TTL     BIT(1)
+#define SCAN_PROBE_STATUS_FAIL_BT      BIT(2)
+
+/* How many statistics are gathered for each channel */
+#define SCAN_RESULTS_STATISTICS 1
+
+/**
+ * enum iwl_scan_complete_status - status codes for scan complete notifications
+ * @SCAN_COMP_STATUS_OK:  scan completed successfully
+ * @SCAN_COMP_STATUS_ABORT: scan was aborted by user
+ * @SCAN_COMP_STATUS_ERR_SLEEP: sending null sleep packet failed
+ * @SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT: timeout before channel is ready
+ * @SCAN_COMP_STATUS_ERR_PROBE: sending probe request failed
+ * @SCAN_COMP_STATUS_ERR_WAKEUP: sending null wakeup packet failed
+ * @SCAN_COMP_STATUS_ERR_ANTENNAS: invalid antennas chosen at scan command
+ * @SCAN_COMP_STATUS_ERR_INTERNAL: internal error caused scan abort
+ * @SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax
+ * @SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful
+ *     (not an error!)
+ * @SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver
+ *     asked for
+ * @SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events
+*/
+enum iwl_scan_complete_status {
+       SCAN_COMP_STATUS_OK = 0x1,
+       SCAN_COMP_STATUS_ABORT = 0x2,
+       SCAN_COMP_STATUS_ERR_SLEEP = 0x3,
+       SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT = 0x4,
+       SCAN_COMP_STATUS_ERR_PROBE = 0x5,
+       SCAN_COMP_STATUS_ERR_WAKEUP = 0x6,
+       SCAN_COMP_STATUS_ERR_ANTENNAS = 0x7,
+       SCAN_COMP_STATUS_ERR_INTERNAL = 0x8,
+       SCAN_COMP_STATUS_ERR_COEX = 0x9,
+       SCAN_COMP_STATUS_P2P_ACTION_OK = 0xA,
+       SCAN_COMP_STATUS_ITERATION_END = 0x0B,
+       SCAN_COMP_STATUS_ERR_ALLOC_TE = 0x0C,
+};
+
+/**
+ * struct iwl_scan_results_notif - scan results for one channel
+ * ( SCAN_RESULTS_NOTIFICATION = 0x83 )
+ * @channel: which channel the results are from
+ * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
+ * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request
+ * @num_probe_not_sent: # of request that weren't sent due to not enough time
+ * @duration: duration spent in channel, in usecs
+ * @statistics: statistics gathered for this channel
+ */
+struct iwl_scan_results_notif {
+       u8 channel;
+       u8 band;
+       u8 probe_status;
+       u8 num_probe_not_sent;
+       __le32 duration;
+       __le32 statistics[SCAN_RESULTS_STATISTICS];
+} __packed; /* SCAN_RESULT_NTF_API_S_VER_2 */
+
+/**
+ * struct iwl_scan_complete_notif - notifies end of scanning (all channels)
+ * ( SCAN_COMPLETE_NOTIFICATION = 0x84 )
+ * @scanned_channels: number of channels scanned (and number of valid results)
+ * @status: one of SCAN_COMP_STATUS_*
+ * @bt_status: BT on/off status
+ * @last_channel: last channel that was scanned
+ * @tsf_low: TSF timer (lower half) in usecs
+ * @tsf_high: TSF timer (higher half) in usecs
+ * @results: all scan results, only "scanned_channels" of them are valid
+ */
+struct iwl_scan_complete_notif {
+       u8 scanned_channels;
+       u8 status;
+       u8 bt_status;
+       u8 last_channel;
+       __le32 tsf_low;
+       __le32 tsf_high;
+       struct iwl_scan_results_notif results[MAX_NUM_SCAN_CHANNELS];
+} __packed; /* SCAN_COMPLETE_NTF_API_S_VER_2 */
+
+/* scan offload */
+#define IWL_MAX_SCAN_CHANNELS          40
+#define IWL_SCAN_MAX_BLACKLIST_LEN     64
+#define IWL_SCAN_MAX_PROFILES          11
+#define SCAN_OFFLOAD_PROBE_REQ_SIZE    512
+
+/* Default watchdog (in MS) for scheduled scan iteration */
+#define IWL_SCHED_SCAN_WATCHDOG cpu_to_le16(15000)
+
+#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1)
+#define CAN_ABORT_STATUS 1
+
+#define IWL_FULL_SCAN_MULTIPLIER 5
+#define IWL_FAST_SCHED_SCAN_ITERATIONS 3
+
+/**
+ * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6
+ * @scan_flags:                see enum iwl_scan_flags
+ * @channel_count:     channels in channel list
+ * @quiet_time:                dwell time, in milisiconds, on quiet channel
+ * @quiet_plcp_th:     quiet channel num of packets threshold
+ * @good_CRC_th:       passive to active promotion threshold
+ * @rx_chain:          RXON rx chain.
+ * @max_out_time:      max uSec to be out of assoceated channel
+ * @suspend_time:      pause scan this long when returning to service channel
+ * @flags:             RXON flags
+ * @filter_flags:      RXONfilter
+ * @tx_cmd:            tx command for active scan; for 2GHz and for 5GHz.
+ * @direct_scan:       list of SSIDs for directed active scan
+ * @scan_type:         see enum iwl_scan_type.
+ * @rep_count:         repetition count for each scheduled scan iteration.
+ */
+struct iwl_scan_offload_cmd {
+       __le16 len;
+       u8 scan_flags;
+       u8 channel_count;
+       __le16 quiet_time;
+       __le16 quiet_plcp_th;
+       __le16 good_CRC_th;
+       __le16 rx_chain;
+       __le32 max_out_time;
+       __le32 suspend_time;
+       /* RX_ON_FLAGS_API_S_VER_1 */
+       __le32 flags;
+       __le32 filter_flags;
+       struct iwl_tx_cmd tx_cmd[2];
+       /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
+       struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+       __le32 scan_type;
+       __le32 rep_count;
+} __packed;
+
+enum iwl_scan_offload_channel_flags {
+       IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE         = BIT(0),
+       IWL_SCAN_OFFLOAD_CHANNEL_NARROW         = BIT(22),
+       IWL_SCAN_OFFLOAD_CHANNEL_FULL           = BIT(24),
+       IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL        = BIT(25),
+};
+
+/**
+ * iwl_scan_channel_cfg - SCAN_CHANNEL_CFG_S
+ * @type:              bitmap - see enum iwl_scan_offload_channel_flags.
+ *                     0:      passive (0) or active (1) scan.
+ *                     1-20:   directed scan to i'th ssid.
+ *                     22:     channel width configuation - 1 for narrow.
+ *                     24:     full scan.
+ *                     25:     partial scan.
+ * @channel_number:    channel number 1-13 etc.
+ * @iter_count:                repetition count for the channel.
+ * @iter_interval:     interval between two innteration on one channel.
+ * @dwell_time:        entry 0 - active scan, entry 1 - passive scan.
+ */
+struct iwl_scan_channel_cfg {
+       __le32 type[IWL_MAX_SCAN_CHANNELS];
+       __le16 channel_number[IWL_MAX_SCAN_CHANNELS];
+       __le16 iter_count[IWL_MAX_SCAN_CHANNELS];
+       __le32 iter_interval[IWL_MAX_SCAN_CHANNELS];
+       u8 dwell_time[IWL_MAX_SCAN_CHANNELS][2];
+} __packed;
+
+/**
+ * iwl_scan_offload_cfg - SCAN_OFFLOAD_CONFIG_API_S
+ * @scan_cmd:          scan command fixed part
+ * @channel_cfg:       scan channel configuration
+ * @data:              probe request frames (one per band)
+ */
+struct iwl_scan_offload_cfg {
+       struct iwl_scan_offload_cmd scan_cmd;
+       struct iwl_scan_channel_cfg channel_cfg;
+       u8 data[0];
+} __packed;
+
+/**
+ * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S
+ * @ssid:              MAC address to filter out
+ * @reported_rssi:     AP rssi reported to the host
+ */
+struct iwl_scan_offload_blacklist {
+       u8 ssid[ETH_ALEN];
+       u8 reported_rssi;
+       u8 reserved;
+} __packed;
+
+enum iwl_scan_offload_network_type {
+       IWL_NETWORK_TYPE_BSS    = 1,
+       IWL_NETWORK_TYPE_IBSS   = 2,
+       IWL_NETWORK_TYPE_ANY    = 3,
+};
+
+enum iwl_scan_offload_band_selection {
+       IWL_SCAN_OFFLOAD_SELECT_2_4     = 0x4,
+       IWL_SCAN_OFFLOAD_SELECT_5_2     = 0x8,
+       IWL_SCAN_OFFLOAD_SELECT_ANY     = 0xc,
+};
+
+/**
+ * iwl_scan_offload_profile - SCAN_OFFLOAD_PROFILE_S
+ * @ssid_index:                index to ssid list in fixed part
+ * @unicast_cipher:    encryption olgorithm to match - bitmap
+ * @aut_alg:           authentication olgorithm to match - bitmap
+ * @network_type:      enum iwl_scan_offload_network_type
+ * @band_selection:    enum iwl_scan_offload_band_selection
+ */
+struct iwl_scan_offload_profile {
+       u8 ssid_index;
+       u8 unicast_cipher;
+       u8 auth_alg;
+       u8 network_type;
+       u8 band_selection;
+       u8 reserved[3];
+} __packed;
+
+/**
+ * iwl_scan_offload_profile_cfg - SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1
+ * @blaclist:          AP list to filter off from scan results
+ * @profiles:          profiles to search for match
+ * @blacklist_len:     length of blacklist
+ * @num_profiles:      num of profiles in the list
+ */
+struct iwl_scan_offload_profile_cfg {
+       struct iwl_scan_offload_blacklist blacklist[IWL_SCAN_MAX_BLACKLIST_LEN];
+       struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES];
+       u8 blacklist_len;
+       u8 num_profiles;
+       u8 reserved[2];
+} __packed;
+
+/**
+ * iwl_scan_offload_schedule - schedule of scan offload
+ * @delay:             delay between iterations, in seconds.
+ * @iterations:                num of scan iterations
+ * @full_scan_mul:     number of partial scans before each full scan
+ */
+struct iwl_scan_offload_schedule {
+       u16 delay;
+       u8 iterations;
+       u8 full_scan_mul;
+} __packed;
+
+/*
+ * iwl_scan_offload_flags
+ *
+ * IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID: filter mode - upload every beacon or match
+ *     ssid list.
+ * IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan.
+ * IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan
+ *     on A band.
+ */
+enum iwl_scan_offload_flags {
+       IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID       = BIT(0),
+       IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL    = BIT(2),
+       IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN       = BIT(3),
+};
+
+/**
+ * iwl_scan_offload_req - scan offload request command
+ * @flags:             bitmap - enum iwl_scan_offload_flags.
+ * @watchdog:          maximum scan duration in TU.
+ * @delay:             delay in seconds before first iteration.
+ * @schedule_line:     scan offload schedule, for fast and regular scan.
+ */
+struct iwl_scan_offload_req {
+       __le16 flags;
+       __le16 watchdog;
+       __le16 delay;
+       __le16 reserved;
+       struct iwl_scan_offload_schedule schedule_line[2];
+} __packed;
+
+enum iwl_scan_offload_compleate_status {
+       IWL_SCAN_OFFLOAD_COMPLETED      = 1,
+       IWL_SCAN_OFFLOAD_ABORTED        = 2,
+};
+
+/**
+ * iwl_scan_offload_complete - SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1
+ * @last_schedule_line:                last schedule line executed (fast or regular)
+ * @last_schedule_iteration:   last scan iteration executed before scan abort
+ * @status:                    enum iwl_scan_offload_compleate_status
+ */
+struct iwl_scan_offload_complete {
+       u8 last_schedule_line;
+       u8 last_schedule_iteration;
+       u8 status;
+       u8 reserved;
+} __packed;
+
+#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
new file mode 100644 (file)
index 0000000..0acb53d
--- /dev/null
@@ -0,0 +1,380 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_sta_h__
+#define __fw_api_sta_h__
+
+/**
+ * enum iwl_sta_flags - flags for the ADD_STA host command
+ * @STA_FLG_REDUCED_TX_PWR_CTRL:
+ * @STA_FLG_REDUCED_TX_PWR_DATA:
+ * @STA_FLG_FLG_ANT_MSK: Antenna selection
+ * @STA_FLG_PS: set if STA is in Power Save
+ * @STA_FLG_INVALID: set if STA is invalid
+ * @STA_FLG_DLP_EN: Direct Link Protocol is enabled
+ * @STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs
+ * @STA_FLG_DRAIN_FLOW: drain flow
+ * @STA_FLG_PAN: STA is for PAN interface
+ * @STA_FLG_CLASS_AUTH:
+ * @STA_FLG_CLASS_ASSOC:
+ * @STA_FLG_CLASS_MIMO_PROT:
+ * @STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU
+ * @STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation
+ * @STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is
+ *     initialised by driver and can be updated by fw upon reception of
+ *     action frames that can change the channel width. When cleared the fw
+ *     will send all the frames in 20MHz even when FAT channel is requested.
+ * @STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the
+ *     driver and can be updated by fw upon reception of action frames.
+ * @STA_FLG_MFP_EN: Management Frame Protection
+ */
+enum iwl_sta_flags {
+       STA_FLG_REDUCED_TX_PWR_CTRL     = BIT(3),
+       STA_FLG_REDUCED_TX_PWR_DATA     = BIT(6),
+
+       STA_FLG_FLG_ANT_A               = (1 << 4),
+       STA_FLG_FLG_ANT_B               = (2 << 4),
+       STA_FLG_FLG_ANT_MSK             = (STA_FLG_FLG_ANT_A |
+                                          STA_FLG_FLG_ANT_B),
+
+       STA_FLG_PS                      = BIT(8),
+       STA_FLG_INVALID                 = BIT(9),
+       STA_FLG_DLP_EN                  = BIT(10),
+       STA_FLG_SET_ALL_KEYS            = BIT(11),
+       STA_FLG_DRAIN_FLOW              = BIT(12),
+       STA_FLG_PAN                     = BIT(13),
+       STA_FLG_CLASS_AUTH              = BIT(14),
+       STA_FLG_CLASS_ASSOC             = BIT(15),
+       STA_FLG_RTS_MIMO_PROT           = BIT(17),
+
+       STA_FLG_MAX_AGG_SIZE_SHIFT      = 19,
+       STA_FLG_MAX_AGG_SIZE_8K         = (0 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_16K        = (1 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_32K        = (2 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_64K        = (3 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_128K       = (4 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_256K       = (5 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_512K       = (6 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_1024K      = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+       STA_FLG_MAX_AGG_SIZE_MSK        = (7 << STA_FLG_MAX_AGG_SIZE_SHIFT),
+
+       STA_FLG_AGG_MPDU_DENS_SHIFT     = 23,
+       STA_FLG_AGG_MPDU_DENS_2US       = (4 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+       STA_FLG_AGG_MPDU_DENS_4US       = (5 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+       STA_FLG_AGG_MPDU_DENS_8US       = (6 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+       STA_FLG_AGG_MPDU_DENS_16US      = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+       STA_FLG_AGG_MPDU_DENS_MSK       = (7 << STA_FLG_AGG_MPDU_DENS_SHIFT),
+
+       STA_FLG_FAT_EN_20MHZ            = (0 << 26),
+       STA_FLG_FAT_EN_40MHZ            = (1 << 26),
+       STA_FLG_FAT_EN_80MHZ            = (2 << 26),
+       STA_FLG_FAT_EN_160MHZ           = (3 << 26),
+       STA_FLG_FAT_EN_MSK              = (3 << 26),
+
+       STA_FLG_MIMO_EN_SISO            = (0 << 28),
+       STA_FLG_MIMO_EN_MIMO2           = (1 << 28),
+       STA_FLG_MIMO_EN_MIMO3           = (2 << 28),
+       STA_FLG_MIMO_EN_MSK             = (3 << 28),
+};
+
+/**
+ * enum iwl_sta_key_flag - key flags for the ADD_STA host command
+ * @STA_KEY_FLG_EN_MSK: mask for encryption algorithm
+ * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
+ *     station info array (1 - n 1X mode)
+ * @STA_KEY_FLG_KEYID_MSK: the index of the key
+ * @STA_KEY_NOT_VALID: key is invalid
+ * @STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key
+ * @STA_KEY_MULTICAST: set for multical key
+ * @STA_KEY_MFP: key is used for Management Frame Protection
+ */
+enum iwl_sta_key_flag {
+       STA_KEY_FLG_NO_ENC              = (0 << 0),
+       STA_KEY_FLG_WEP                 = (1 << 0),
+       STA_KEY_FLG_CCM                 = (2 << 0),
+       STA_KEY_FLG_TKIP                = (3 << 0),
+       STA_KEY_FLG_CMAC                = (6 << 0),
+       STA_KEY_FLG_ENC_UNKNOWN         = (7 << 0),
+       STA_KEY_FLG_EN_MSK              = (7 << 0),
+
+       STA_KEY_FLG_WEP_KEY_MAP         = BIT(3),
+       STA_KEY_FLG_KEYID_POS            = 8,
+       STA_KEY_FLG_KEYID_MSK           = (3 << STA_KEY_FLG_KEYID_POS),
+       STA_KEY_NOT_VALID               = BIT(11),
+       STA_KEY_FLG_WEP_13BYTES         = BIT(12),
+       STA_KEY_MULTICAST               = BIT(14),
+       STA_KEY_MFP                     = BIT(15),
+};
+
+/**
+ * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed
+ * @STA_MODIFY_KEY: this command modifies %key
+ * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx
+ * @STA_MODIFY_TX_RATE: unused
+ * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid
+ * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid
+ * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count
+ * @STA_MODIFY_PROT_TH:
+ * @STA_MODIFY_QUEUES: modify the queues used by this station
+ */
+enum iwl_sta_modify_flag {
+       STA_MODIFY_KEY                          = BIT(0),
+       STA_MODIFY_TID_DISABLE_TX               = BIT(1),
+       STA_MODIFY_TX_RATE                      = BIT(2),
+       STA_MODIFY_ADD_BA_TID                   = BIT(3),
+       STA_MODIFY_REMOVE_BA_TID                = BIT(4),
+       STA_MODIFY_SLEEPING_STA_TX_COUNT        = BIT(5),
+       STA_MODIFY_PROT_TH                      = BIT(6),
+       STA_MODIFY_QUEUES                       = BIT(7),
+};
+
+#define STA_MODE_MODIFY        1
+
+/**
+ * enum iwl_sta_sleep_flag - type of sleep of the station
+ * @STA_SLEEP_STATE_AWAKE:
+ * @STA_SLEEP_STATE_PS_POLL:
+ * @STA_SLEEP_STATE_UAPSD:
+ */
+enum iwl_sta_sleep_flag {
+       STA_SLEEP_STATE_AWAKE   = 0,
+       STA_SLEEP_STATE_PS_POLL = BIT(0),
+       STA_SLEEP_STATE_UAPSD   = BIT(1),
+};
+
+/* STA ID and color bits definitions */
+#define STA_ID_SEED            (0x0f)
+#define STA_ID_POS             (0)
+#define STA_ID_MSK             (STA_ID_SEED << STA_ID_POS)
+
+#define STA_COLOR_SEED         (0x7)
+#define STA_COLOR_POS          (4)
+#define STA_COLOR_MSK          (STA_COLOR_SEED << STA_COLOR_POS)
+
+#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \
+       (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS)
+#define STA_ID_N_COLOR_GET_ID(id_n_color)    \
+       (((id_n_color) & STA_ID_MSK) >> STA_ID_POS)
+
+#define STA_KEY_MAX_NUM (16)
+#define STA_KEY_IDX_INVALID (0xff)
+#define STA_KEY_MAX_DATA_KEY_NUM (4)
+#define IWL_MAX_GLOBAL_KEYS (4)
+#define STA_KEY_LEN_WEP40 (5)
+#define STA_KEY_LEN_WEP104 (13)
+
+/**
+ * struct iwl_mvm_keyinfo - key information
+ * @key_flags: type %iwl_sta_key_flag
+ * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection
+ * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx
+ * @key_offset: key offset in the fw's key table
+ * @key: 16-byte unicast decryption key
+ * @tx_secur_seq_cnt: initial RSC / PN needed for replay check
+ * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only
+ * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only
+ */
+struct iwl_mvm_keyinfo {
+       __le16 key_flags;
+       u8 tkip_rx_tsc_byte2;
+       u8 reserved1;
+       __le16 tkip_rx_ttak[5];
+       u8 key_offset;
+       u8 reserved2;
+       u8 key[16];
+       __le64 tx_secur_seq_cnt;
+       __le64 hw_tkip_mic_rx_key;
+       __le64 hw_tkip_mic_tx_key;
+} __packed;
+
+/**
+ * struct iwl_mvm_add_sta_cmd - Add / modify a station in the fw's station table
+ * ( REPLY_ADD_STA = 0x18 )
+ * @add_modify: 1: modify existing, 0: add new station
+ * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent
+ * @multicast_tx_key_id: multicast tx key id. Relevant only when multicast key
+ *     sent
+ * @mac_id_n_color: the Mac context this station belongs to
+ * @addr[ETH_ALEN]: station's MAC address
+ * @sta_id: index of station in uCode's station table
+ * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave
+ *     alone. 1 - modify, 0 - don't change.
+ * @key: look at %iwl_mvm_keyinfo
+ * @station_flags: look at %iwl_sta_flags
+ * @station_flags_msk: what of %station_flags have changed
+ * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable
+ *     AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field.
+ * @add_immediate_ba_tid: tid for which to add block-ack support (Rx)
+ *     Set %STA_MODIFY_ADD_BA_TID to use this field, and also set
+ *     add_immediate_ba_ssn.
+ * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx)
+ *     Set %STA_MODIFY_REMOVE_BA_TID to use this field
+ * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with
+ *     add_immediate_ba_tid.
+ * @sleep_tx_count: number of packets to transmit to station even though it is
+ *     asleep. Used to synchronise PS-poll and u-APSD responses while ucode
+ *     keeps track of STA sleep state.
+ * @sleep_state_flags: Look at %iwl_sta_sleep_flag.
+ * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP
+ *     mac-addr.
+ * @beamform_flags: beam forming controls
+ * @tfd_queue_msk: tfd queues used by this station
+ *
+ * The device contains an internal table of per-station information, with info
+ * on security keys, aggregation parameters, and Tx rates for initial Tx
+ * attempt and any retries (set by REPLY_TX_LINK_QUALITY_CMD).
+ *
+ * ADD_STA sets up the table entry for one station, either creating a new
+ * entry, or modifying a pre-existing one.
+ */
+struct iwl_mvm_add_sta_cmd {
+       u8 add_modify;
+       u8 unicast_tx_key_id;
+       u8 multicast_tx_key_id;
+       u8 reserved1;
+       __le32 mac_id_n_color;
+       u8 addr[ETH_ALEN];
+       __le16 reserved2;
+       u8 sta_id;
+       u8 modify_mask;
+       __le16 reserved3;
+       struct iwl_mvm_keyinfo key;
+       __le32 station_flags;
+       __le32 station_flags_msk;
+       __le16 tid_disable_tx;
+       __le16 reserved4;
+       u8 add_immediate_ba_tid;
+       u8 remove_immediate_ba_tid;
+       __le16 add_immediate_ba_ssn;
+       __le16 sleep_tx_count;
+       __le16 sleep_state_flags;
+       __le16 assoc_id;
+       __le16 beamform_flags;
+       __le32 tfd_queue_msk;
+} __packed; /* ADD_STA_CMD_API_S_VER_5 */
+
+/**
+ * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command
+ * @ADD_STA_SUCCESS: operation was executed successfully
+ * @ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table
+ * @ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session
+ * @ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station that
+ *     doesn't exist.
+ */
+enum iwl_mvm_add_sta_rsp_status {
+       ADD_STA_SUCCESS                 = 0x1,
+       ADD_STA_STATIONS_OVERLOAD       = 0x2,
+       ADD_STA_IMMEDIATE_BA_FAILURE    = 0x4,
+       ADD_STA_MODIFY_NON_EXISTING_STA = 0x8,
+};
+
+/**
+ * struct iwl_mvm_rm_sta_cmd - Add / modify a station in the fw's station table
+ * ( REMOVE_STA = 0x19 )
+ * @sta_id: the station id of the station to be removed
+ */
+struct iwl_mvm_rm_sta_cmd {
+       u8 sta_id;
+       u8 reserved[3];
+} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */
+
+/**
+ * struct iwl_mvm_mgmt_mcast_key_cmd
+ * ( MGMT_MCAST_KEY = 0x1f )
+ * @ctrl_flags: %iwl_sta_key_flag
+ * @IGTK:
+ * @K1: IGTK master key
+ * @K2: IGTK sub key
+ * @sta_id: station ID that support IGTK
+ * @key_id:
+ * @receive_seq_cnt: initial RSC/PN needed for replay check
+ */
+struct iwl_mvm_mgmt_mcast_key_cmd {
+       __le32 ctrl_flags;
+       u8 IGTK[16];
+       u8 K1[16];
+       u8 K2[16];
+       __le32 key_id;
+       __le32 sta_id;
+       __le64 receive_seq_cnt;
+} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */
+
+struct iwl_mvm_wep_key {
+       u8 key_index;
+       u8 key_offset;
+       __le16 reserved1;
+       u8 key_size;
+       u8 reserved2[3];
+       u8 key[16];
+} __packed;
+
+struct iwl_mvm_wep_key_cmd {
+       __le32 mac_id_n_color;
+       u8 num_keys;
+       u8 decryption_type;
+       u8 flags;
+       u8 reserved;
+       struct iwl_mvm_wep_key wep_key[0];
+} __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */
+
+
+#endif /* __fw_api_sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
new file mode 100644 (file)
index 0000000..2677914
--- /dev/null
@@ -0,0 +1,580 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+#ifndef __fw_api_tx_h__
+#define __fw_api_tx_h__
+
+/**
+ * enum iwl_tx_flags - bitmasks for tx_flags in TX command
+ * @TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame
+ * @TX_CMD_FLG_ACK: expect ACK from receiving station
+ * @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command.
+ *     Otherwise, use rate_n_flags from the TX command
+ * @TX_CMD_FLG_BA: this frame is a block ack
+ * @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected
+ *     Must set TX_CMD_FLG_ACK with this flag.
+ * @TX_CMD_FLG_TXOP_PROT: protect frame with full TXOP protection
+ * @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
+ * @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
+ * @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
+ * @TX_CMD_FLG_BT_DIS: disable BT priority for this frame
+ * @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
+ *     Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
+ * @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU
+ * @TX_CMD_FLG_NEXT_FRAME: this frame includes information of the next frame
+ * @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame
+ *     Should be set for beacons and probe responses
+ * @TX_CMD_FLG_CALIB: activate PA TX power calibrations
+ * @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count
+ * @TX_CMD_FLG_AGG_START: allow this frame to start aggregation
+ * @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header.
+ *     Should be set for 26/30 length MAC headers
+ * @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
+ * @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration
+ * @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation
+ * @TX_CMD_FLG_CTS_ONLY: send CTS only, no data after that
+ * @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id
+ * @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped
+ * @TX_CMD_FLG_EXEC_PAPD: execute PAPD
+ * @TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power
+ * @TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk
+ */
+enum iwl_tx_flags {
+       TX_CMD_FLG_PROT_REQUIRE         = BIT(0),
+       TX_CMD_FLG_ACK                  = BIT(3),
+       TX_CMD_FLG_STA_RATE             = BIT(4),
+       TX_CMD_FLG_BA                   = BIT(5),
+       TX_CMD_FLG_BAR                  = BIT(6),
+       TX_CMD_FLG_TXOP_PROT            = BIT(7),
+       TX_CMD_FLG_VHT_NDPA             = BIT(8),
+       TX_CMD_FLG_HT_NDPA              = BIT(9),
+       TX_CMD_FLG_CSI_FDBK2HOST        = BIT(10),
+       TX_CMD_FLG_BT_DIS               = BIT(12),
+       TX_CMD_FLG_SEQ_CTL              = BIT(13),
+       TX_CMD_FLG_MORE_FRAG            = BIT(14),
+       TX_CMD_FLG_NEXT_FRAME           = BIT(15),
+       TX_CMD_FLG_TSF                  = BIT(16),
+       TX_CMD_FLG_CALIB                = BIT(17),
+       TX_CMD_FLG_KEEP_SEQ_CTL         = BIT(18),
+       TX_CMD_FLG_AGG_START            = BIT(19),
+       TX_CMD_FLG_MH_PAD               = BIT(20),
+       TX_CMD_FLG_RESP_TO_DRV          = BIT(21),
+       TX_CMD_FLG_CCMP_AGG             = BIT(22),
+       TX_CMD_FLG_TKIP_MIC_DONE        = BIT(23),
+       TX_CMD_FLG_CTS_ONLY             = BIT(24),
+       TX_CMD_FLG_DUR                  = BIT(25),
+       TX_CMD_FLG_FW_DROP              = BIT(26),
+       TX_CMD_FLG_EXEC_PAPD            = BIT(27),
+       TX_CMD_FLG_PAPD_TYPE            = BIT(28),
+       TX_CMD_FLG_HCCA_CHUNK           = BIT(31)
+}; /* TX_FLAGS_BITS_API_S_VER_1 */
+
+/*
+ * TX command security control
+ */
+#define TX_CMD_SEC_WEP                 0x01
+#define TX_CMD_SEC_CCM                 0x02
+#define TX_CMD_SEC_TKIP                        0x03
+#define TX_CMD_SEC_WEP_KEY_IDX_POS     6
+#define TX_CMD_SEC_WEP_KEY_IDX_MSK     0xc0
+#define TX_CMD_SEC_KEY128              0x08
+
+/* TODO: how does these values are OK with only 16 bit variable??? */
+/*
+ * TX command next frame info
+ *
+ * bits 0:2 - security control (TX_CMD_SEC_*)
+ * bit 3 - immediate ACK required
+ * bit 4 - rate is taken from STA table
+ * bit 5 - frame belongs to BA stream
+ * bit 6 - immediate BA response expected
+ * bit 7 - unused
+ * bits 8:15 - Station ID
+ * bits 16:31 - rate
+ */
+#define TX_CMD_NEXT_FRAME_ACK_MSK              (0x8)
+#define TX_CMD_NEXT_FRAME_STA_RATE_MSK         (0x10)
+#define TX_CMD_NEXT_FRAME_BA_MSK               (0x20)
+#define TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK       (0x40)
+#define TX_CMD_NEXT_FRAME_FLAGS_MSK            (0xf8)
+#define TX_CMD_NEXT_FRAME_STA_ID_MSK           (0xff00)
+#define TX_CMD_NEXT_FRAME_STA_ID_POS           (8)
+#define TX_CMD_NEXT_FRAME_RATE_MSK             (0xffff0000)
+#define TX_CMD_NEXT_FRAME_RATE_POS             (16)
+
+/*
+ * TX command Frame life time in us - to be written in pm_frame_timeout
+ */
+#define TX_CMD_LIFE_TIME_INFINITE      0xFFFFFFFF
+#define TX_CMD_LIFE_TIME_DEFAULT       2000000 /* 2000 ms*/
+#define TX_CMD_LIFE_TIME_PROBE_RESP    40000 /* 40 ms */
+#define TX_CMD_LIFE_TIME_EXPIRED_FRAME 0
+
+/*
+ * TID for non QoS frames - to be written in tid_tspec
+ */
+#define IWL_TID_NON_QOS        IWL_MAX_TID_COUNT
+
+/*
+ * Limits on the retransmissions - to be written in {data,rts}_retry_limit
+ */
+#define IWL_DEFAULT_TX_RETRY                   15
+#define IWL_MGMT_DFAULT_RETRY_LIMIT            3
+#define IWL_RTS_DFAULT_RETRY_LIMIT             60
+#define IWL_BAR_DFAULT_RETRY_LIMIT             60
+#define IWL_LOW_RETRY_LIMIT                    7
+
+/* TODO: complete documentation for try_cnt and btkill_cnt */
+/**
+ * struct iwl_tx_cmd - TX command struct to FW
+ * ( TX_CMD = 0x1c )
+ * @len: in bytes of the payload, see below for details
+ * @next_frame_len: same as len, but for next frame (0 if not applicable)
+ *     Used for fragmentation and bursting, but not in 11n aggregation.
+ * @tx_flags: combination of TX_CMD_FLG_*
+ * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
+ *     cleared. Combination of RATE_MCS_*
+ * @sta_id: index of destination station in FW station table
+ * @sec_ctl: security control, TX_CMD_SEC_*
+ * @initial_rate_index: index into the the rate table for initial TX attempt.
+ *     Applied if TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames.
+ * @key: security key
+ * @next_frame_flags: TX_CMD_SEC_* and TX_CMD_NEXT_FRAME_*
+ * @life_time: frame life time (usecs??)
+ * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt +
+ *     btkill_cnd + reserved), first 32 bits. "0" disables usage.
+ * @dram_msb_ptr: upper bits of the scratch physical address
+ * @rts_retry_limit: max attempts for RTS
+ * @data_retry_limit: max attempts to send the data packet
+ * @tid_spec: TID/tspec
+ * @pm_frame_timeout: PM TX frame timeout
+ * @driver_txop: duration od EDCA TXOP, in 32-usec units. Set this if not
+ *     specified by HCCA protocol
+ *
+ * The byte count (both len and next_frame_len) includes MAC header
+ * (24/26/30/32 bytes)
+ * + 2 bytes pad if 26/30 header size
+ * + 8 byte IV for CCM or TKIP (not used for WEP)
+ * + Data payload
+ * + 8-byte MIC (not used for CCM/WEP)
+ * It does not include post-MAC padding, i.e.,
+ * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.
+ * Range of len: 14-2342 bytes.
+ *
+ * After the struct fields the MAC header is placed, plus any padding,
+ * and then the actial payload.
+ */
+struct iwl_tx_cmd {
+       __le16 len;
+       __le16 next_frame_len;
+       __le32 tx_flags;
+       /* DRAM_SCRATCH_API_U_VER_1 */
+       u8 try_cnt;
+       u8 btkill_cnt;
+       __le16 reserved;
+       __le32 rate_n_flags;
+       u8 sta_id;
+       u8 sec_ctl;
+       u8 initial_rate_index;
+       u8 reserved2;
+       u8 key[16];
+       __le16 next_frame_flags;
+       __le16 reserved3;
+       __le32 life_time;
+       __le32 dram_lsb_ptr;
+       u8 dram_msb_ptr;
+       u8 rts_retry_limit;
+       u8 data_retry_limit;
+       u8 tid_tspec;
+       __le16 pm_frame_timeout;
+       __le16 driver_txop;
+       u8 payload[0];
+       struct ieee80211_hdr hdr[0];
+} __packed; /* TX_CMD_API_S_VER_3 */
+
+/*
+ * TX response related data
+ */
+
+/*
+ * enum iwl_tx_status - status that is returned by the fw after attempts to Tx
+ * @TX_STATUS_SUCCESS:
+ * @TX_STATUS_DIRECT_DONE:
+ * @TX_STATUS_POSTPONE_DELAY:
+ * @TX_STATUS_POSTPONE_FEW_BYTES:
+ * @TX_STATUS_POSTPONE_BT_PRIO:
+ * @TX_STATUS_POSTPONE_QUIET_PERIOD:
+ * @TX_STATUS_POSTPONE_CALC_TTAK:
+ * @TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY:
+ * @TX_STATUS_FAIL_SHORT_LIMIT:
+ * @TX_STATUS_FAIL_LONG_LIMIT:
+ * @TX_STATUS_FAIL_UNDERRUN:
+ * @TX_STATUS_FAIL_DRAIN_FLOW:
+ * @TX_STATUS_FAIL_RFKILL_FLUSH:
+ * @TX_STATUS_FAIL_LIFE_EXPIRE:
+ * @TX_STATUS_FAIL_DEST_PS:
+ * @TX_STATUS_FAIL_HOST_ABORTED:
+ * @TX_STATUS_FAIL_BT_RETRY:
+ * @TX_STATUS_FAIL_STA_INVALID:
+ * @TX_TATUS_FAIL_FRAG_DROPPED:
+ * @TX_STATUS_FAIL_TID_DISABLE:
+ * @TX_STATUS_FAIL_FIFO_FLUSHED:
+ * @TX_STATUS_FAIL_SMALL_CF_POLL:
+ * @TX_STATUS_FAIL_FW_DROP:
+ * @TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and
+ *     STA table
+ * @TX_FRAME_STATUS_INTERNAL_ABORT:
+ * @TX_MODE_MSK:
+ * @TX_MODE_NO_BURST:
+ * @TX_MODE_IN_BURST_SEQ:
+ * @TX_MODE_FIRST_IN_BURST:
+ * @TX_QUEUE_NUM_MSK:
+ *
+ * Valid only if frame_count =1
+ * TODO: complete documentation
+ */
+enum iwl_tx_status {
+       TX_STATUS_MSK = 0x000000ff,
+       TX_STATUS_SUCCESS = 0x01,
+       TX_STATUS_DIRECT_DONE = 0x02,
+       /* postpone TX */
+       TX_STATUS_POSTPONE_DELAY = 0x40,
+       TX_STATUS_POSTPONE_FEW_BYTES = 0x41,
+       TX_STATUS_POSTPONE_BT_PRIO = 0x42,
+       TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43,
+       TX_STATUS_POSTPONE_CALC_TTAK = 0x44,
+       /* abort TX */
+       TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81,
+       TX_STATUS_FAIL_SHORT_LIMIT = 0x82,
+       TX_STATUS_FAIL_LONG_LIMIT = 0x83,
+       TX_STATUS_FAIL_UNDERRUN = 0x84,
+       TX_STATUS_FAIL_DRAIN_FLOW = 0x85,
+       TX_STATUS_FAIL_RFKILL_FLUSH = 0x86,
+       TX_STATUS_FAIL_LIFE_EXPIRE = 0x87,
+       TX_STATUS_FAIL_DEST_PS = 0x88,
+       TX_STATUS_FAIL_HOST_ABORTED = 0x89,
+       TX_STATUS_FAIL_BT_RETRY = 0x8a,
+       TX_STATUS_FAIL_STA_INVALID = 0x8b,
+       TX_STATUS_FAIL_FRAG_DROPPED = 0x8c,
+       TX_STATUS_FAIL_TID_DISABLE = 0x8d,
+       TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e,
+       TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f,
+       TX_STATUS_FAIL_FW_DROP = 0x90,
+       TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91,
+       TX_STATUS_INTERNAL_ABORT = 0x92,
+       TX_MODE_MSK = 0x00000f00,
+       TX_MODE_NO_BURST = 0x00000000,
+       TX_MODE_IN_BURST_SEQ = 0x00000100,
+       TX_MODE_FIRST_IN_BURST = 0x00000200,
+       TX_QUEUE_NUM_MSK = 0x0001f000,
+       TX_NARROW_BW_MSK = 0x00060000,
+       TX_NARROW_BW_1DIV2 = 0x00020000,
+       TX_NARROW_BW_1DIV4 = 0x00040000,
+       TX_NARROW_BW_1DIV8 = 0x00060000,
+};
+
+/*
+ * enum iwl_tx_agg_status - TX aggregation status
+ * @AGG_TX_STATE_STATUS_MSK:
+ * @AGG_TX_STATE_TRANSMITTED:
+ * @AGG_TX_STATE_UNDERRUN:
+ * @AGG_TX_STATE_BT_PRIO:
+ * @AGG_TX_STATE_FEW_BYTES:
+ * @AGG_TX_STATE_ABORT:
+ * @AGG_TX_STATE_LAST_SENT_TTL:
+ * @AGG_TX_STATE_LAST_SENT_TRY_CNT:
+ * @AGG_TX_STATE_LAST_SENT_BT_KILL:
+ * @AGG_TX_STATE_SCD_QUERY:
+ * @AGG_TX_STATE_TEST_BAD_CRC32:
+ * @AGG_TX_STATE_RESPONSE:
+ * @AGG_TX_STATE_DUMP_TX:
+ * @AGG_TX_STATE_DELAY_TX:
+ * @AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries
+ *     occur if tx failed for this frame when it was a member of a previous
+ *     aggregation block). If rate scaling is used, retry count indicates the
+ *     rate table entry used for all frames in the new agg.
+ *@ AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for
+ *     this frame
+ *
+ * TODO: complete documentation
+ */
+enum iwl_tx_agg_status {
+       AGG_TX_STATE_STATUS_MSK = 0x00fff,
+       AGG_TX_STATE_TRANSMITTED = 0x000,
+       AGG_TX_STATE_UNDERRUN = 0x001,
+       AGG_TX_STATE_BT_PRIO = 0x002,
+       AGG_TX_STATE_FEW_BYTES = 0x004,
+       AGG_TX_STATE_ABORT = 0x008,
+       AGG_TX_STATE_LAST_SENT_TTL = 0x010,
+       AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020,
+       AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040,
+       AGG_TX_STATE_SCD_QUERY = 0x080,
+       AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100,
+       AGG_TX_STATE_RESPONSE = 0x1ff,
+       AGG_TX_STATE_DUMP_TX = 0x200,
+       AGG_TX_STATE_DELAY_TX = 0x400,
+       AGG_TX_STATE_TRY_CNT_POS = 12,
+       AGG_TX_STATE_TRY_CNT_MSK = 0xf << AGG_TX_STATE_TRY_CNT_POS,
+};
+
+#define AGG_TX_STATE_LAST_SENT_MSK  (AGG_TX_STATE_LAST_SENT_TTL| \
+                                    AGG_TX_STATE_LAST_SENT_TRY_CNT| \
+                                    AGG_TX_STATE_LAST_SENT_BT_KILL)
+
+/*
+ * The mask below describes a status where we are absolutely sure that the MPDU
+ * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've
+ * written the bytes to the TXE, but we know nothing about what the DSP did.
+ */
+#define AGG_TX_STAT_FRAME_NOT_SENT (AGG_TX_STATE_FEW_BYTES | \
+                                   AGG_TX_STATE_ABORT | \
+                                   AGG_TX_STATE_SCD_QUERY)
+
+/*
+ * REPLY_TX = 0x1c (response)
+ *
+ * This response may be in one of two slightly different formats, indicated
+ * by the frame_count field:
+ *
+ * 1)  No aggregation (frame_count == 1).  This reports Tx results for a single
+ *     frame. Multiple attempts, at various bit rates, may have been made for
+ *     this frame.
+ *
+ * 2)  Aggregation (frame_count > 1).  This reports Tx results for two or more
+ *     frames that used block-acknowledge.  All frames were transmitted at
+ *     same rate. Rate scaling may have been used if first frame in this new
+ *     agg block failed in previous agg block(s).
+ *
+ *     Note that, for aggregation, ACK (block-ack) status is not delivered
+ *     here; block-ack has not been received by the time the device records
+ *     this status.
+ *     This status relates to reasons the tx might have been blocked or aborted
+ *     within the device, rather than whether it was received successfully by
+ *     the destination station.
+ */
+
+/**
+ * struct agg_tx_status - per packet TX aggregation status
+ * @status: enum iwl_tx_agg_status
+ * @sequence: Sequence # for this frame's Tx cmd (not SSN!)
+ */
+struct agg_tx_status {
+       __le16 status;
+       __le16 sequence;
+} __packed;
+
+/*
+ * definitions for initial rate index field
+ * bits [3:0] initial rate index
+ * bits [6:4] rate table color, used for the initial rate
+ * bit-7 invalid rate indication
+ */
+#define TX_RES_INIT_RATE_INDEX_MSK 0x0f
+#define TX_RES_RATE_TABLE_COLOR_MSK 0x70
+#define TX_RES_INV_RATE_INDEX_MSK 0x80
+
+#define IWL_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f)
+#define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4)
+
+/**
+ * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet
+ * ( REPLY_TX = 0x1c )
+ * @frame_count: 1 no aggregation, >1 aggregation
+ * @bt_kill_count: num of times blocked by bluetooth (unused for agg)
+ * @failure_rts: num of failures due to unsuccessful RTS
+ * @failure_frame: num failures due to no ACK (unused for agg)
+ * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the
+ *     Tx of all the batch. RATE_MCS_*
+ * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK.
+ *     for agg: RTS + CTS + aggregation tx time + block-ack time.
+ *     in usec.
+ * @pa_status: tx power info
+ * @pa_integ_res_a: tx power info
+ * @pa_integ_res_b: tx power info
+ * @pa_integ_res_c: tx power info
+ * @measurement_req_id: tx power info
+ * @tfd_info: TFD information set by the FH
+ * @seq_ctl: sequence control from the Tx cmd
+ * @byte_cnt: byte count from the Tx cmd
+ * @tlc_info: TLC rate info
+ * @ra_tid: bits [3:0] = ra, bits [7:4] = tid
+ * @frame_ctrl: frame control
+ * @status: for non-agg:  frame status TX_STATUS_*
+ *     for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields
+ *     follow this one, up to frame_count.
+ *
+ * After the array of statuses comes the SSN of the SCD. Look at
+ * %iwl_mvm_get_scd_ssn for more details.
+ */
+struct iwl_mvm_tx_resp {
+       u8 frame_count;
+       u8 bt_kill_count;
+       u8 failure_rts;
+       u8 failure_frame;
+       __le32 initial_rate;
+       __le16 wireless_media_time;
+
+       u8 pa_status;
+       u8 pa_integ_res_a[3];
+       u8 pa_integ_res_b[3];
+       u8 pa_integ_res_c[3];
+       __le16 measurement_req_id;
+       __le16 reserved;
+
+       __le32 tfd_info;
+       __le16 seq_ctl;
+       __le16 byte_cnt;
+       u8 tlc_info;
+       u8 ra_tid;
+       __le16 frame_ctrl;
+
+       struct agg_tx_status status;
+} __packed; /* TX_RSP_API_S_VER_3 */
+
+/**
+ * struct iwl_mvm_ba_notif - notifies about reception of BA
+ * ( BA_NOTIF = 0xc5 )
+ * @sta_addr_lo32: lower 32 bits of the MAC address
+ * @sta_addr_hi16: upper 16 bits of the MAC address
+ * @sta_id: Index of recipient (BA-sending) station in fw's station table
+ * @tid: tid of the session
+ * @seq_ctl:
+ * @bitmap: the bitmap of the BA notification as seen in the air
+ * @scd_flow: the tx queue this BA relates to
+ * @scd_ssn: the index of the last contiguously sent packet
+ * @txed: number of Txed frames in this batch
+ * @txed_2_done: number of Acked frames in this batch
+ */
+struct iwl_mvm_ba_notif {
+       __le32 sta_addr_lo32;
+       __le16 sta_addr_hi16;
+       __le16 reserved;
+
+       u8 sta_id;
+       u8 tid;
+       __le16 seq_ctl;
+       __le64 bitmap;
+       __le16 scd_flow;
+       __le16 scd_ssn;
+       u8 txed;
+       u8 txed_2_done;
+       __le16 reserved1;
+} __packed;
+
+/*
+ * struct iwl_mac_beacon_cmd - beacon template command
+ * @tx: the tx commands associated with the beacon frame
+ * @template_id: currently equal to the mac context id of the coresponding
+ *  mac.
+ * @tim_idx: the offset of the tim IE in the beacon
+ * @tim_size: the length of the tim IE
+ * @frame: the template of the beacon frame
+ */
+struct iwl_mac_beacon_cmd {
+       struct iwl_tx_cmd tx;
+       __le32 template_id;
+       __le32 tim_idx;
+       __le32 tim_size;
+       struct ieee80211_hdr frame[0];
+} __packed;
+
+/**
+ * enum iwl_dump_control - dump (flush) control flags
+ * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
+ *     and the TFD queues are empty.
+ */
+enum iwl_dump_control {
+       DUMP_TX_FIFO_FLUSH      = BIT(1),
+};
+
+/**
+ * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command
+ * @queues_ctl: bitmap of queues to flush
+ * @flush_ctl: control flags
+ * @reserved: reserved
+ */
+struct iwl_tx_path_flush_cmd {
+       __le32 queues_ctl;
+       __le16 flush_ctl;
+       __le16 reserved;
+} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */
+
+/**
+ * iwl_mvm_get_scd_ssn - returns the SSN of the SCD
+ * @tx_resp: the Tx response from the fw (agg or non-agg)
+ *
+ * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since
+ * it can't know that everything will go well until the end of the AMPDU, it
+ * can't know in advance the number of MPDUs that will be sent in the current
+ * batch. This is why it writes the agg Tx response while it fetches the MPDUs.
+ * Hence, it can't know in advance what the SSN of the SCD will be at the end
+ * of the batch. This is why the SSN of the SCD is written at the end of the
+ * whole struct at a variable offset. This function knows how to cope with the
+ * variable offset and returns the SSN of the SCD.
+ */
+static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp)
+{
+       return le32_to_cpup((__le32 *)&tx_resp->status +
+                           tx_resp->frame_count) & 0xfff;
+}
+
+#endif /* __fw_api_tx_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
new file mode 100644 (file)
index 0000000..9fd49db
--- /dev/null
@@ -0,0 +1,949 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __fw_api_h__
+#define __fw_api_h__
+
+#include "fw-api-rs.h"
+#include "fw-api-tx.h"
+#include "fw-api-sta.h"
+#include "fw-api-mac.h"
+#include "fw-api-power.h"
+#include "fw-api-d3.h"
+
+/* queue and FIFO numbers by usage */
+enum {
+       IWL_MVM_OFFCHANNEL_QUEUE = 8,
+       IWL_MVM_CMD_QUEUE = 9,
+       IWL_MVM_AUX_QUEUE = 15,
+       IWL_MVM_FIRST_AGG_QUEUE = 16,
+       IWL_MVM_NUM_QUEUES = 20,
+       IWL_MVM_LAST_AGG_QUEUE = IWL_MVM_NUM_QUEUES - 1,
+       IWL_MVM_CMD_FIFO = 7
+};
+
+#define IWL_MVM_STATION_COUNT  16
+
+/* commands */
+enum {
+       MVM_ALIVE = 0x1,
+       REPLY_ERROR = 0x2,
+
+       INIT_COMPLETE_NOTIF = 0x4,
+
+       /* PHY context commands */
+       PHY_CONTEXT_CMD = 0x8,
+       DBG_CFG = 0x9,
+
+       /* station table */
+       ADD_STA = 0x18,
+       REMOVE_STA = 0x19,
+
+       /* TX */
+       TX_CMD = 0x1c,
+       TXPATH_FLUSH = 0x1e,
+       MGMT_MCAST_KEY = 0x1f,
+
+       /* global key */
+       WEP_KEY = 0x20,
+
+       /* MAC and Binding commands */
+       MAC_CONTEXT_CMD = 0x28,
+       TIME_EVENT_CMD = 0x29, /* both CMD and response */
+       TIME_EVENT_NOTIFICATION = 0x2a,
+       BINDING_CONTEXT_CMD = 0x2b,
+       TIME_QUOTA_CMD = 0x2c,
+
+       LQ_CMD = 0x4e,
+
+       /* Calibration */
+       TEMPERATURE_NOTIFICATION = 0x62,
+       CALIBRATION_CFG_CMD = 0x65,
+       CALIBRATION_RES_NOTIFICATION = 0x66,
+       CALIBRATION_COMPLETE_NOTIFICATION = 0x67,
+       RADIO_VERSION_NOTIFICATION = 0x68,
+
+       /* Scan offload */
+       SCAN_OFFLOAD_REQUEST_CMD = 0x51,
+       SCAN_OFFLOAD_ABORT_CMD = 0x52,
+       SCAN_OFFLOAD_COMPLETE = 0x6D,
+       SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
+       SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
+
+       /* Phy */
+       PHY_CONFIGURATION_CMD = 0x6a,
+       CALIB_RES_NOTIF_PHY_DB = 0x6b,
+       /* PHY_DB_CMD = 0x6c, */
+
+       /* Power */
+       POWER_TABLE_CMD = 0x77,
+
+       /* Scanning */
+       SCAN_REQUEST_CMD = 0x80,
+       SCAN_ABORT_CMD = 0x81,
+       SCAN_START_NOTIFICATION = 0x82,
+       SCAN_RESULTS_NOTIFICATION = 0x83,
+       SCAN_COMPLETE_NOTIFICATION = 0x84,
+
+       /* NVM */
+       NVM_ACCESS_CMD = 0x88,
+
+       SET_CALIB_DEFAULT_CMD = 0x8e,
+
+       BEACON_TEMPLATE_CMD = 0x91,
+       TX_ANT_CONFIGURATION_CMD = 0x98,
+       STATISTICS_NOTIFICATION = 0x9d,
+
+       /* RF-KILL commands and notifications */
+       CARD_STATE_CMD = 0xa0,
+       CARD_STATE_NOTIFICATION = 0xa1,
+
+       REPLY_RX_PHY_CMD = 0xc0,
+       REPLY_RX_MPDU_CMD = 0xc1,
+       BA_NOTIF = 0xc5,
+
+       REPLY_DEBUG_CMD = 0xf0,
+       DEBUG_LOG_MSG = 0xf7,
+
+       /* D3 commands/notifications */
+       D3_CONFIG_CMD = 0xd3,
+       PROT_OFFLOAD_CONFIG_CMD = 0xd4,
+       OFFLOADS_QUERY_CMD = 0xd5,
+       REMOTE_WAKE_CONFIG_CMD = 0xd6,
+
+       /* for WoWLAN in particular */
+       WOWLAN_PATTERNS = 0xe0,
+       WOWLAN_CONFIGURATION = 0xe1,
+       WOWLAN_TSC_RSC_PARAM = 0xe2,
+       WOWLAN_TKIP_PARAM = 0xe3,
+       WOWLAN_KEK_KCK_MATERIAL = 0xe4,
+       WOWLAN_GET_STATUSES = 0xe5,
+       WOWLAN_TX_POWER_PER_DB = 0xe6,
+
+       /* and for NetDetect */
+       NET_DETECT_CONFIG_CMD = 0x54,
+       NET_DETECT_PROFILES_QUERY_CMD = 0x56,
+       NET_DETECT_PROFILES_CMD = 0x57,
+       NET_DETECT_HOTSPOTS_CMD = 0x58,
+       NET_DETECT_HOTSPOTS_QUERY_CMD = 0x59,
+
+       REPLY_MAX = 0xff,
+};
+
+/**
+ * struct iwl_cmd_response - generic response struct for most commands
+ * @status: status of the command asked, changes for each one
+ */
+struct iwl_cmd_response {
+       __le32 status;
+};
+
+/*
+ * struct iwl_tx_ant_cfg_cmd
+ * @valid: valid antenna configuration
+ */
+struct iwl_tx_ant_cfg_cmd {
+       __le32 valid;
+} __packed;
+
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ *             flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ *             event triggers.
+ */
+struct iwl_calib_ctrl {
+       __le32 flow_trigger;
+       __le32 event_trigger;
+} __packed;
+
+/* This enum defines the bitmap of various calibrations to enable in both
+ * init ucode and runtime ucode through CALIBRATION_CFG_CMD.
+ */
+enum iwl_calib_cfg {
+       IWL_CALIB_CFG_XTAL_IDX                  = BIT(0),
+       IWL_CALIB_CFG_TEMPERATURE_IDX           = BIT(1),
+       IWL_CALIB_CFG_VOLTAGE_READ_IDX          = BIT(2),
+       IWL_CALIB_CFG_PAPD_IDX                  = BIT(3),
+       IWL_CALIB_CFG_TX_PWR_IDX                = BIT(4),
+       IWL_CALIB_CFG_DC_IDX                    = BIT(5),
+       IWL_CALIB_CFG_BB_FILTER_IDX             = BIT(6),
+       IWL_CALIB_CFG_LO_LEAKAGE_IDX            = BIT(7),
+       IWL_CALIB_CFG_TX_IQ_IDX                 = BIT(8),
+       IWL_CALIB_CFG_TX_IQ_SKEW_IDX            = BIT(9),
+       IWL_CALIB_CFG_RX_IQ_IDX                 = BIT(10),
+       IWL_CALIB_CFG_RX_IQ_SKEW_IDX            = BIT(11),
+       IWL_CALIB_CFG_SENSITIVITY_IDX           = BIT(12),
+       IWL_CALIB_CFG_CHAIN_NOISE_IDX           = BIT(13),
+       IWL_CALIB_CFG_DISCONNECTED_ANT_IDX      = BIT(14),
+       IWL_CALIB_CFG_ANT_COUPLING_IDX          = BIT(15),
+       IWL_CALIB_CFG_DAC_IDX                   = BIT(16),
+       IWL_CALIB_CFG_ABS_IDX                   = BIT(17),
+       IWL_CALIB_CFG_AGC_IDX                   = BIT(18),
+};
+
+/*
+ * Phy configuration command.
+ */
+struct iwl_phy_cfg_cmd {
+       __le32  phy_cfg;
+       struct iwl_calib_ctrl calib_control;
+} __packed;
+
+#define PHY_CFG_RADIO_TYPE     (BIT(0) | BIT(1))
+#define PHY_CFG_RADIO_STEP     (BIT(2) | BIT(3))
+#define PHY_CFG_RADIO_DASH     (BIT(4) | BIT(5))
+#define PHY_CFG_PRODUCT_NUMBER (BIT(6) | BIT(7))
+#define PHY_CFG_TX_CHAIN_A     BIT(8)
+#define PHY_CFG_TX_CHAIN_B     BIT(9)
+#define PHY_CFG_TX_CHAIN_C     BIT(10)
+#define PHY_CFG_RX_CHAIN_A     BIT(12)
+#define PHY_CFG_RX_CHAIN_B     BIT(13)
+#define PHY_CFG_RX_CHAIN_C     BIT(14)
+
+
+/* Target of the NVM_ACCESS_CMD */
+enum {
+       NVM_ACCESS_TARGET_CACHE = 0,
+       NVM_ACCESS_TARGET_OTP = 1,
+       NVM_ACCESS_TARGET_EEPROM = 2,
+};
+
+/**
+ * struct iwl_nvm_access_cmd_ver1 - Request the device to send the NVM.
+ * @op_code: 0 - read, 1 - write.
+ * @target: NVM_ACCESS_TARGET_*. should be 0 for read.
+ * @cache_refresh: 0 - None, 1- NVM.
+ * @offset: offset in the nvm data.
+ * @length: of the chunk.
+ * @data: empty on read, the NVM chunk on write
+ */
+struct iwl_nvm_access_cmd_ver1 {
+       u8 op_code;
+       u8 target;
+       u8 cache_refresh;
+       u8 reserved;
+       __le16 offset;
+       __le16 length;
+       u8 data[];
+} __packed; /* NVM_ACCESS_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_nvm_access_resp_ver1 - response to NVM_ACCESS_CMD
+ * @offset: the offset in the nvm data
+ * @length: of the chunk
+ * @data: the nvm chunk on when NVM_ACCESS_CMD was read, nothing on write
+ */
+struct iwl_nvm_access_resp_ver1 {
+       __le16 offset;
+       __le16 length;
+       u8 data[];
+} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_1 */
+
+/* Section types for NVM_ACCESS_CMD version 2 */
+enum {
+       NVM_SECTION_TYPE_HW = 0,
+       NVM_SECTION_TYPE_SW,
+       NVM_SECTION_TYPE_PAPD,
+       NVM_SECTION_TYPE_BT,
+       NVM_SECTION_TYPE_CALIBRATION,
+       NVM_SECTION_TYPE_PRODUCTION,
+       NVM_SECTION_TYPE_POST_FCS_CALIB,
+       NVM_NUM_OF_SECTIONS,
+};
+
+/**
+ * struct iwl_nvm_access_cmd_ver2 - Request the device to send an NVM section
+ * @op_code: 0 - read, 1 - write
+ * @target: NVM_ACCESS_TARGET_*
+ * @type: NVM_SECTION_TYPE_*
+ * @offset: offset in bytes into the section
+ * @length: in bytes, to read/write
+ * @data: if write operation, the data to write. On read its empty
+ */
+struct iwl_nvm_access_cmd_ver2 {
+       u8 op_code;
+       u8 target;
+       __le16 type;
+       __le16 offset;
+       __le16 length;
+       u8 data[];
+} __packed; /* NVM_ACCESS_CMD_API_S_VER_2 */
+
+/**
+ * struct iwl_nvm_access_resp_ver2 - response to NVM_ACCESS_CMD
+ * @offset: offset in bytes into the section
+ * @length: in bytes, either how much was written or read
+ * @type: NVM_SECTION_TYPE_*
+ * @status: 0 for success, fail otherwise
+ * @data: if read operation, the data returned. Empty on write.
+ */
+struct iwl_nvm_access_resp_ver2 {
+       __le16 offset;
+       __le16 length;
+       __le16 type;
+       __le16 status;
+       u8 data[];
+} __packed; /* NVM_ACCESS_CMD_RESP_API_S_VER_2 */
+
+/* MVM_ALIVE 0x1 */
+
+/* alive response is_valid values */
+#define ALIVE_RESP_UCODE_OK    BIT(0)
+#define ALIVE_RESP_RFKILL      BIT(1)
+
+/* alive response ver_type values */
+enum {
+       FW_TYPE_HW = 0,
+       FW_TYPE_PROT = 1,
+       FW_TYPE_AP = 2,
+       FW_TYPE_WOWLAN = 3,
+       FW_TYPE_TIMING = 4,
+       FW_TYPE_WIPAN = 5
+};
+
+/* alive response ver_subtype values */
+enum {
+       FW_SUBTYPE_FULL_FEATURE = 0,
+       FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */
+       FW_SUBTYPE_REDUCED = 2,
+       FW_SUBTYPE_ALIVE_ONLY = 3,
+       FW_SUBTYPE_WOWLAN = 4,
+       FW_SUBTYPE_AP_SUBTYPE = 5,
+       FW_SUBTYPE_WIPAN = 6,
+       FW_SUBTYPE_INITIALIZE = 9
+};
+
+#define IWL_ALIVE_STATUS_ERR 0xDEAD
+#define IWL_ALIVE_STATUS_OK 0xCAFE
+
+#define IWL_ALIVE_FLG_RFKILL   BIT(0)
+
+struct mvm_alive_resp {
+       __le16 status;
+       __le16 flags;
+       u8 ucode_minor;
+       u8 ucode_major;
+       __le16 id;
+       u8 api_minor;
+       u8 api_major;
+       u8 ver_subtype;
+       u8 ver_type;
+       u8 mac;
+       u8 opt;
+       __le16 reserved2;
+       __le32 timestamp;
+       __le32 error_event_table_ptr;   /* SRAM address for error log */
+       __le32 log_event_table_ptr;     /* SRAM address for event log */
+       __le32 cpu_register_ptr;
+       __le32 dbgm_config_ptr;
+       __le32 alive_counter_ptr;
+       __le32 scd_base_ptr;            /* SRAM address for SCD */
+} __packed; /* ALIVE_RES_API_S_VER_1 */
+
+/* Error response/notification */
+enum {
+       FW_ERR_UNKNOWN_CMD = 0x0,
+       FW_ERR_INVALID_CMD_PARAM = 0x1,
+       FW_ERR_SERVICE = 0x2,
+       FW_ERR_ARC_MEMORY = 0x3,
+       FW_ERR_ARC_CODE = 0x4,
+       FW_ERR_WATCH_DOG = 0x5,
+       FW_ERR_WEP_GRP_KEY_INDX = 0x10,
+       FW_ERR_WEP_KEY_SIZE = 0x11,
+       FW_ERR_OBSOLETE_FUNC = 0x12,
+       FW_ERR_UNEXPECTED = 0xFE,
+       FW_ERR_FATAL = 0xFF
+};
+
+/**
+ * struct iwl_error_resp - FW error indication
+ * ( REPLY_ERROR = 0x2 )
+ * @error_type: one of FW_ERR_*
+ * @cmd_id: the command ID for which the error occured
+ * @bad_cmd_seq_num: sequence number of the erroneous command
+ * @error_service: which service created the error, applicable only if
+ *     error_type = 2, otherwise 0
+ * @timestamp: TSF in usecs.
+ */
+struct iwl_error_resp {
+       __le32 error_type;
+       u8 cmd_id;
+       u8 reserved1;
+       __le16 bad_cmd_seq_num;
+       __le32 error_service;
+       __le64 timestamp;
+} __packed;
+
+
+/* Common PHY, MAC and Bindings definitions */
+
+#define MAX_MACS_IN_BINDING    (3)
+#define MAX_BINDINGS           (4)
+#define AUX_BINDING_INDEX      (3)
+#define MAX_PHYS               (4)
+
+/* Used to extract ID and color from the context dword */
+#define FW_CTXT_ID_POS   (0)
+#define FW_CTXT_ID_MSK   (0xff << FW_CTXT_ID_POS)
+#define FW_CTXT_COLOR_POS (8)
+#define FW_CTXT_COLOR_MSK (0xff << FW_CTXT_COLOR_POS)
+#define FW_CTXT_INVALID          (0xffffffff)
+
+#define FW_CMD_ID_AND_COLOR(_id, _color) ((_id << FW_CTXT_ID_POS) |\
+                                         (_color << FW_CTXT_COLOR_POS))
+
+/* Possible actions on PHYs, MACs and Bindings */
+enum {
+       FW_CTXT_ACTION_STUB = 0,
+       FW_CTXT_ACTION_ADD,
+       FW_CTXT_ACTION_MODIFY,
+       FW_CTXT_ACTION_REMOVE,
+       FW_CTXT_ACTION_NUM
+}; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */
+
+/* Time Events */
+
+/* Time Event types, according to MAC type */
+enum iwl_time_event_type {
+       /* BSS Station Events */
+       TE_BSS_STA_AGGRESSIVE_ASSOC,
+       TE_BSS_STA_ASSOC,
+       TE_BSS_EAP_DHCP_PROT,
+       TE_BSS_QUIET_PERIOD,
+
+       /* P2P Device Events */
+       TE_P2P_DEVICE_DISCOVERABLE,
+       TE_P2P_DEVICE_LISTEN,
+       TE_P2P_DEVICE_ACTION_SCAN,
+       TE_P2P_DEVICE_FULL_SCAN,
+
+       /* P2P Client Events */
+       TE_P2P_CLIENT_AGGRESSIVE_ASSOC,
+       TE_P2P_CLIENT_ASSOC,
+       TE_P2P_CLIENT_QUIET_PERIOD,
+
+       /* P2P GO Events */
+       TE_P2P_GO_ASSOC_PROT,
+       TE_P2P_GO_REPETITIVE_NOA,
+       TE_P2P_GO_CT_WINDOW,
+
+       /* WiDi Sync Events */
+       TE_WIDI_TX_SYNC,
+
+       TE_MAX
+}; /* MAC_EVENT_TYPE_API_E_VER_1 */
+
+/* Time Event dependencies: none, on another TE, or in a specific time */
+enum {
+       TE_INDEPENDENT          = 0,
+       TE_DEP_OTHER            = 1,
+       TE_DEP_TSF              = 2,
+       TE_EVENT_SOCIOPATHIC    = 4,
+}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */
+
+/* When to send Time Event notifications and to whom (internal = FW) */
+enum {
+       TE_NOTIF_NONE = 0,
+       TE_NOTIF_HOST_START = 0x1,
+       TE_NOTIF_HOST_END = 0x2,
+       TE_NOTIF_INTERNAL_START = 0x4,
+       TE_NOTIF_INTERNAL_END = 0x8
+}; /* MAC_EVENT_ACTION_API_E_VER_1 */
+
+/*
+ * @TE_FRAG_NONE: fragmentation of the time event is NOT allowed.
+ * @TE_FRAG_SINGLE: fragmentation of the time event is allowed, but only
+ *  the first fragment is scheduled.
+ * @TE_FRAG_DUAL: fragmentation of the time event is allowed, but only
+ *  the first 2 fragments are scheduled.
+ * @TE_FRAG_ENDLESS: fragmentation of the time event is allowed, and any number
+ *  of fragments are valid.
+ *
+ * Other than the constant defined above, specifying a fragmentation value 'x'
+ * means that the event can be fragmented but only the first 'x' will be
+ * scheduled.
+ */
+enum {
+       TE_FRAG_NONE = 0,
+       TE_FRAG_SINGLE = 1,
+       TE_FRAG_DUAL = 2,
+       TE_FRAG_ENDLESS = 0xffffffff
+};
+
+/* Repeat the time event endlessly (until removed) */
+#define TE_REPEAT_ENDLESS      (0xffffffff)
+/* If a Time Event has bounded repetitions, this is the maximal value */
+#define TE_REPEAT_MAX_MSK      (0x0fffffff)
+/* If a Time Event can be fragmented, this is the max number of fragments */
+#define TE_FRAG_MAX_MSK                (0x0fffffff)
+
+/**
+ * struct iwl_time_event_cmd - configuring Time Events
+ * ( TIME_EVENT_CMD = 0x29 )
+ * @id_and_color: ID and color of the relevant MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @id: this field has two meanings, depending on the action:
+ *     If the action is ADD, then it means the type of event to add.
+ *     For all other actions it is the unique event ID assigned when the
+ *     event was added by the FW.
+ * @apply_time: When to start the Time Event (in GP2)
+ * @max_delay: maximum delay to event's start (apply time), in TU
+ * @depends_on: the unique ID of the event we depend on (if any)
+ * @interval: interval between repetitions, in TU
+ * @interval_reciprocal: 2^32 / interval
+ * @duration: duration of event in TU
+ * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS
+ * @dep_policy: one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
+ * @is_present: 0 or 1, are we present or absent during the Time Event
+ * @max_frags: maximal number of fragments the Time Event can be divided to
+ * @notify: notifications using TE_NOTIF_* (whom to notify when)
+ */
+struct iwl_time_event_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       __le32 id;
+       /* MAC_TIME_EVENT_DATA_API_S_VER_1 */
+       __le32 apply_time;
+       __le32 max_delay;
+       __le32 dep_policy;
+       __le32 depends_on;
+       __le32 is_present;
+       __le32 max_frags;
+       __le32 interval;
+       __le32 interval_reciprocal;
+       __le32 duration;
+       __le32 repeat;
+       __le32 notify;
+} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_time_event_resp - response structure to iwl_time_event_cmd
+ * @status: bit 0 indicates success, all others specify errors
+ * @id: the Time Event type
+ * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE
+ * @id_and_color: ID and color of the relevant MAC
+ */
+struct iwl_time_event_resp {
+       __le32 status;
+       __le32 id;
+       __le32 unique_id;
+       __le32 id_and_color;
+} __packed; /* MAC_TIME_EVENT_RSP_API_S_VER_1 */
+
+/**
+ * struct iwl_time_event_notif - notifications of time event start/stop
+ * ( TIME_EVENT_NOTIFICATION = 0x2a )
+ * @timestamp: action timestamp in GP2
+ * @session_id: session's unique id
+ * @unique_id: unique id of the Time Event itself
+ * @id_and_color: ID and color of the relevant MAC
+ * @action: one of TE_NOTIF_START or TE_NOTIF_END
+ * @status: true if scheduled, false otherwise (not executed)
+ */
+struct iwl_time_event_notif {
+       __le32 timestamp;
+       __le32 session_id;
+       __le32 unique_id;
+       __le32 id_and_color;
+       __le32 action;
+       __le32 status;
+} __packed; /* MAC_TIME_EVENT_NTFY_API_S_VER_1 */
+
+
+/* Bindings and Time Quota */
+
+/**
+ * struct iwl_binding_cmd - configuring bindings
+ * ( BINDING_CONTEXT_CMD = 0x2b )
+ * @id_and_color: ID and color of the relevant Binding
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @macs: array of MAC id and colors which belong to the binding
+ * @phy: PHY id and color which belongs to the binding
+ */
+struct iwl_binding_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       /* BINDING_DATA_API_S_VER_1 */
+       __le32 macs[MAX_MACS_IN_BINDING];
+       __le32 phy;
+} __packed; /* BINDING_CMD_API_S_VER_1 */
+
+/**
+ * struct iwl_time_quota_data - configuration of time quota per binding
+ * @id_and_color: ID and color of the relevant Binding
+ * @quota: absolute time quota in TU. The scheduler will try to divide the
+ *     remainig quota (after Time Events) according to this quota.
+ * @max_duration: max uninterrupted context duration in TU
+ */
+struct iwl_time_quota_data {
+       __le32 id_and_color;
+       __le32 quota;
+       __le32 max_duration;
+} __packed; /* TIME_QUOTA_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_time_quota_cmd - configuration of time quota between bindings
+ * ( TIME_QUOTA_CMD = 0x2c )
+ * @quotas: allocations per binding
+ */
+struct iwl_time_quota_cmd {
+       struct iwl_time_quota_data quotas[MAX_BINDINGS];
+} __packed; /* TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */
+
+
+/* PHY context */
+
+/* Supported bands */
+#define PHY_BAND_5  (0)
+#define PHY_BAND_24 (1)
+
+/* Supported channel width, vary if there is VHT support */
+#define PHY_VHT_CHANNEL_MODE20 (0x0)
+#define PHY_VHT_CHANNEL_MODE40 (0x1)
+#define PHY_VHT_CHANNEL_MODE80 (0x2)
+#define PHY_VHT_CHANNEL_MODE160        (0x3)
+
+/*
+ * Control channel position:
+ * For legacy set bit means upper channel, otherwise lower.
+ * For VHT - bit-2 marks if the control is lower/upper relative to center-freq
+ *   bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0.
+ *                                   center_freq
+ *                                        |
+ * 40Mhz                          |_______|_______|
+ * 80Mhz                  |_______|_______|_______|_______|
+ * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______|
+ * code      011     010     001     000  |  100     101     110    111
+ */
+#define PHY_VHT_CTRL_POS_1_BELOW  (0x0)
+#define PHY_VHT_CTRL_POS_2_BELOW  (0x1)
+#define PHY_VHT_CTRL_POS_3_BELOW  (0x2)
+#define PHY_VHT_CTRL_POS_4_BELOW  (0x3)
+#define PHY_VHT_CTRL_POS_1_ABOVE  (0x4)
+#define PHY_VHT_CTRL_POS_2_ABOVE  (0x5)
+#define PHY_VHT_CTRL_POS_3_ABOVE  (0x6)
+#define PHY_VHT_CTRL_POS_4_ABOVE  (0x7)
+
+/*
+ * @band: PHY_BAND_*
+ * @channel: channel number
+ * @width: PHY_[VHT|LEGACY]_CHANNEL_*
+ * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_*
+ */
+struct iwl_fw_channel_info {
+       u8 band;
+       u8 channel;
+       u8 width;
+       u8 ctrl_pos;
+} __packed;
+
+#define PHY_RX_CHAIN_DRIVER_FORCE_POS  (0)
+#define PHY_RX_CHAIN_DRIVER_FORCE_MSK \
+       (0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS)
+#define PHY_RX_CHAIN_VALID_POS         (1)
+#define PHY_RX_CHAIN_VALID_MSK \
+       (0x7 << PHY_RX_CHAIN_VALID_POS)
+#define PHY_RX_CHAIN_FORCE_SEL_POS     (4)
+#define PHY_RX_CHAIN_FORCE_SEL_MSK \
+       (0x7 << PHY_RX_CHAIN_FORCE_SEL_POS)
+#define PHY_RX_CHAIN_FORCE_MIMO_SEL_POS        (7)
+#define PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \
+       (0x7 << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS)
+#define PHY_RX_CHAIN_CNT_POS           (10)
+#define PHY_RX_CHAIN_CNT_MSK \
+       (0x3 << PHY_RX_CHAIN_CNT_POS)
+#define PHY_RX_CHAIN_MIMO_CNT_POS      (12)
+#define PHY_RX_CHAIN_MIMO_CNT_MSK \
+       (0x3 << PHY_RX_CHAIN_MIMO_CNT_POS)
+#define PHY_RX_CHAIN_MIMO_FORCE_POS    (14)
+#define PHY_RX_CHAIN_MIMO_FORCE_MSK \
+       (0x1 << PHY_RX_CHAIN_MIMO_FORCE_POS)
+
+/* TODO: fix the value, make it depend on firmware at runtime? */
+#define NUM_PHY_CTX    3
+
+/* TODO: complete missing documentation */
+/**
+ * struct iwl_phy_context_cmd - config of the PHY context
+ * ( PHY_CONTEXT_CMD = 0x8 )
+ * @id_and_color: ID and color of the relevant Binding
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @apply_time: 0 means immediate apply and context switch.
+ *     other value means apply new params after X usecs
+ * @tx_param_color: ???
+ * @channel_info:
+ * @txchain_info: ???
+ * @rxchain_info: ???
+ * @acquisition_data: ???
+ * @dsp_cfg_flags: set to 0
+ */
+struct iwl_phy_context_cmd {
+       /* COMMON_INDEX_HDR_API_S_VER_1 */
+       __le32 id_and_color;
+       __le32 action;
+       /* PHY_CONTEXT_DATA_API_S_VER_1 */
+       __le32 apply_time;
+       __le32 tx_param_color;
+       struct iwl_fw_channel_info ci;
+       __le32 txchain_info;
+       __le32 rxchain_info;
+       __le32 acquisition_data;
+       __le32 dsp_cfg_flags;
+} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
+
+#define IWL_RX_INFO_PHY_CNT 8
+#define IWL_RX_INFO_AGC_IDX 1
+#define IWL_RX_INFO_RSSI_AB_IDX 2
+#define IWL_RX_INFO_RSSI_C_IDX 3
+#define IWL_OFDM_AGC_DB_MSK 0xfe00
+#define IWL_OFDM_AGC_DB_POS 9
+#define IWL_OFDM_RSSI_INBAND_A_MSK 0x00ff
+#define IWL_OFDM_RSSI_ALLBAND_A_MSK 0xff00
+#define IWL_OFDM_RSSI_A_POS 0
+#define IWL_OFDM_RSSI_INBAND_B_MSK 0xff0000
+#define IWL_OFDM_RSSI_ALLBAND_B_MSK 0xff000000
+#define IWL_OFDM_RSSI_B_POS 16
+#define IWL_OFDM_RSSI_INBAND_C_MSK 0x00ff
+#define IWL_OFDM_RSSI_ALLBAND_C_MSK 0xff00
+#define IWL_OFDM_RSSI_C_POS 0
+
+/**
+ * struct iwl_rx_phy_info - phy info
+ * (REPLY_RX_PHY_CMD = 0xc0)
+ * @non_cfg_phy_cnt: non configurable DSP phy data byte count
+ * @cfg_phy_cnt: configurable DSP phy data byte count
+ * @stat_id: configurable DSP phy data set ID
+ * @reserved1:
+ * @system_timestamp: GP2  at on air rise
+ * @timestamp: TSF at on air rise
+ * @beacon_time_stamp: beacon at on-air rise
+ * @phy_flags: general phy flags: band, modulation, ...
+ * @channel: channel number
+ * @non_cfg_phy_buf: for various implementations of non_cfg_phy
+ * @rate_n_flags: RATE_MCS_*
+ * @byte_count: frame's byte-count
+ * @frame_time: frame's time on the air, based on byte count and frame rate
+ *     calculation
+ *
+ * Before each Rx, the device sends this data. It contains PHY information
+ * about the reception of the packet.
+ */
+struct iwl_rx_phy_info {
+       u8 non_cfg_phy_cnt;
+       u8 cfg_phy_cnt;
+       u8 stat_id;
+       u8 reserved1;
+       __le32 system_timestamp;
+       __le64 timestamp;
+       __le32 beacon_time_stamp;
+       __le16 phy_flags;
+       __le16 channel;
+       __le32 non_cfg_phy[IWL_RX_INFO_PHY_CNT];
+       __le32 rate_n_flags;
+       __le32 byte_count;
+       __le16 reserved2;
+       __le16 frame_time;
+} __packed;
+
+struct iwl_rx_mpdu_res_start {
+       __le16 byte_count;
+       __le16 reserved;
+} __packed;
+
+/**
+ * enum iwl_rx_phy_flags - to parse %iwl_rx_phy_info phy_flags
+ * @RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band
+ * @RX_RES_PHY_FLAGS_MOD_CCK:
+ * @RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short
+ * @RX_RES_PHY_FLAGS_NARROW_BAND:
+ * @RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received
+ * @RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU
+ * @RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame
+ * @RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble
+ * @RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame
+ */
+enum iwl_rx_phy_flags {
+       RX_RES_PHY_FLAGS_BAND_24        = BIT(0),
+       RX_RES_PHY_FLAGS_MOD_CCK        = BIT(1),
+       RX_RES_PHY_FLAGS_SHORT_PREAMBLE = BIT(2),
+       RX_RES_PHY_FLAGS_NARROW_BAND    = BIT(3),
+       RX_RES_PHY_FLAGS_ANTENNA        = (0x7 << 4),
+       RX_RES_PHY_FLAGS_ANTENNA_POS    = 4,
+       RX_RES_PHY_FLAGS_AGG            = BIT(7),
+       RX_RES_PHY_FLAGS_OFDM_HT        = BIT(8),
+       RX_RES_PHY_FLAGS_OFDM_GF        = BIT(9),
+       RX_RES_PHY_FLAGS_OFDM_VHT       = BIT(10),
+};
+
+/**
+ * enum iwl_mvm_rx_status - written by fw for each Rx packet
+ * @RX_MPDU_RES_STATUS_CRC_OK: CRC is fine
+ * @RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow
+ * @RX_MPDU_RES_STATUS_SRC_STA_FOUND:
+ * @RX_MPDU_RES_STATUS_KEY_VALID:
+ * @RX_MPDU_RES_STATUS_KEY_PARAM_OK:
+ * @RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed
+ * @RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked
+ *     in the driver.
+ * @RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine
+ * @RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR:  valid for alg = CCM_CMAC or
+ *     alg = CCM only. Checks replay attack for 11w frames. Relevant only if
+ *     %RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set.
+ * @RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted
+ * @RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP
+ * @RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM
+ * @RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP
+ * @RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC
+ * @RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted
+ * @RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm
+ * @RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted
+ * @RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP:
+ * @RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP:
+ * @RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT:
+ * @RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame
+ * @RX_MPDU_RES_STATUS_HASH_INDEX_MSK:
+ * @RX_MPDU_RES_STATUS_STA_ID_MSK:
+ * @RX_MPDU_RES_STATUS_RRF_KILL:
+ * @RX_MPDU_RES_STATUS_FILTERING_MSK:
+ * @RX_MPDU_RES_STATUS2_FILTERING_MSK:
+ */
+enum iwl_mvm_rx_status {
+       RX_MPDU_RES_STATUS_CRC_OK                       = BIT(0),
+       RX_MPDU_RES_STATUS_OVERRUN_OK                   = BIT(1),
+       RX_MPDU_RES_STATUS_SRC_STA_FOUND                = BIT(2),
+       RX_MPDU_RES_STATUS_KEY_VALID                    = BIT(3),
+       RX_MPDU_RES_STATUS_KEY_PARAM_OK                 = BIT(4),
+       RX_MPDU_RES_STATUS_ICV_OK                       = BIT(5),
+       RX_MPDU_RES_STATUS_MIC_OK                       = BIT(6),
+       RX_MPDU_RES_STATUS_TTAK_OK                      = BIT(7),
+       RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR         = BIT(7),
+       RX_MPDU_RES_STATUS_SEC_NO_ENC                   = (0 << 8),
+       RX_MPDU_RES_STATUS_SEC_WEP_ENC                  = (1 << 8),
+       RX_MPDU_RES_STATUS_SEC_CCM_ENC                  = (2 << 8),
+       RX_MPDU_RES_STATUS_SEC_TKIP_ENC                 = (3 << 8),
+       RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC             = (6 << 8),
+       RX_MPDU_RES_STATUS_SEC_ENC_ERR                  = (7 << 8),
+       RX_MPDU_RES_STATUS_SEC_ENC_MSK                  = (7 << 8),
+       RX_MPDU_RES_STATUS_DEC_DONE                     = BIT(11),
+       RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP        = BIT(12),
+       RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP               = BIT(13),
+       RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT               = BIT(14),
+       RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME             = BIT(15),
+       RX_MPDU_RES_STATUS_HASH_INDEX_MSK               = (0x3F0000),
+       RX_MPDU_RES_STATUS_STA_ID_MSK                   = (0x1f000000),
+       RX_MPDU_RES_STATUS_RRF_KILL                     = BIT(29),
+       RX_MPDU_RES_STATUS_FILTERING_MSK                = (0xc00000),
+       RX_MPDU_RES_STATUS2_FILTERING_MSK               = (0xc0000000),
+};
+
+/**
+ * struct iwl_radio_version_notif - information on the radio version
+ * ( RADIO_VERSION_NOTIFICATION = 0x68 )
+ * @radio_flavor:
+ * @radio_step:
+ * @radio_dash:
+ */
+struct iwl_radio_version_notif {
+       __le32 radio_flavor;
+       __le32 radio_step;
+       __le32 radio_dash;
+} __packed; /* RADIO_VERSION_NOTOFICATION_S_VER_1 */
+
+enum iwl_card_state_flags {
+       CARD_ENABLED            = 0x00,
+       HW_CARD_DISABLED        = 0x01,
+       SW_CARD_DISABLED        = 0x02,
+       CT_KILL_CARD_DISABLED   = 0x04,
+       HALT_CARD_DISABLED      = 0x08,
+       CARD_DISABLED_MSK       = 0x0f,
+       CARD_IS_RX_ON           = 0x10,
+};
+
+/**
+ * struct iwl_radio_version_notif - information on the radio version
+ * ( CARD_STATE_NOTIFICATION = 0xa1 )
+ * @flags: %iwl_card_state_flags
+ */
+struct iwl_card_state_notif {
+       __le32 flags;
+} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
+
+/**
+ * struct iwl_set_calib_default_cmd - set default value for calibration.
+ * ( SET_CALIB_DEFAULT_CMD = 0x8e )
+ * @calib_index: the calibration to set value for
+ * @length: of data
+ * @data: the value to set for the calibration result
+ */
+struct iwl_set_calib_default_cmd {
+       __le16 calib_index;
+       __le16 length;
+       u8 data[0];
+} __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
+
+#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
new file mode 100644 (file)
index 0000000..90473c2
--- /dev/null
@@ -0,0 +1,644 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-fw.h"
+#include "iwl-debug.h"
+#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */
+#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */
+#include "iwl-eeprom-parse.h"
+
+#include "mvm.h"
+#include "iwl-phy-db.h"
+
+#define MVM_UCODE_ALIVE_TIMEOUT        HZ
+#define MVM_UCODE_CALIB_TIMEOUT        (2*HZ)
+
+#define UCODE_VALID_OK cpu_to_le32(0x1)
+
+/* Default calibration values for WkP - set to INIT image w/o running */
+static const u8 wkp_calib_values_bb_filter[] = { 0xbf, 0x00, 0x5f, 0x00, 0x2f,
+                                                0x00, 0x18, 0x00 };
+static const u8 wkp_calib_values_rx_dc[] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+                                            0x7f, 0x7f, 0x7f };
+static const u8 wkp_calib_values_tx_lo[] = { 0x00, 0x00, 0x00, 0x00 };
+static const u8 wkp_calib_values_tx_iq[] = { 0xff, 0x00, 0xff, 0x00, 0x00,
+                                            0x00 };
+static const u8 wkp_calib_values_rx_iq[] = { 0xff, 0x00, 0x00, 0x00 };
+static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 };
+static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 };
+static const u8 wkp_calib_values_xtal[] = { 0xd2, 0xd2 };
+
+struct iwl_calib_default_data {
+       u16 size;
+       void *data;
+};
+
+#define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf}
+
+static const struct iwl_calib_default_data wkp_calib_default_data[12] = {
+       [5] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_dc),
+       [6] = CALIB_SIZE_N_DATA(wkp_calib_values_bb_filter),
+       [7] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_lo),
+       [8] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq),
+       [9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew),
+       [10] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq),
+       [11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew),
+};
+
+struct iwl_mvm_alive_data {
+       bool valid;
+       u32 scd_base_addr;
+};
+
+static inline const struct fw_img *
+iwl_get_ucode_image(struct iwl_mvm *mvm, enum iwl_ucode_type ucode_type)
+{
+       if (ucode_type >= IWL_UCODE_TYPE_MAX)
+               return NULL;
+
+       return &mvm->fw->img[ucode_type];
+}
+
+static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant)
+{
+       struct iwl_tx_ant_cfg_cmd tx_ant_cmd = {
+               .valid = cpu_to_le32(valid_tx_ant),
+       };
+
+       IWL_DEBUG_HC(mvm, "select valid tx ant: %u\n", valid_tx_ant);
+       return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC,
+                                   sizeof(tx_ant_cmd), &tx_ant_cmd);
+}
+
+static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
+                        struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       struct iwl_mvm_alive_data *alive_data = data;
+       struct mvm_alive_resp *palive;
+
+       palive = (void *)pkt->data;
+
+       mvm->error_event_table = le32_to_cpu(palive->error_event_table_ptr);
+       mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr);
+       alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
+
+       alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK;
+       IWL_DEBUG_FW(mvm, "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n",
+                    le16_to_cpu(palive->status), palive->ver_type,
+                    palive->ver_subtype);
+
+       return true;
+}
+
+static bool iwl_wait_phy_db_entry(struct iwl_notif_wait_data *notif_wait,
+                                 struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_phy_db *phy_db = data;
+
+       if (pkt->hdr.cmd != CALIB_RES_NOTIF_PHY_DB) {
+               WARN_ON(pkt->hdr.cmd != INIT_COMPLETE_NOTIF);
+               return true;
+       }
+
+       WARN_ON(iwl_phy_db_set_section(phy_db, pkt, GFP_ATOMIC));
+
+       return false;
+}
+
+static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
+                                        enum iwl_ucode_type ucode_type)
+{
+       struct iwl_notification_wait alive_wait;
+       struct iwl_mvm_alive_data alive_data;
+       const struct fw_img *fw;
+       int ret, i;
+       enum iwl_ucode_type old_type = mvm->cur_ucode;
+       static const u8 alive_cmd[] = { MVM_ALIVE };
+
+       mvm->cur_ucode = ucode_type;
+       fw = iwl_get_ucode_image(mvm, ucode_type);
+
+       mvm->ucode_loaded = false;
+
+       if (!fw)
+               return -EINVAL;
+
+       iwl_init_notification_wait(&mvm->notif_wait, &alive_wait,
+                                  alive_cmd, ARRAY_SIZE(alive_cmd),
+                                  iwl_alive_fn, &alive_data);
+
+       ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT);
+       if (ret) {
+               mvm->cur_ucode = old_type;
+               iwl_remove_notification(&mvm->notif_wait, &alive_wait);
+               return ret;
+       }
+
+       /*
+        * Some things may run in the background now, but we
+        * just wait for the ALIVE notification here.
+        */
+       ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait,
+                                   MVM_UCODE_ALIVE_TIMEOUT);
+       if (ret) {
+               mvm->cur_ucode = old_type;
+               return ret;
+       }
+
+       if (!alive_data.valid) {
+               IWL_ERR(mvm, "Loaded ucode is not valid!\n");
+               mvm->cur_ucode = old_type;
+               return -EIO;
+       }
+
+       iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
+
+       /*
+        * Note: all the queues are enabled as part of the interface
+        * initialization, but in firmware restart scenarios they
+        * could be stopped, so wake them up. In firmware restart,
+        * mac80211 will have the queues stopped as well until the
+        * reconfiguration completes. During normal startup, they
+        * will be empty.
+        */
+
+       for (i = 0; i < IWL_MAX_HW_QUEUES; i++) {
+               if (i < IWL_MVM_FIRST_AGG_QUEUE && i != IWL_MVM_CMD_QUEUE)
+                       mvm->queue_to_mac80211[i] = i;
+               else
+                       mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE;
+               atomic_set(&mvm->queue_stop_count[i], 0);
+       }
+
+       mvm->transport_queue_stop = 0;
+
+       mvm->ucode_loaded = true;
+
+       return 0;
+}
+#define IWL_HW_REV_ID_RAINBOW  0x2
+#define IWL_PROJ_TYPE_LHP      0x5
+
+static u32 iwl_mvm_build_phy_cfg(struct iwl_mvm *mvm)
+{
+       struct iwl_nvm_data *data = mvm->nvm_data;
+       /* Temp calls to static definitions, will be changed to CSR calls */
+       u8 hw_rev_id = IWL_HW_REV_ID_RAINBOW;
+       u8 project_type = IWL_PROJ_TYPE_LHP;
+
+       return data->radio_cfg_dash | (data->radio_cfg_step << 2) |
+               (hw_rev_id << 4) | ((project_type & 0x7f) << 6) |
+               (data->valid_tx_ant << 16) | (data->valid_rx_ant << 20);
+}
+
+static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm)
+{
+       struct iwl_phy_cfg_cmd phy_cfg_cmd;
+       enum iwl_ucode_type ucode_type = mvm->cur_ucode;
+
+       /* Set parameters */
+       phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_build_phy_cfg(mvm));
+       phy_cfg_cmd.calib_control.event_trigger =
+               mvm->fw->default_calib[ucode_type].event_trigger;
+       phy_cfg_cmd.calib_control.flow_trigger =
+               mvm->fw->default_calib[ucode_type].flow_trigger;
+
+       IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n",
+                      phy_cfg_cmd.phy_cfg);
+
+       return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, CMD_SYNC,
+                                   sizeof(phy_cfg_cmd), &phy_cfg_cmd);
+}
+
+/* Starting with the new PHY DB implementation - New calibs are enabled */
+/* Value - 0x405e7 */
+#define IWL_CALIB_DEFAULT_FLOW_INIT    (IWL_CALIB_CFG_XTAL_IDX         |\
+                                        IWL_CALIB_CFG_TEMPERATURE_IDX  |\
+                                        IWL_CALIB_CFG_VOLTAGE_READ_IDX |\
+                                        IWL_CALIB_CFG_DC_IDX           |\
+                                        IWL_CALIB_CFG_BB_FILTER_IDX    |\
+                                        IWL_CALIB_CFG_LO_LEAKAGE_IDX   |\
+                                        IWL_CALIB_CFG_TX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_RX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_AGC_IDX)
+
+#define IWL_CALIB_DEFAULT_EVENT_INIT   0x0
+
+/* Value 0x41567 */
+#define IWL_CALIB_DEFAULT_FLOW_RUN     (IWL_CALIB_CFG_XTAL_IDX         |\
+                                        IWL_CALIB_CFG_TEMPERATURE_IDX  |\
+                                        IWL_CALIB_CFG_VOLTAGE_READ_IDX |\
+                                        IWL_CALIB_CFG_BB_FILTER_IDX    |\
+                                        IWL_CALIB_CFG_DC_IDX           |\
+                                        IWL_CALIB_CFG_TX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_RX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_SENSITIVITY_IDX  |\
+                                        IWL_CALIB_CFG_AGC_IDX)
+
+#define IWL_CALIB_DEFAULT_EVENT_RUN    (IWL_CALIB_CFG_XTAL_IDX         |\
+                                        IWL_CALIB_CFG_TEMPERATURE_IDX  |\
+                                        IWL_CALIB_CFG_VOLTAGE_READ_IDX |\
+                                        IWL_CALIB_CFG_TX_PWR_IDX       |\
+                                        IWL_CALIB_CFG_DC_IDX           |\
+                                        IWL_CALIB_CFG_TX_IQ_IDX        |\
+                                        IWL_CALIB_CFG_SENSITIVITY_IDX)
+
+/*
+ * Sets the calibrations trigger values that will be sent to the FW for runtime
+ * and init calibrations.
+ * The ones given in the FW TLV are not correct.
+ */
+static void iwl_set_default_calib_trigger(struct iwl_mvm *mvm)
+{
+       struct iwl_tlv_calib_ctrl default_calib;
+
+       /*
+        * WkP FW TLV calib bits are wrong, overwrite them.
+        * This defines the dynamic calibrations which are implemented in the
+        * uCode both for init(flow) calculation and event driven calibs.
+        */
+
+       /* Init Image */
+       default_calib.event_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_EVENT_INIT);
+       default_calib.flow_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_FLOW_INIT);
+
+       if (default_calib.event_trigger !=
+           mvm->fw->default_calib[IWL_UCODE_INIT].event_trigger)
+               IWL_ERR(mvm,
+                       "Updating the event calib for INIT image: 0x%x -> 0x%x\n",
+                       mvm->fw->default_calib[IWL_UCODE_INIT].event_trigger,
+                       default_calib.event_trigger);
+       if (default_calib.flow_trigger !=
+           mvm->fw->default_calib[IWL_UCODE_INIT].flow_trigger)
+               IWL_ERR(mvm,
+                       "Updating the flow calib for INIT image: 0x%x -> 0x%x\n",
+                       mvm->fw->default_calib[IWL_UCODE_INIT].flow_trigger,
+                       default_calib.flow_trigger);
+
+       memcpy((void *)&mvm->fw->default_calib[IWL_UCODE_INIT],
+              &default_calib, sizeof(struct iwl_tlv_calib_ctrl));
+       IWL_ERR(mvm,
+               "Setting uCode init calibrations event 0x%x, trigger 0x%x\n",
+               default_calib.event_trigger,
+               default_calib.flow_trigger);
+
+       /* Run time image */
+       default_calib.event_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_EVENT_RUN);
+       default_calib.flow_trigger = cpu_to_le32(IWL_CALIB_DEFAULT_FLOW_RUN);
+
+       if (default_calib.event_trigger !=
+           mvm->fw->default_calib[IWL_UCODE_REGULAR].event_trigger)
+               IWL_ERR(mvm,
+                       "Updating the event calib for RT image: 0x%x -> 0x%x\n",
+                       mvm->fw->default_calib[IWL_UCODE_REGULAR].event_trigger,
+                       default_calib.event_trigger);
+       if (default_calib.flow_trigger !=
+           mvm->fw->default_calib[IWL_UCODE_REGULAR].flow_trigger)
+               IWL_ERR(mvm,
+                       "Updating the flow calib for RT image: 0x%x -> 0x%x\n",
+                       mvm->fw->default_calib[IWL_UCODE_REGULAR].flow_trigger,
+                       default_calib.flow_trigger);
+
+       memcpy((void *)&mvm->fw->default_calib[IWL_UCODE_REGULAR],
+              &default_calib, sizeof(struct iwl_tlv_calib_ctrl));
+       IWL_ERR(mvm,
+               "Setting uCode runtime calibs event 0x%x, trigger 0x%x\n",
+               default_calib.event_trigger,
+               default_calib.flow_trigger);
+}
+
+static int iwl_set_default_calibrations(struct iwl_mvm *mvm)
+{
+       u8 cmd_raw[16]; /* holds the variable size commands */
+       struct iwl_set_calib_default_cmd *cmd =
+               (struct iwl_set_calib_default_cmd *)cmd_raw;
+       int ret, i;
+
+       /* Setting default values for calibrations we don't run */
+       for (i = 0; i < ARRAY_SIZE(wkp_calib_default_data); i++) {
+               u16 cmd_len;
+
+               if (wkp_calib_default_data[i].size == 0)
+                       continue;
+
+               memset(cmd_raw, 0, sizeof(cmd_raw));
+               cmd_len = wkp_calib_default_data[i].size + sizeof(cmd);
+               cmd->calib_index = cpu_to_le16(i);
+               cmd->length = cpu_to_le16(wkp_calib_default_data[i].size);
+               if (WARN_ONCE(cmd_len > sizeof(cmd_raw),
+                             "Need to enlarge cmd_raw to %d\n", cmd_len))
+                       break;
+               memcpy(cmd->data, wkp_calib_default_data[i].data,
+                      wkp_calib_default_data[i].size);
+               ret = iwl_mvm_send_cmd_pdu(mvm, SET_CALIB_DEFAULT_CMD, 0,
+                                          sizeof(*cmd) +
+                                          wkp_calib_default_data[i].size,
+                                          cmd);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
+{
+       struct iwl_notification_wait calib_wait;
+       static const u8 init_complete[] = {
+               INIT_COMPLETE_NOTIF,
+               CALIB_RES_NOTIF_PHY_DB
+       };
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (mvm->init_ucode_run)
+               return 0;
+
+       iwl_init_notification_wait(&mvm->notif_wait,
+                                  &calib_wait,
+                                  init_complete,
+                                  ARRAY_SIZE(init_complete),
+                                  iwl_wait_phy_db_entry,
+                                  mvm->phy_db);
+
+       /* Will also start the device */
+       ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_INIT);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to start INIT ucode: %d\n", ret);
+               goto error;
+       }
+
+       if (read_nvm) {
+               /* Read nvm */
+               ret = iwl_nvm_init(mvm);
+               if (ret) {
+                       IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
+                       goto error;
+               }
+       }
+
+       ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
+       WARN_ON(ret);
+
+       /* Override the calibrations from TLV and the const of fw */
+       iwl_set_default_calib_trigger(mvm);
+
+       /* WkP doesn't have all calibrations, need to set default values */
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               ret = iwl_set_default_calibrations(mvm);
+               if (ret)
+                       goto error;
+       }
+
+       /*
+        * Send phy configurations command to init uCode
+        * to start the 16.0 uCode init image internal calibrations.
+        */
+       ret = iwl_send_phy_cfg_cmd(mvm);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to run INIT calibrations: %d\n",
+                       ret);
+               goto error;
+       }
+
+       /*
+        * Some things may run in the background now, but we
+        * just wait for the calibration complete notification.
+        */
+       ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait,
+                       MVM_UCODE_CALIB_TIMEOUT);
+       if (!ret)
+               mvm->init_ucode_run = true;
+       goto out;
+
+error:
+       iwl_remove_notification(&mvm->notif_wait, &calib_wait);
+out:
+       if (!iwlmvm_mod_params.init_dbg) {
+               iwl_trans_stop_device(mvm->trans);
+       } else if (!mvm->nvm_data) {
+               /* we want to debug INIT and we have no NVM - fake */
+               mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
+                                       sizeof(struct ieee80211_channel) +
+                                       sizeof(struct ieee80211_rate),
+                                       GFP_KERNEL);
+               if (!mvm->nvm_data)
+                       return -ENOMEM;
+               mvm->nvm_data->valid_rx_ant = 1;
+               mvm->nvm_data->valid_tx_ant = 1;
+               mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels;
+               mvm->nvm_data->bands[0].n_channels = 1;
+               mvm->nvm_data->bands[0].n_bitrates = 1;
+               mvm->nvm_data->bands[0].bitrates =
+                       (void *)mvm->nvm_data->channels + 1;
+               mvm->nvm_data->bands[0].bitrates->hw_value = 10;
+       }
+
+       return ret;
+}
+
+#define UCODE_CALIB_TIMEOUT    (2*HZ)
+
+int iwl_mvm_up(struct iwl_mvm *mvm)
+{
+       int ret, i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_trans_start_hw(mvm->trans);
+       if (ret)
+               return ret;
+
+       /* If we were in RFKILL during module loading, load init ucode now */
+       if (!mvm->init_ucode_run) {
+               ret = iwl_run_init_mvm_ucode(mvm, false);
+               if (ret && !iwlmvm_mod_params.init_dbg) {
+                       IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret);
+                       goto error;
+               }
+       }
+
+       if (iwlmvm_mod_params.init_dbg)
+               return 0;
+
+       ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret);
+               goto error;
+       }
+
+       ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+       if (ret)
+               goto error;
+
+       /* Send phy db control command and then phy db calibration*/
+       ret = iwl_send_phy_db_data(mvm->phy_db);
+       if (ret)
+               goto error;
+
+       ret = iwl_send_phy_cfg_cmd(mvm);
+       if (ret)
+               goto error;
+
+       /* init the fw <-> mac80211 STA mapping */
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
+               RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
+
+       /* Add auxiliary station for scanning */
+       ret = iwl_mvm_add_aux_sta(mvm);
+       if (ret)
+               goto error;
+
+       IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
+
+       return 0;
+ error:
+       iwl_trans_stop_device(mvm->trans);
+       return ret;
+}
+
+int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm)
+{
+       int ret, i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_trans_start_hw(mvm->trans);
+       if (ret)
+               return ret;
+
+       ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_WOWLAN);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to start WoWLAN firmware: %d\n", ret);
+               goto error;
+       }
+
+       ret = iwl_send_tx_ant_cfg(mvm, mvm->nvm_data->valid_tx_ant);
+       if (ret)
+               goto error;
+
+       /* Send phy db control command and then phy db calibration*/
+       ret = iwl_send_phy_db_data(mvm->phy_db);
+       if (ret)
+               goto error;
+
+       ret = iwl_send_phy_cfg_cmd(mvm);
+       if (ret)
+               goto error;
+
+       /* init the fw <-> mac80211 STA mapping */
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
+               RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
+
+       /* Add auxiliary station for scanning */
+       ret = iwl_mvm_add_aux_sta(mvm);
+       if (ret)
+               goto error;
+
+       return 0;
+ error:
+       iwl_trans_stop_device(mvm->trans);
+       return ret;
+}
+
+int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb,
+                                   struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_card_state_notif *card_state_notif = (void *)pkt->data;
+       u32 flags = le32_to_cpu(card_state_notif->flags);
+
+       IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n",
+                         (flags & HW_CARD_DISABLED) ? "Kill" : "On",
+                         (flags & SW_CARD_DISABLED) ? "Kill" : "On",
+                         (flags & CT_KILL_CARD_DISABLED) ?
+                         "Reached" : "Not reached");
+
+       if (flags & CARD_DISABLED_MSK)
+               iwl_write32(mvm->trans, CSR_UCODE_DRV_GP1_SET,
+                           CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
+
+       return 0;
+}
+
+int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                        struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_radio_version_notif *radio_version = (void *)pkt->data;
+
+       /* TODO: what to do with that? */
+       IWL_DEBUG_INFO(mvm,
+                      "Radio version: flavor: 0x%08x, step 0x%08x, dash 0x%08x\n",
+                      le32_to_cpu(radio_version->radio_flavor),
+                      le32_to_cpu(radio_version->radio_step),
+                      le32_to_cpu(radio_version->radio_dash));
+       return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c
new file mode 100644 (file)
index 0000000..011906e
--- /dev/null
@@ -0,0 +1,134 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/leds.h>
+#include "iwl-io.h"
+#include "iwl-csr.h"
+#include "mvm.h"
+
+/* Set led register on */
+static void iwl_mvm_led_enable(struct iwl_mvm *mvm)
+{
+       iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_ON);
+}
+
+/* Set led register off */
+static void iwl_mvm_led_disable(struct iwl_mvm *mvm)
+{
+       iwl_write32(mvm->trans, CSR_LED_REG, CSR_LED_REG_TURN_OFF);
+}
+
+static void iwl_led_brightness_set(struct led_classdev *led_cdev,
+                                  enum led_brightness brightness)
+{
+       struct iwl_mvm *mvm = container_of(led_cdev, struct iwl_mvm, led);
+       if (brightness > 0)
+               iwl_mvm_led_enable(mvm);
+       else
+               iwl_mvm_led_disable(mvm);
+}
+
+int iwl_mvm_leds_init(struct iwl_mvm *mvm)
+{
+       int mode = iwlwifi_mod_params.led_mode;
+       int ret;
+
+       switch (mode) {
+       case IWL_LED_DEFAULT:
+       case IWL_LED_RF_STATE:
+               mode = IWL_LED_RF_STATE;
+               break;
+       case IWL_LED_DISABLE:
+               IWL_INFO(mvm, "Led disabled\n");
+               return 0;
+       default:
+               return -EINVAL;
+       };
+
+       mvm->led.name = kasprintf(GFP_KERNEL, "%s-led",
+                                  wiphy_name(mvm->hw->wiphy));
+       mvm->led.brightness_set = iwl_led_brightness_set;
+       mvm->led.max_brightness = 1;
+
+       if (mode == IWL_LED_RF_STATE)
+               mvm->led.default_trigger =
+                       ieee80211_get_radio_led_name(mvm->hw);
+
+       ret = led_classdev_register(mvm->trans->dev, &mvm->led);
+       if (ret) {
+               kfree(mvm->led.name);
+               IWL_INFO(mvm, "Failed to enable led\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
+{
+       if (iwlwifi_mod_params.led_mode == IWL_LED_DISABLE)
+               return;
+
+       led_classdev_unregister(&mvm->led);
+       kfree(mvm->led.name);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
new file mode 100644 (file)
index 0000000..c08a17a
--- /dev/null
@@ -0,0 +1,951 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "fw-api.h"
+#include "mvm.h"
+
+const u8 iwl_mvm_ac_to_tx_fifo[] = {
+       IWL_MVM_TX_FIFO_BK,
+       IWL_MVM_TX_FIFO_BE,
+       IWL_MVM_TX_FIFO_VI,
+       IWL_MVM_TX_FIFO_VO,
+};
+
+struct iwl_mvm_mac_iface_iterator_data {
+       struct iwl_mvm *mvm;
+       struct ieee80211_vif *vif;
+       unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)];
+       unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)];
+       unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_FIRST_AGG_QUEUE)];
+       enum iwl_tsf_id preferred_tsf;
+       bool found_vif;
+};
+
+static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_mac_iface_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 ac;
+
+       /* Iterator may already find the interface being added -- skip it */
+       if (vif == data->vif) {
+               data->found_vif = true;
+               return;
+       }
+
+       /* Mark the queues used by the vif */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+                       __set_bit(vif->hw_queue[ac], data->used_hw_queues);
+
+       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
+               __set_bit(vif->cab_queue, data->used_hw_queues);
+
+       /*
+        * Mark MAC IDs as used by clearing the available bit, and
+        * (below) mark TSFs as used if their existing use is not
+        * compatible with the new interface type.
+        * No locking or atomic bit operations are needed since the
+        * data is on the stack of the caller function.
+        */
+       __clear_bit(mvmvif->id, data->available_mac_ids);
+
+       /*
+        * The TSF is a hardware/firmware resource, there are 4 and
+        * the driver should assign and free them as needed. However,
+        * there are cases where 2 MACs should share the same TSF ID
+        * for the purpose of clock sync, an optimization to avoid
+        * clock drift causing overlapping TBTTs/DTIMs for a GO and
+        * client in the system.
+        *
+        * The firmware will decide according to the MAC type which
+        * will be the master and slave. Clients that need to sync
+        * with a remote station will be the master, and an AP or GO
+        * will be the slave.
+        *
+        * Depending on the new interface type it can be slaved to
+        * or become the master of an existing interface.
+        */
+       switch (data->vif->type) {
+       case NL80211_IFTYPE_STATION:
+               /*
+                * The new interface is client, so if the existing one
+                * we're iterating is an AP, the TSF should be used to
+                * avoid drift between the new client and existing AP,
+                * the existing AP will get drift updates from the new
+                * client context in this case
+                */
+               if (vif->type == NL80211_IFTYPE_AP) {
+                       if (data->preferred_tsf == NUM_TSF_IDS &&
+                           test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+                               data->preferred_tsf = mvmvif->tsf_id;
+                       return;
+               }
+               break;
+       case NL80211_IFTYPE_AP:
+               /*
+                * The new interface is AP/GO, so should get drift
+                * updates from an existing client or use the same
+                * TSF as an existing GO. There's no drift between
+                * TSFs internally but if they used different TSFs
+                * then a new client MAC could update one of them
+                * and cause drift that way.
+                */
+               if (vif->type == NL80211_IFTYPE_STATION ||
+                   vif->type == NL80211_IFTYPE_AP) {
+                       if (data->preferred_tsf == NUM_TSF_IDS &&
+                           test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+                               data->preferred_tsf = mvmvif->tsf_id;
+                       return;
+               }
+               break;
+       default:
+               /*
+                * For all other interface types there's no need to
+                * take drift into account. Either they're exclusive
+                * like IBSS and monitor, or we don't care much about
+                * their TSF (like P2P Device), but we won't be able
+                * to share the TSF resource.
+                */
+               break;
+       }
+
+       /*
+        * Unless we exited above, we can't share the TSF resource
+        * that the virtual interface we're iterating over is using
+        * with the new one, so clear the available bit and if this
+        * was the preferred one, reset that as well.
+        */
+       __clear_bit(mvmvif->tsf_id, data->available_tsf_ids);
+
+       if (data->preferred_tsf == mvmvif->tsf_id)
+               data->preferred_tsf = NUM_TSF_IDS;
+}
+
+/*
+ * Get the mask of the queus used by the vif
+ */
+u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
+                               struct ieee80211_vif *vif)
+{
+       u32 qmask, ac;
+
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
+               return BIT(IWL_OFFCHANNEL_QUEUE);
+
+       qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ?
+               BIT(vif->cab_queue) : 0;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+                       qmask |= BIT(vif->hw_queue[ac]);
+
+       return qmask;
+}
+
+static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
+                                              struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_mac_iface_iterator_data data = {
+               .mvm = mvm,
+               .vif = vif,
+               .available_mac_ids = { (1 << NUM_MAC_INDEX_DRIVER) - 1 },
+               .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
+               /* no preference yet */
+               .preferred_tsf = NUM_TSF_IDS,
+               .used_hw_queues = {
+                       BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
+                       BIT(IWL_MVM_AUX_QUEUE) |
+                       BIT(IWL_MVM_CMD_QUEUE)
+               },
+               .found_vif = false,
+       };
+       u32 ac;
+       int ret;
+
+       /*
+        * Allocate a MAC ID and a TSF for this MAC, along with the queues
+        * and other resources.
+        */
+
+       /*
+        * Before the iterator, we start with all MAC IDs and TSFs available.
+        *
+        * During iteration, all MAC IDs are cleared that are in use by other
+        * virtual interfaces, and all TSF IDs are cleared that can't be used
+        * by this new virtual interface because they're used by an interface
+        * that can't share it with the new one.
+        * At the same time, we check if there's a preferred TSF in the case
+        * that we should share it with another interface.
+        */
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+               iwl_mvm_mac_iface_iterator, &data);
+
+       /*
+        * In the case we're getting here during resume, it's similar to
+        * firmware restart, and with RESUME_ALL the iterator will find
+        * the vif being added already.
+        * We don't want to reassign any IDs in either case since doing
+        * so would probably assign different IDs (as interfaces aren't
+        * necessarily added in the same order), but the old IDs were
+        * preserved anyway, so skip ID assignment for both resume and
+        * recovery.
+        */
+       if (data.found_vif)
+               return 0;
+
+       /* Therefore, in recovery, we can't get here */
+       WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
+
+       mvmvif->id = find_first_bit(data.available_mac_ids,
+                                   NUM_MAC_INDEX_DRIVER);
+       if (mvmvif->id == NUM_MAC_INDEX_DRIVER) {
+               IWL_ERR(mvm, "Failed to init MAC context - no free ID!\n");
+               ret = -EIO;
+               goto exit_fail;
+       }
+
+       if (data.preferred_tsf != NUM_TSF_IDS)
+               mvmvif->tsf_id = data.preferred_tsf;
+       else
+               mvmvif->tsf_id = find_first_bit(data.available_tsf_ids,
+                                               NUM_TSF_IDS);
+       if (mvmvif->tsf_id == NUM_TSF_IDS) {
+               IWL_ERR(mvm, "Failed to init MAC context - no free TSF!\n");
+               ret = -EIO;
+               goto exit_fail;
+       }
+
+       mvmvif->color = 0;
+
+       /* No need to allocate data queues to P2P Device MAC.*/
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
+
+               return 0;
+       }
+
+       /* Find available queues, and allocate them to the ACs */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               u8 queue = find_first_zero_bit(data.used_hw_queues,
+                                              IWL_MVM_FIRST_AGG_QUEUE);
+
+               if (queue >= IWL_MVM_FIRST_AGG_QUEUE) {
+                       IWL_ERR(mvm, "Failed to allocate queue\n");
+                       ret = -EIO;
+                       goto exit_fail;
+               }
+
+               __set_bit(queue, data.used_hw_queues);
+               vif->hw_queue[ac] = queue;
+       }
+
+       /* Allocate the CAB queue for softAP and GO interfaces */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               u8 queue = find_first_zero_bit(data.used_hw_queues,
+                                              IWL_MVM_FIRST_AGG_QUEUE);
+
+               if (queue >= IWL_MVM_FIRST_AGG_QUEUE) {
+                       IWL_ERR(mvm, "Failed to allocate cab queue\n");
+                       ret = -EIO;
+                       goto exit_fail;
+               }
+
+               vif->cab_queue = queue;
+       } else {
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+       }
+
+       mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT;
+       mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+       INIT_LIST_HEAD(&mvmvif->time_event_data.list);
+       mvmvif->time_event_data.id = TE_MAX;
+
+       return 0;
+
+exit_fail:
+       memset(mvmvif, 0, sizeof(struct iwl_mvm_vif));
+       memset(vif->hw_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(vif->hw_queue));
+       vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+       return ret;
+}
+
+int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       u32 ac;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_mvm_mac_ctxt_allocate_resources(mvm, vif);
+       if (ret)
+               return ret;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               iwl_trans_ac_txq_enable(mvm->trans, IWL_MVM_OFFCHANNEL_QUEUE,
+                                       IWL_MVM_TX_FIFO_VO);
+               break;
+       case NL80211_IFTYPE_AP:
+               iwl_trans_ac_txq_enable(mvm->trans, vif->cab_queue,
+                                       IWL_MVM_TX_FIFO_VO);
+               /* fall through */
+       default:
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       iwl_trans_ac_txq_enable(mvm->trans, vif->hw_queue[ac],
+                                               iwl_mvm_ac_to_tx_fifo[ac]);
+               break;
+       }
+
+       return 0;
+}
+
+void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       int ac;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               iwl_trans_txq_disable(mvm->trans, IWL_MVM_OFFCHANNEL_QUEUE);
+               break;
+       case NL80211_IFTYPE_AP:
+               iwl_trans_txq_disable(mvm->trans, vif->cab_queue);
+               /* fall through */
+       default:
+               for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+                       iwl_trans_txq_disable(mvm->trans, vif->hw_queue[ac]);
+       }
+}
+
+static void iwl_mvm_ack_rates(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             enum ieee80211_band band,
+                             u8 *cck_rates, u8 *ofdm_rates)
+{
+       struct ieee80211_supported_band *sband;
+       unsigned long basic = vif->bss_conf.basic_rates;
+       int lowest_present_ofdm = 100;
+       int lowest_present_cck = 100;
+       u8 cck = 0;
+       u8 ofdm = 0;
+       int i;
+
+       sband = mvm->hw->wiphy->bands[band];
+
+       for_each_set_bit(i, &basic, BITS_PER_LONG) {
+               int hw = sband->bitrates[i].hw_value;
+               if (hw >= IWL_FIRST_OFDM_RATE) {
+                       ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE);
+                       if (lowest_present_ofdm > hw)
+                               lowest_present_ofdm = hw;
+               } else {
+                       BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
+
+                       cck |= BIT(hw);
+                       if (lowest_present_cck > hw)
+                               lowest_present_cck = hw;
+               }
+       }
+
+       /*
+        * Now we've got the basic rates as bitmaps in the ofdm and cck
+        * variables. This isn't sufficient though, as there might not
+        * be all the right rates in the bitmap. E.g. if the only basic
+        * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
+        * and 6 Mbps because the 802.11-2007 standard says in 9.6:
+        *
+        *    [...] a STA responding to a received frame shall transmit
+        *    its Control Response frame [...] at the highest rate in the
+        *    BSSBasicRateSet parameter that is less than or equal to the
+        *    rate of the immediately previous frame in the frame exchange
+        *    sequence ([...]) and that is of the same modulation class
+        *    ([...]) as the received frame. If no rate contained in the
+        *    BSSBasicRateSet parameter meets these conditions, then the
+        *    control frame sent in response to a received frame shall be
+        *    transmitted at the highest mandatory rate of the PHY that is
+        *    less than or equal to the rate of the received frame, and
+        *    that is of the same modulation class as the received frame.
+        *
+        * As a consequence, we need to add all mandatory rates that are
+        * lower than all of the basic rates to these bitmaps.
+        */
+
+       if (IWL_RATE_24M_INDEX < lowest_present_ofdm)
+               ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
+       if (IWL_RATE_12M_INDEX < lowest_present_ofdm)
+               ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
+       /* 6M already there or needed so always add */
+       ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
+
+       /*
+        * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
+        * Note, however:
+        *  - if no CCK rates are basic, it must be ERP since there must
+        *    be some basic rates at all, so they're OFDM => ERP PHY
+        *    (or we're in 5 GHz, and the cck bitmap will never be used)
+        *  - if 11M is a basic rate, it must be ERP as well, so add 5.5M
+        *  - if 5.5M is basic, 1M and 2M are mandatory
+        *  - if 2M is basic, 1M is mandatory
+        *  - if 1M is basic, that's the only valid ACK rate.
+        * As a consequence, it's not as complicated as it sounds, just add
+        * any lower rates to the ACK rate bitmap.
+        */
+       if (IWL_RATE_11M_INDEX < lowest_present_cck)
+               cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE;
+       if (IWL_RATE_5M_INDEX < lowest_present_cck)
+               cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE;
+       if (IWL_RATE_2M_INDEX < lowest_present_cck)
+               cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE;
+       /* 1M already there or needed so always add */
+       cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE;
+
+       *cck_rates = cck;
+       *ofdm_rates = ofdm;
+}
+
+static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       struct iwl_mac_ctx_cmd *cmd,
+                                       u32 action)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_chanctx_conf *chanctx;
+       u8 cck_ack_rates, ofdm_ack_rates;
+       int i;
+
+       cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                           mvmvif->color));
+       cmd->action = cpu_to_le32(action);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               if (vif->p2p)
+                       cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_STA);
+               else
+                       cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_BSS_STA);
+               break;
+       case NL80211_IFTYPE_AP:
+               cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_GO);
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_LISTENER);
+               break;
+       case NL80211_IFTYPE_P2P_DEVICE:
+               cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_P2P_DEVICE);
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               cmd->mac_type = cpu_to_le32(FW_MAC_TYPE_IBSS);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
+
+       cmd->tsf_id = cpu_to_le32(mvmvif->tsf_id);
+
+       memcpy(cmd->node_addr, vif->addr, ETH_ALEN);
+       if (vif->bss_conf.bssid)
+               memcpy(cmd->bssid_addr, vif->bss_conf.bssid, ETH_ALEN);
+       else
+               eth_broadcast_addr(cmd->bssid_addr);
+
+       rcu_read_lock();
+       chanctx = rcu_dereference(vif->chanctx_conf);
+       iwl_mvm_ack_rates(mvm, vif, chanctx ? chanctx->def.chan->band
+                                           : IEEE80211_BAND_2GHZ,
+                         &cck_ack_rates, &ofdm_ack_rates);
+       rcu_read_unlock();
+
+       cmd->cck_rates = cpu_to_le32((u32)cck_ack_rates);
+       cmd->ofdm_rates = cpu_to_le32((u32)ofdm_ack_rates);
+
+       cmd->cck_short_preamble =
+               cpu_to_le32(vif->bss_conf.use_short_preamble ?
+                           MAC_FLG_SHORT_PREAMBLE : 0);
+       cmd->short_slot =
+               cpu_to_le32(vif->bss_conf.use_short_slot ?
+                           MAC_FLG_SHORT_SLOT : 0);
+
+       for (i = 0; i < AC_NUM; i++) {
+               cmd->ac[i].cw_min = cpu_to_le16(mvmvif->queue_params[i].cw_min);
+               cmd->ac[i].cw_max = cpu_to_le16(mvmvif->queue_params[i].cw_max);
+               cmd->ac[i].aifsn = mvmvif->queue_params[i].aifs;
+               cmd->ac[i].edca_txop =
+                       cpu_to_le16(mvmvif->queue_params[i].txop * 32);
+               cmd->ac[i].fifos_mask = BIT(iwl_mvm_ac_to_tx_fifo[i]);
+       }
+
+       if (vif->bss_conf.qos)
+               cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
+
+       if (vif->bss_conf.use_cts_prot)
+               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT |
+                                                    MAC_PROT_FLG_SELF_CTS_EN);
+
+       /*
+        * I think that we should enable these 2 flags regardless the HT PROT
+        * fields in the HT IE, but I am not sure. Someone knows whom to ask?...
+        */
+       if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
+               cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN);
+               cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_HT_PROT |
+                                                    MAC_PROT_FLG_FAT_PROT);
+       }
+
+       cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP);
+}
+
+static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
+                                    struct iwl_mac_ctx_cmd *cmd)
+{
+       int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC,
+                                      sizeof(*cmd), cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n",
+                       le32_to_cpu(cmd->action), ret);
+       return ret;
+}
+
+/*
+ * Fill the specific data for mac context of type station or p2p client
+ */
+static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct iwl_mac_data_sta *ctxt_sta)
+{
+       ctxt_sta->is_assoc = cpu_to_le32(vif->bss_conf.assoc ? 1 : 0);
+
+       ctxt_sta->bi = cpu_to_le32(vif->bss_conf.beacon_int);
+       ctxt_sta->bi_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+       ctxt_sta->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
+                                             vif->bss_conf.dtim_period);
+       ctxt_sta->dtim_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
+                                              vif->bss_conf.dtim_period));
+
+       ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval);
+       ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_STATION || vif->p2p);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /* Fill the data specific for station mode */
+       iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif,
+                                          u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_STATION || !vif->p2p);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /* Fill the data specific for station mode */
+       iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta);
+
+       cmd.p2p_sta.ctwin = cpu_to_le32(vif->bss_conf.p2p_ctwindow);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif,
+                                        u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
+
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+       /* No other data to be filled */
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+struct iwl_mvm_go_iterator_data {
+       bool go_active;
+};
+
+static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_go_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif->type == NL80211_IFTYPE_AP && vif->p2p && mvmvif->ap_active)
+               data->go_active = true;
+}
+
+static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif,
+                                          u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+       struct iwl_mvm_go_iterator_data data = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
+
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       cmd.protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
+       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROMISC);
+
+       /*
+        * This flag should be set to true when the P2P Device is
+        * discoverable and there is at least another active P2P GO. Settings
+        * this flag will allow the P2P Device to be discoverable on other
+        * channels in addition to its listen channel.
+        * Note that this flag should not be set in other cases as it opens the
+        * Rx filters on all MAC and increases the number of interrupts.
+        */
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+               iwl_mvm_go_iterator, &data);
+
+       cmd.p2p_dev.is_disc_extended = cpu_to_le32(data.go_active ? 1 : 0);
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm,
+                                    struct iwl_mac_beacon_cmd *beacon_cmd,
+                                    u8 *beacon, u32 frame_size)
+{
+       u32 tim_idx;
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon;
+
+       /* The index is relative to frame start but we start looking at the
+        * variable-length part of the beacon. */
+       tim_idx = mgmt->u.beacon.variable - beacon;
+
+       /* Parse variable-length elements of beacon to find WLAN_EID_TIM */
+       while ((tim_idx < (frame_size - 2)) &&
+                       (beacon[tim_idx] != WLAN_EID_TIM))
+               tim_idx += beacon[tim_idx+1] + 2;
+
+       /* If TIM field was found, set variables */
+       if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) {
+               beacon_cmd->tim_idx = cpu_to_le32(tim_idx);
+               beacon_cmd->tim_size = cpu_to_le32((u32)beacon[tim_idx+1]);
+       } else {
+               IWL_WARN(mvm, "Unable to find TIM Element in beacon\n");
+       }
+}
+
+static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       struct sk_buff *beacon)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_host_cmd cmd = {
+               .id = BEACON_TEMPLATE_CMD,
+               .flags = CMD_ASYNC,
+       };
+       struct iwl_mac_beacon_cmd beacon_cmd = {};
+       struct ieee80211_tx_info *info;
+       u32 beacon_skb_len;
+       u32 rate;
+
+       if (WARN_ON(!beacon))
+               return -EINVAL;
+
+       beacon_skb_len = beacon->len;
+
+       /* TODO: for now the beacon template id is set to be the mac context id.
+        * Might be better to handle it as another resource ... */
+       beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id);
+
+       /* Set up TX command fields */
+       beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len);
+       beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id;
+       beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+       beacon_cmd.tx.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+                                            TX_CMD_FLG_BT_DIS  |
+                                            TX_CMD_FLG_TSF);
+
+       mvm->mgmt_last_antenna_idx =
+               iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+                                    mvm->mgmt_last_antenna_idx);
+
+       beacon_cmd.tx.rate_n_flags =
+               cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
+                           RATE_MCS_ANT_POS);
+
+       info = IEEE80211_SKB_CB(beacon);
+
+       if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) {
+               rate = IWL_FIRST_OFDM_RATE;
+       } else {
+               rate = IWL_FIRST_CCK_RATE;
+               beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK);
+       }
+       beacon_cmd.tx.rate_n_flags |=
+               cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate));
+
+       /* Set up TX beacon command fields */
+       iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd,
+                                beacon->data,
+                                beacon_skb_len);
+
+       /* Submit command */
+       cmd.len[0] = sizeof(beacon_cmd);
+       cmd.data[0] = &beacon_cmd;
+       cmd.dataflags[0] = 0;
+       cmd.len[1] = beacon_skb_len;
+       cmd.data[1] = beacon->data;
+       cmd.dataflags[1] = IWL_HCMD_DFL_DUP;
+
+       return iwl_mvm_send_cmd(mvm, &cmd);
+}
+
+/* The beacon template for the AP/GO context has changed and needs update */
+int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif)
+{
+       struct sk_buff *beacon;
+       int ret;
+
+       WARN_ON(vif->type != NL80211_IFTYPE_AP);
+
+       beacon = ieee80211_beacon_get(mvm->hw, vif);
+       if (!beacon)
+               return -ENOMEM;
+
+       ret = iwl_mvm_mac_ctxt_send_beacon(mvm, vif, beacon);
+       dev_kfree_skb(beacon);
+       return ret;
+}
+
+/*
+ * Fill the specific data for mac context of type AP of P2P GO
+ */
+static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif,
+                                        struct iwl_mac_data_ap *ctxt_ap)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 curr_dev_time;
+
+       ctxt_ap->bi = cpu_to_le32(vif->bss_conf.beacon_int);
+       ctxt_ap->bi_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int));
+       ctxt_ap->dtim_interval = cpu_to_le32(vif->bss_conf.beacon_int *
+                                            vif->bss_conf.dtim_period);
+       ctxt_ap->dtim_reciprocal =
+               cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int *
+                                              vif->bss_conf.dtim_period));
+
+       ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue);
+       curr_dev_time = iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG);
+       ctxt_ap->beacon_time = cpu_to_le32(curr_dev_time);
+
+       ctxt_ap->beacon_tsf = cpu_to_le64(curr_dev_time);
+
+       /* TODO: Assume that the beacon id == mac context id */
+       ctxt_ap->beacon_template = cpu_to_le32(mvmvif->id);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /* Fill the data specific for ap mode */
+       iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  u32 action)
+{
+       struct iwl_mac_ctx_cmd cmd = {};
+
+       WARN_ON(vif->type != NL80211_IFTYPE_AP || !vif->p2p);
+
+       /* Fill the common data for all mac context types */
+       iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
+
+       /* Fill the data specific for GO mode */
+       iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap);
+
+       cmd.go.ctwin = cpu_to_le32(vif->bss_conf.p2p_ctwindow);
+       cmd.go.opp_ps_enabled = cpu_to_le32(!!vif->bss_conf.p2p_oppps);
+
+       return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd);
+}
+
+static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               u32 action)
+{
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               if (!vif->p2p)
+                       return iwl_mvm_mac_ctxt_cmd_station(mvm, vif,
+                                                           action);
+               else
+                       return iwl_mvm_mac_ctxt_cmd_p2p_client(mvm, vif,
+                                                              action);
+               break;
+       case NL80211_IFTYPE_AP:
+               if (!vif->p2p)
+                       return iwl_mvm_mac_ctxt_cmd_ap(mvm, vif, action);
+               else
+                       return iwl_mvm_mac_ctxt_cmd_go(mvm, vif, action);
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action);
+       case NL80211_IFTYPE_P2P_DEVICE:
+               return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action);
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       if (WARN_ONCE(mvmvif->uploaded, "Adding active MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD);
+       if (ret)
+               return ret;
+
+       mvmvif->uploaded = true;
+       return 0;
+}
+
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (WARN_ONCE(!mvmvif->uploaded, "Changing inactive MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY);
+}
+
+int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_ctx_cmd cmd;
+       int ret;
+
+       if (WARN_ONCE(!mvmvif->uploaded, "Removing inactive MAC %pM/%d\n",
+                     vif->addr, ieee80211_vif_type_p2p(vif)))
+               return -EIO;
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                          mvmvif->color));
+       cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC,
+                                  sizeof(cmd), &cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret);
+               return ret;
+       }
+
+       mvmvif->uploaded = false;
+       return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
new file mode 100644 (file)
index 0000000..a6b05a0
--- /dev/null
@@ -0,0 +1,1310 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-io.h"
+#include "mvm.h"
+#include "sta.h"
+#include "time-event.h"
+#include "iwl-eeprom-parse.h"
+#include "fw-api-scan.h"
+#include "iwl-phy-db.h"
+
+static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_STATION) |
+                       BIT(NL80211_IFTYPE_AP),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                       BIT(NL80211_IFTYPE_P2P_GO),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
+       },
+};
+
+static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
+       {
+               .num_different_channels = 1,
+               .max_interfaces = 3,
+               .limits = iwl_mvm_limits,
+               .n_limits = ARRAY_SIZE(iwl_mvm_limits),
+       },
+};
+
+int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
+{
+       struct ieee80211_hw *hw = mvm->hw;
+       int num_mac, ret;
+
+       /* Tell mac80211 our characteristics */
+       hw->flags = IEEE80211_HW_SIGNAL_DBM |
+                   IEEE80211_HW_SPECTRUM_MGMT |
+                   IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+                   IEEE80211_HW_QUEUE_CONTROL |
+                   IEEE80211_HW_WANT_MONITOR_VIF |
+                   IEEE80211_HW_SCAN_WHILE_IDLE |
+                   IEEE80211_HW_NEED_DTIM_PERIOD |
+                   IEEE80211_HW_SUPPORTS_PS |
+                   IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+                   IEEE80211_HW_AMPDU_AGGREGATION;
+
+       hw->queues = IWL_FIRST_AMPDU_QUEUE;
+       hw->offchannel_tx_hw_queue = IWL_OFFCHANNEL_QUEUE;
+       hw->rate_control_algorithm = "iwl-mvm-rs";
+
+       /*
+        * Enable 11w if advertised by firmware and software crypto
+        * is not enabled (as the firmware will interpret some mgmt
+        * packets, so enabling it with software crypto isn't safe)
+        */
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP &&
+           !iwlwifi_mod_params.sw_crypto)
+               hw->flags |= IEEE80211_HW_MFP_CAPABLE;
+
+       hw->sta_data_size = sizeof(struct iwl_mvm_sta);
+       hw->vif_data_size = sizeof(struct iwl_mvm_vif);
+       hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt);
+
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+               BIT(NL80211_IFTYPE_P2P_CLIENT) |
+               BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_P2P_GO) |
+               BIT(NL80211_IFTYPE_P2P_DEVICE);
+
+       hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
+                           WIPHY_FLAG_DISABLE_BEACON_HINTS |
+                           WIPHY_FLAG_IBSS_RSN;
+
+       hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
+       hw->wiphy->n_iface_combinations =
+               ARRAY_SIZE(iwl_mvm_iface_combinations);
+
+       hw->wiphy->max_remain_on_channel_duration = 500;
+       hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+
+       /* Extract MAC address */
+       memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
+       hw->wiphy->addresses = mvm->addresses;
+       hw->wiphy->n_addresses = 1;
+       num_mac = mvm->nvm_data->n_hw_addrs;
+       if (num_mac > 1) {
+               memcpy(mvm->addresses[1].addr, mvm->addresses[0].addr,
+                      ETH_ALEN);
+               mvm->addresses[1].addr[5]++;
+               hw->wiphy->n_addresses++;
+       }
+
+       /* we create the 802.11 header and a max-length SSID element */
+       hw->wiphy->max_scan_ie_len =
+               mvm->fw->ucode_capa.max_probe_length - 24 - 34;
+       hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
+
+       if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+                       &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+       if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels)
+               hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+                       &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+
+       hw->wiphy->hw_version = mvm->trans->hw_id;
+
+       if (iwlwifi_mod_params.power_save)
+               hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+       else
+               hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+       hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
+                              NL80211_FEATURE_P2P_GO_OPPPS;
+
+       mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
+
+#ifdef CONFIG_PM_SLEEP
+       if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
+           mvm->trans->ops->d3_suspend &&
+           mvm->trans->ops->d3_resume &&
+           device_can_wakeup(mvm->trans->dev)) {
+               hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
+                                         WIPHY_WOWLAN_DISCONNECT |
+                                         WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                         WIPHY_WOWLAN_RFKILL_RELEASE;
+               if (!iwlwifi_mod_params.sw_crypto)
+                       hw->wiphy->wowlan.flags |=
+                               WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+                               WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+                               WIPHY_WOWLAN_4WAY_HANDSHAKE;
+
+               hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
+               hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
+               hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+       }
+#endif
+
+       ret = iwl_mvm_leds_init(mvm);
+       if (ret)
+               return ret;
+
+       return ieee80211_register_hw(mvm->hw);
+}
+
+static void iwl_mvm_mac_tx(struct ieee80211_hw *hw,
+                          struct ieee80211_tx_control *control,
+                          struct sk_buff *skb)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) {
+               IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n");
+               goto drop;
+       }
+
+       if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_OFFCHANNEL_QUEUE &&
+           !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
+               goto drop;
+
+       if (control->sta) {
+               if (iwl_mvm_tx_skb(mvm, skb, control->sta))
+                       goto drop;
+               return;
+       }
+
+       if (iwl_mvm_tx_skb_non_sta(mvm, skb))
+               goto drop;
+       return;
+ drop:
+       ieee80211_free_txskb(hw, skb);
+}
+
+static int iwl_mvm_mac_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 iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n",
+                    sta->addr, tid, action);
+
+       if (!(mvm->nvm_data->sku_cap_11n_enable))
+               return -EACCES;
+
+       mutex_lock(&mvm->mutex);
+
+       switch (action) {
+       case IEEE80211_AMPDU_RX_START:
+               if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, *ssn, true);
+               break;
+       case IEEE80211_AMPDU_RX_STOP:
+               ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false);
+               break;
+       case IEEE80211_AMPDU_TX_START:
+               ret = iwl_mvm_sta_tx_agg_start(mvm, vif, sta, tid, ssn);
+               break;
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH:
+       case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+               ret = iwl_mvm_sta_tx_agg_stop(mvm, vif, sta, tid);
+               break;
+       case IEEE80211_AMPDU_TX_OPERATIONAL:
+               ret = iwl_mvm_sta_tx_agg_oper(mvm, vif, sta, tid, buf_size);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               ret = -EINVAL;
+               break;
+       }
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->uploaded = false;
+       mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+
+       /* does this make sense at all? */
+       mvmvif->color++;
+
+       spin_lock_bh(&mvm->time_event_lock);
+       iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
+               mvmvif->phy_ctxt = NULL;
+}
+
+static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
+{
+       iwl_trans_stop_device(mvm->trans);
+       iwl_trans_stop_hw(mvm->trans, false);
+
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+
+       /* just in case one was running */
+       ieee80211_remain_on_channel_expired(mvm->hw);
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+               iwl_mvm_cleanup_iterator, mvm);
+
+       memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
+       memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
+
+       ieee80211_wake_queues(mvm->hw);
+
+       mvm->vif_count = 0;
+}
+
+static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       /* Clean up some internal and mac80211 state on restart */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               iwl_mvm_restart_cleanup(mvm);
+
+       ret = iwl_mvm_up(mvm);
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+       ret = iwl_mvm_update_quotas(mvm, NULL);
+       if (ret)
+               IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
+                       ret);
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       flush_work(&mvm->async_handlers_wk);
+
+       mutex_lock(&mvm->mutex);
+       /* async_handlers_wk is now blocked */
+
+       /*
+        * The work item could be running or queued if the
+        * ROC time event stops just as we get here.
+        */
+       cancel_work_sync(&mvm->roc_done_wk);
+
+       iwl_trans_stop_device(mvm->trans);
+       iwl_trans_stop_hw(mvm->trans, false);
+
+       iwl_mvm_async_handlers_purge(mvm);
+       /* async_handlers_list is empty and will stay empty: HW is stopped */
+
+       /* the fw is stopped, the aux sta is dead: clean up driver state */
+       iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
+
+       mutex_unlock(&mvm->mutex);
+
+       /*
+        * The worker might have been waiting for the mutex, let it run and
+        * discover that its list is now empty.
+        */
+       cancel_work_sync(&mvm->async_handlers_wk);
+}
+
+static void iwl_mvm_pm_disable_iterator(void *data, u8 *mac,
+                                       struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = data;
+       int ret;
+
+       ret = iwl_mvm_power_disable(mvm, vif);
+       if (ret)
+               IWL_ERR(mvm, "failed to disable power management\n");
+}
+
+static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
+                                         struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = data;
+
+       iwl_mvm_power_update_mode(mvm, vif);
+}
+
+static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       /*
+        * Not much to do here. The stack will not allow interface
+        * types or combinations that we didn't advertise, so we
+        * don't really have to check the types.
+        */
+
+       mutex_lock(&mvm->mutex);
+
+       /* Allocate resources for the MAC context, and add it the the fw  */
+       ret = iwl_mvm_mac_ctxt_init(mvm, vif);
+       if (ret)
+               goto out_unlock;
+
+       /*
+        * The AP binding flow can be done only after the beacon
+        * template is configured (which happens only in the mac80211
+        * start_ap() flow), and adding the broadcast station can happen
+        * only after the binding.
+        * In addition, since modifying the MAC before adding a bcast
+        * station is not allowed by the FW, delay the adding of MAC context to
+        * the point where we can also add the bcast station.
+        * In short: there's not much we can do at this point, other than
+        * allocating resources :)
+        */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
+               ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta,
+                                              qmask);
+               if (ret) {
+                       IWL_ERR(mvm, "Failed to allocate bcast sta\n");
+                       goto out_release;
+               }
+
+               goto out_unlock;
+       }
+
+       /*
+        * TODO: remove this temporary code.
+        * Currently MVM FW supports power management only on single MAC.
+        * Iterate and disable PM on all active interfaces.
+        * Note: the method below does not count the new interface being added
+        * at this moment.
+        */
+       mvm->vif_count++;
+       if (mvm->vif_count > 1) {
+               IWL_DEBUG_MAC80211(mvm,
+                                  "Disable power on existing interfaces\n");
+               ieee80211_iterate_active_interfaces(
+                                           mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_pm_disable_iterator, mvm);
+       }
+
+       ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+       if (ret)
+               goto out_release;
+
+       /*
+        * Update power state on the new interface. Admittedly, based on
+        * mac80211 logics this power update will disable power management
+        */
+       iwl_mvm_power_update_mode(mvm, vif);
+
+       /*
+        * P2P_DEVICE interface does not have a channel context assigned to it,
+        * so a dedicated PHY context is allocated to it and the corresponding
+        * MAC context is bound to it at this stage.
+        */
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               struct ieee80211_channel *chan;
+               struct cfg80211_chan_def chandef;
+
+               mvmvif->phy_ctxt = &mvm->phy_ctxt_roc;
+
+               /*
+                * The channel used here isn't relevant as it's
+                * going to be overwritten as part of the ROC flow.
+                * For now use the first channel we have.
+                */
+               chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
+               cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+               ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt,
+                                          &chandef, 1, 1);
+               if (ret)
+                       goto out_remove_mac;
+
+               ret = iwl_mvm_binding_add_vif(mvm, vif);
+               if (ret)
+                       goto out_remove_phy;
+
+               ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
+               if (ret)
+                       goto out_unbind;
+
+               /* Save a pointer to p2p device vif, so it can later be used to
+                * update the p2p device MAC when a GO is started/stopped */
+               mvm->p2p_device_vif = vif;
+       }
+
+       goto out_unlock;
+
+ out_unbind:
+       iwl_mvm_binding_remove_vif(mvm, vif);
+ out_remove_phy:
+       iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+ out_remove_mac:
+       mvmvif->phy_ctxt = NULL;
+       iwl_mvm_mac_ctxt_remove(mvm, vif);
+ out_release:
+       /*
+        * TODO: remove this temporary code.
+        * Currently MVM FW supports power management only on single MAC.
+        * Check if only one additional interface remains after rereasing
+        * current one. Update power mode on the remaining interface.
+        */
+       mvm->vif_count--;
+       IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
+                          mvm->vif_count);
+       if (mvm->vif_count == 1) {
+               ieee80211_iterate_active_interfaces(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_power_update_iterator, mvm);
+       }
+       iwl_mvm_mac_ctxt_release(mvm, vif);
+ out_unlock:
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 tfd_msk = 0, ac;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+                       tfd_msk |= BIT(vif->hw_queue[ac]);
+
+       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
+               tfd_msk |= BIT(vif->cab_queue);
+
+       if (tfd_msk) {
+               mutex_lock(&mvm->mutex);
+               iwl_mvm_flush_tx_path(mvm, tfd_msk, true);
+               mutex_unlock(&mvm->mutex);
+       }
+
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               /*
+                * Flush the ROC worker which will flush the OFFCHANNEL queue.
+                * We assume here that all the packets sent to the OFFCHANNEL
+                * queue are sent in ROC session.
+                */
+               flush_work(&mvm->roc_done_wk);
+       } else {
+               /*
+                * By now, all the AC queues are empty. The AGG queues are
+                * empty too. We already got all the Tx responses for all the
+                * packets in the queues. The drain work can have been
+                * triggered. Flush it. This work item takes the mutex, so kill
+                * it before we take it.
+                */
+               flush_work(&mvm->sta_drained_wk);
+       }
+
+       mutex_lock(&mvm->mutex);
+
+       /*
+        * For AP/GO interface, the tear down of the resources allocated to the
+        * interface should be handled as part of the bss_info_changed flow.
+        */
+       if (vif->type == NL80211_IFTYPE_AP) {
+               iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta);
+               goto out_release;
+       }
+
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               mvm->p2p_device_vif = NULL;
+               iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+               iwl_mvm_binding_remove_vif(mvm, vif);
+               iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt);
+               mvmvif->phy_ctxt = NULL;
+       }
+
+       /*
+        * TODO: remove this temporary code.
+        * Currently MVM FW supports power management only on single MAC.
+        * Check if only one additional interface remains after removing
+        * current one. Update power mode on the remaining interface.
+        */
+       if (mvm->vif_count)
+               mvm->vif_count--;
+       IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
+                          mvm->vif_count);
+       if (mvm->vif_count == 1) {
+               ieee80211_iterate_active_interfaces(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_power_update_iterator, mvm);
+       }
+
+       iwl_mvm_mac_ctxt_remove(mvm, vif);
+
+out_release:
+       iwl_mvm_mac_ctxt_release(mvm, vif);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
+{
+       return 0;
+}
+
+static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
+                                    unsigned int changed_flags,
+                                    unsigned int *total_flags,
+                                    u64 multicast)
+{
+       *total_flags = 0;
+}
+
+static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
+                                            struct ieee80211_vif *vif,
+                                            struct ieee80211_bss_conf *bss_conf,
+                                            u32 changes)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+       if (ret)
+               IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
+
+       if (changes & BSS_CHANGED_ASSOC) {
+               if (bss_conf->assoc) {
+                       /* add quota for this interface */
+                       ret = iwl_mvm_update_quotas(mvm, vif);
+                       if (ret) {
+                               IWL_ERR(mvm, "failed to update quotas\n");
+                               return;
+                       }
+                       iwl_mvm_remove_time_event(mvm, mvmvif,
+                                                 &mvmvif->time_event_data);
+               } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+                       /* remove AP station now that the MAC is unassoc */
+                       ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
+                       if (ret)
+                               IWL_ERR(mvm, "failed to remove AP station\n");
+                       mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+                       /* remove quota for this interface */
+                       ret = iwl_mvm_update_quotas(mvm, NULL);
+                       if (ret)
+                               IWL_ERR(mvm, "failed to update quotas\n");
+               }
+       } else if (changes & BSS_CHANGED_PS) {
+               /*
+                * TODO: remove this temporary code.
+                * Currently MVM FW supports power management only on single
+                * MAC. Avoid power mode update if more than one interface
+                * is active.
+                */
+               IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
+                                  mvm->vif_count);
+               if (mvm->vif_count == 1) {
+                       ret = iwl_mvm_power_update_mode(mvm, vif);
+                       if (ret)
+                               IWL_ERR(mvm, "failed to update power mode\n");
+               }
+       }
+}
+
+static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       /* Send the beacon template */
+       ret = iwl_mvm_mac_ctxt_beacon_changed(mvm, vif);
+       if (ret)
+               goto out_unlock;
+
+       /* Add the mac context */
+       ret = iwl_mvm_mac_ctxt_add(mvm, vif);
+       if (ret)
+               goto out_unlock;
+
+       /* Perform the binding */
+       ret = iwl_mvm_binding_add_vif(mvm, vif);
+       if (ret)
+               goto out_remove;
+
+       mvmvif->ap_active = true;
+
+       /* Send the bcast station. At this stage the TBTT and DTIM time events
+        * are added and applied to the scheduler */
+       ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta);
+       if (ret)
+               goto out_unbind;
+
+       ret = iwl_mvm_update_quotas(mvm, vif);
+       if (ret)
+               goto out_rm_bcast;
+
+       /* Need to update the P2P Device MAC */
+       if (vif->p2p && mvm->p2p_device_vif)
+               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
+
+       mutex_unlock(&mvm->mutex);
+       return 0;
+
+out_rm_bcast:
+       iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+out_unbind:
+       iwl_mvm_binding_remove_vif(mvm, vif);
+out_remove:
+       iwl_mvm_mac_ctxt_remove(mvm, vif);
+out_unlock:
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mutex_lock(&mvm->mutex);
+
+       mvmvif->ap_active = false;
+
+       /* Need to update the P2P Device MAC */
+       if (vif->p2p && mvm->p2p_device_vif)
+               iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif);
+
+       iwl_mvm_update_quotas(mvm, NULL);
+       iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta);
+       iwl_mvm_binding_remove_vif(mvm, vif);
+       iwl_mvm_mac_ctxt_remove(mvm, vif);
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_bss_conf *bss_conf,
+                                       u32 changes)
+{
+       /* Need to send a new beacon template to the FW */
+       if (changes & BSS_CHANGED_BEACON) {
+               if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
+                       IWL_WARN(mvm, "Failed updating beacon data\n");
+       }
+}
+
+static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct ieee80211_bss_conf *bss_conf,
+                                    u32 changes)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes);
+               break;
+       case NL80211_IFTYPE_AP:
+               iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes);
+               break;
+       default:
+               /* shouldn't happen */
+               WARN_ON_ONCE(1);
+       }
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct cfg80211_scan_request *req)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
+               return -EINVAL;
+
+       mutex_lock(&mvm->mutex);
+
+       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+               ret = iwl_mvm_scan_request(mvm, vif, req);
+       else
+               ret = -EBUSY;
+
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mutex_lock(&mvm->mutex);
+
+       iwl_mvm_cancel_scan(mvm);
+
+       mutex_unlock(&mvm->mutex);
+}
+
+static void
+iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw,
+                                 struct ieee80211_sta *sta, u16 tid,
+                                 int num_frames,
+                                 enum ieee80211_frame_release_type reason,
+                                 bool more_data)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+
+       /* TODO: how do we tell the fw to send frames for a specific TID */
+
+       /*
+        * The fw will send EOSP notification when the last frame will be
+        * transmitted.
+        */
+       iwl_mvm_sta_modify_sleep_tx_count(mvm, mvmsta->sta_id, reason,
+                                         num_frames);
+}
+
+static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  enum sta_notify_cmd cmd,
+                                  struct ieee80211_sta *sta)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+
+       switch (cmd) {
+       case STA_NOTIFY_SLEEP:
+               if (atomic_read(&mvmsta->pending_frames) > 0)
+                       ieee80211_sta_block_awake(hw, sta, true);
+               /*
+                * The fw updates the STA to be asleep. Tx packets on the Tx
+                * queues to this station will not be transmitted. The fw will
+                * send a Tx response with TX_STATUS_FAIL_DEST_PS.
+                */
+               break;
+       case STA_NOTIFY_AWAKE:
+               if (WARN_ON(mvmsta->sta_id == IWL_INVALID_STATION))
+                       break;
+               iwl_mvm_sta_modify_ps_wake(mvm, mvmsta->sta_id);
+               break;
+       default:
+               break;
+       }
+}
+
+static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta,
+                                enum ieee80211_sta_state old_state,
+                                enum ieee80211_sta_state new_state)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       IWL_DEBUG_MAC80211(mvm, "station %pM state change %d->%d\n",
+                          sta->addr, old_state, new_state);
+
+       /* this would be a mac80211 bug ... but don't crash */
+       if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
+               return -EINVAL;
+
+       /* if a STA is being removed, reuse its ID */
+       flush_work(&mvm->sta_drained_wk);
+
+       mutex_lock(&mvm->mutex);
+       if (old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE) {
+               ret = iwl_mvm_add_sta(mvm, vif, sta);
+       } else if (old_state == IEEE80211_STA_NONE &&
+                  new_state == IEEE80211_STA_AUTH) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_ASSOC) {
+               iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band);
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTHORIZED) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_AUTHORIZED &&
+                  new_state == IEEE80211_STA_ASSOC) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_ASSOC &&
+                  new_state == IEEE80211_STA_AUTH) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_AUTH &&
+                  new_state == IEEE80211_STA_NONE) {
+               ret = 0;
+       } else if (old_state == IEEE80211_STA_NONE &&
+                  new_state == IEEE80211_STA_NOTEXIST) {
+               ret = iwl_mvm_rm_sta(mvm, vif, sta);
+       } else {
+               ret = -EIO;
+       }
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       mvm->rts_threshold = value;
+
+       return 0;
+}
+
+static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif, u16 ac,
+                              const struct ieee80211_tx_queue_params *params)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->queue_params[ac] = *params;
+
+       /*
+        * No need to update right away, we'll get BSS_CHANGED_QOS
+        * The exception is P2P_DEVICE interface which needs immediate update.
+        */
+       if (vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+               int ret;
+
+               mutex_lock(&mvm->mutex);
+               ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
+               mutex_unlock(&mvm->mutex);
+               return ret;
+       }
+       return 0;
+}
+
+static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       u32 duration = min(IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS,
+                          200 + vif->bss_conf.beacon_int);
+       u32 min_duration = min(IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS,
+                              100 + vif->bss_conf.beacon_int);
+
+       if (WARN_ON_ONCE(vif->bss_conf.assoc))
+               return;
+
+       mutex_lock(&mvm->mutex);
+       /* Try really hard to protect the session and hear a beacon */
+       iwl_mvm_protect_session(mvm, vif, duration, min_duration);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_mac_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 iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       if (iwlwifi_mod_params.sw_crypto) {
+               IWL_DEBUG_MAC80211(mvm, "leave - hwcrypto disabled\n");
+               return -EOPNOTSUPP;
+       }
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+               /* fall-through */
+       case WLAN_CIPHER_SUITE_CCMP:
+               key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+               break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               WARN_ON_ONCE(!(hw->flags & IEEE80211_HW_MFP_CAPABLE));
+               break;
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               /*
+                * Support for TX only, at least for now, so accept
+                * the key and do nothing else. Then mac80211 will
+                * pass it for TX but we don't have to use it for RX.
+                */
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       mutex_lock(&mvm->mutex);
+
+       switch (cmd) {
+       case SET_KEY:
+               IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
+               ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, false);
+               if (ret) {
+                       IWL_WARN(mvm, "set key failed\n");
+                       /*
+                        * can't add key for RX, but we don't need it
+                        * in the device for TX so still return 0
+                        */
+                       ret = 0;
+               }
+
+               break;
+       case DISABLE_KEY:
+               IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n");
+               ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_key_conf *keyconf,
+                                       struct ieee80211_sta *sta,
+                                       u32 iv32, u16 *phase1key)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key);
+}
+
+
+static int iwl_mvm_roc(struct ieee80211_hw *hw,
+                      struct ieee80211_vif *vif,
+                      struct ieee80211_channel *channel,
+                      int duration)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct cfg80211_chan_def chandef;
+       int ret;
+
+       if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
+               IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type);
+               return -EINVAL;
+       }
+
+       IWL_DEBUG_MAC80211(mvm, "enter (%d, %d)\n", channel->hw_value,
+                          duration);
+
+       mutex_lock(&mvm->mutex);
+
+       cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
+       ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc,
+                                      &chandef, 1, 1);
+
+       /* Schedule the time events */
+       ret = iwl_mvm_start_p2p_roc(mvm, vif, duration);
+
+       mutex_unlock(&mvm->mutex);
+       IWL_DEBUG_MAC80211(mvm, "leave\n");
+
+       return ret;
+}
+
+static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       IWL_DEBUG_MAC80211(mvm, "enter\n");
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_stop_p2p_roc(mvm);
+       mutex_unlock(&mvm->mutex);
+
+       IWL_DEBUG_MAC80211(mvm, "leave\n");
+       return 0;
+}
+
+static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
+                              struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       IWL_DEBUG_MAC80211(mvm, "Add PHY context\n");
+       ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def,
+                                  ctx->rx_chains_static,
+                                  ctx->rx_chains_dynamic);
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
+static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
+                                  struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt);
+       mutex_unlock(&mvm->mutex);
+}
+
+static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
+                                  struct ieee80211_chanctx_conf *ctx,
+                                  u32 changed)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+
+       mutex_lock(&mvm->mutex);
+       iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
+                                ctx->rx_chains_static,
+                                ctx->rx_chains_dynamic);
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+
+       mvmvif->phy_ctxt = phyctx;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_AP:
+               /*
+                * The AP binding flow is handled as part of the start_ap flow
+                * (in bss_info_changed).
+                */
+               ret = 0;
+               goto out_unlock;
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MONITOR:
+               break;
+       default:
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       ret = iwl_mvm_binding_add_vif(mvm, vif);
+       if (ret)
+               goto out_unlock;
+
+       /*
+        * Setting the quota at this stage is only required for monitor
+        * interfaces. For the other types, the bss_info changed flow
+        * will handle quota settings.
+        */
+       if (vif->type == NL80211_IFTYPE_MONITOR) {
+               ret = iwl_mvm_update_quotas(mvm, vif);
+               if (ret)
+                       goto out_remove_binding;
+       }
+
+       goto out_unlock;
+
+ out_remove_binding:
+       iwl_mvm_binding_remove_vif(mvm, vif);
+ out_unlock:
+       mutex_unlock(&mvm->mutex);
+       if (ret)
+               mvmvif->phy_ctxt = NULL;
+       return ret;
+}
+
+static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mutex_lock(&mvm->mutex);
+
+       iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
+
+       if (vif->type == NL80211_IFTYPE_AP)
+               goto out_unlock;
+
+       iwl_mvm_binding_remove_vif(mvm, vif);
+       switch (vif->type) {
+       case NL80211_IFTYPE_MONITOR:
+               iwl_mvm_update_quotas(mvm, vif);
+               break;
+       default:
+               break;
+       }
+
+out_unlock:
+       mvmvif->phy_ctxt = NULL;
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
+                          struct ieee80211_sta *sta,
+                          bool set)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+
+       if (!mvm_sta || !mvm_sta->vif) {
+               IWL_ERR(mvm, "Station is not associated to a vif\n");
+               return -EINVAL;
+       }
+
+       return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif);
+}
+
+struct ieee80211_ops iwl_mvm_hw_ops = {
+       .tx = iwl_mvm_mac_tx,
+       .ampdu_action = iwl_mvm_mac_ampdu_action,
+       .start = iwl_mvm_mac_start,
+       .restart_complete = iwl_mvm_mac_restart_complete,
+       .stop = iwl_mvm_mac_stop,
+       .add_interface = iwl_mvm_mac_add_interface,
+       .remove_interface = iwl_mvm_mac_remove_interface,
+       .config = iwl_mvm_mac_config,
+       .configure_filter = iwl_mvm_configure_filter,
+       .bss_info_changed = iwl_mvm_bss_info_changed,
+       .hw_scan = iwl_mvm_mac_hw_scan,
+       .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan,
+       .sta_state = iwl_mvm_mac_sta_state,
+       .sta_notify = iwl_mvm_mac_sta_notify,
+       .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
+       .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
+       .conf_tx = iwl_mvm_mac_conf_tx,
+       .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .set_key = iwl_mvm_mac_set_key,
+       .update_tkip_key = iwl_mvm_mac_update_tkip_key,
+       .remain_on_channel = iwl_mvm_roc,
+       .cancel_remain_on_channel = iwl_mvm_cancel_roc,
+
+       .add_chanctx = iwl_mvm_add_chanctx,
+       .remove_chanctx = iwl_mvm_remove_chanctx,
+       .change_chanctx = iwl_mvm_change_chanctx,
+       .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
+       .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
+
+       .start_ap = iwl_mvm_start_ap,
+       .stop_ap = iwl_mvm_stop_ap,
+
+       .set_tim = iwl_mvm_set_tim,
+
+#ifdef CONFIG_PM_SLEEP
+       /* look at d3.c */
+       .suspend = iwl_mvm_suspend,
+       .resume = iwl_mvm_resume,
+       .set_wakeup = iwl_mvm_set_wakeup,
+       .set_rekey_data = iwl_mvm_set_rekey_data,
+#if IS_ENABLED(CONFIG_IPV6)
+       .ipv6_addr_change = iwl_mvm_ipv6_addr_change,
+#endif
+       .set_default_unicast_key = iwl_mvm_set_default_unicast_key,
+#endif
+};
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
new file mode 100644 (file)
index 0000000..4e339cc
--- /dev/null
@@ -0,0 +1,500 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_MVM_H__
+#define __IWL_MVM_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include <linux/in6.h>
+
+#include "iwl-op-mode.h"
+#include "iwl-trans.h"
+#include "iwl-notif-wait.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-test.h"
+#include "iwl-trans.h"
+#include "sta.h"
+#include "fw-api.h"
+
+#define IWL_INVALID_MAC80211_QUEUE     0xff
+#define IWL_MVM_MAX_ADDRESSES          2
+#define IWL_RSSI_OFFSET 44
+
+enum iwl_mvm_tx_fifo {
+       IWL_MVM_TX_FIFO_BK = 0,
+       IWL_MVM_TX_FIFO_BE,
+       IWL_MVM_TX_FIFO_VI,
+       IWL_MVM_TX_FIFO_VO,
+};
+
+/* Placeholder */
+#define IWL_OFFCHANNEL_QUEUE 8
+#define IWL_FIRST_AMPDU_QUEUE 11
+
+extern struct ieee80211_ops iwl_mvm_hw_ops;
+/**
+ * struct iwl_mvm_mod_params - module parameters for iwlmvm
+ * @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
+ *     We will register to mac80211 to have testmode working. The NIC must not
+ *     be up'ed after the INIT fw asserted. This is useful to be able to use
+ *     proprietary tools over testmode to debug the INIT fw.
+ * @power_scheme: CAM(Continuous Active Mode)-1, BPS(Balanced Power
+ *     Save)-2(default), LP(Low Power)-3
+ */
+struct iwl_mvm_mod_params {
+       bool init_dbg;
+       int power_scheme;
+};
+extern struct iwl_mvm_mod_params iwlmvm_mod_params;
+
+struct iwl_mvm_phy_ctxt {
+       u16 id;
+       u16 color;
+
+       /*
+        * TODO: This should probably be removed. Currently here only for rate
+        * scaling algorithm
+        */
+       struct ieee80211_channel *channel;
+};
+
+struct iwl_mvm_time_event_data {
+       struct ieee80211_vif *vif;
+       struct list_head list;
+       unsigned long end_jiffies;
+       u32 duration;
+       bool running;
+       u32 uid;
+
+       /*
+        * The access to the 'id' field must be done when the
+        * mvm->time_event_lock is held, as it value is used to indicate
+        * if the te is in the time event list or not (when id == TE_MAX)
+        */
+       u32 id;
+};
+
+ /* Power management */
+
+/**
+ * enum iwl_power_scheme
+ * @IWL_POWER_LEVEL_CAM - Continuously Active Mode
+ * @IWL_POWER_LEVEL_BPS - Balanced Power Save (default)
+ * @IWL_POWER_LEVEL_LP  - Low Power
+ */
+enum iwl_power_scheme {
+       IWL_POWER_SCHEME_CAM = 1,
+       IWL_POWER_SCHEME_BPS,
+       IWL_POWER_SCHEME_LP
+};
+
+#define IWL_CONN_MAX_LISTEN_INTERVAL   70
+
+/**
+ * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
+ * @id: between 0 and 3
+ * @color: to solve races upon MAC addition and removal
+ * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA
+ * @uploaded: indicates the MAC context has been added to the device
+ * @ap_active: indicates that ap context is configured, and that the interface
+ *  should get quota etc.
+ * @queue_params: QoS params for this MAC
+ * @bcast_sta: station used for broadcast packets. Used by the following
+ *  vifs: P2P_DEVICE, GO and AP.
+ * @beacon_skb: the skb used to hold the AP/GO beacon template
+ */
+struct iwl_mvm_vif {
+       u16 id;
+       u16 color;
+       u8 ap_sta_id;
+
+       bool uploaded;
+       bool ap_active;
+
+       enum iwl_tsf_id tsf_id;
+
+       /*
+        * QoS data from mac80211, need to store this here
+        * as mac80211 has a separate callback but we need
+        * to have the data for the MAC context
+        */
+       struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+       struct iwl_mvm_time_event_data time_event_data;
+
+       struct iwl_mvm_int_sta bcast_sta;
+
+       /*
+        * Assigned while mac80211 has the interface in a channel context,
+        * or, for P2P Device, while it exists.
+        */
+       struct iwl_mvm_phy_ctxt *phy_ctxt;
+
+#ifdef CONFIG_PM_SLEEP
+       /* WoWLAN GTK rekey data */
+       struct {
+               u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
+               __le64 replay_ctr;
+               bool valid;
+       } rekey_data;
+
+       int tx_key_idx;
+
+#if IS_ENABLED(CONFIG_IPV6)
+       /* IPv6 addresses for WoWLAN */
+       struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS];
+       int num_target_ipv6_addrs;
+#endif
+#endif
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       struct dentry *dbgfs_dir;
+       void *dbgfs_data;
+#endif
+};
+
+static inline struct iwl_mvm_vif *
+iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif)
+{
+       return (void *)vif->drv_priv;
+}
+
+enum iwl_mvm_status {
+       IWL_MVM_STATUS_HW_RFKILL,
+       IWL_MVM_STATUS_ROC_RUNNING,
+       IWL_MVM_STATUS_IN_HW_RESTART,
+};
+
+enum iwl_scan_status {
+       IWL_MVM_SCAN_NONE,
+       IWL_MVM_SCAN_OS,
+};
+
+/**
+ * struct iwl_nvm_section - describes an NVM section in memory.
+ *
+ * This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD,
+ * and saved for later use by the driver. Not all NVM sections are saved
+ * this way, only the needed ones.
+ */
+struct iwl_nvm_section {
+       u16 length;
+       const u8 *data;
+};
+
+struct iwl_mvm {
+       /* for logger access */
+       struct device *dev;
+
+       struct iwl_trans *trans;
+       const struct iwl_fw *fw;
+       const struct iwl_cfg *cfg;
+       struct iwl_phy_db *phy_db;
+       struct ieee80211_hw *hw;
+
+       /* for protecting access to iwl_mvm */
+       struct mutex mutex;
+       struct list_head async_handlers_list;
+       spinlock_t async_handlers_lock;
+       struct work_struct async_handlers_wk;
+
+       struct work_struct roc_done_wk;
+
+       unsigned long status;
+
+       enum iwl_ucode_type cur_ucode;
+       bool ucode_loaded;
+       bool init_ucode_run;
+       u32 error_event_table;
+       u32 log_event_table;
+
+       u32 ampdu_ref;
+
+       struct iwl_notif_wait_data notif_wait;
+
+       unsigned long transport_queue_stop;
+       u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
+       atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
+
+       struct iwl_nvm_data *nvm_data;
+       /* eeprom blob for debugfs/testmode */
+       u8 *eeprom_blob;
+       size_t eeprom_blob_size;
+       /* NVM sections for 7000 family */
+       struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS];
+
+       /* EEPROM MAC addresses */
+       struct mac_address addresses[IWL_MVM_MAX_ADDRESSES];
+
+       /* data related to data path */
+       struct iwl_rx_phy_info last_phy_info;
+       struct ieee80211_sta __rcu *fw_id_to_mac_id[IWL_MVM_STATION_COUNT];
+       struct work_struct sta_drained_wk;
+       unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
+
+       /* configured by mac80211 */
+       u32 rts_threshold;
+
+       /* Scan status, cmd (pre-allocated) and auxiliary station */
+       enum iwl_scan_status scan_status;
+       struct iwl_scan_cmd *scan_cmd;
+
+       /* Internal station */
+       struct iwl_mvm_int_sta aux_sta;
+
+       u8 scan_last_antenna_idx; /* to toggle TX between antennas */
+       u8 mgmt_last_antenna_idx;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+       struct dentry *debugfs_dir;
+       u32 dbgfs_sram_offset, dbgfs_sram_len;
+       bool prevent_power_down_d3;
+#endif
+
+       struct iwl_mvm_phy_ctxt phy_ctxt_roc;
+
+       struct list_head time_event_list;
+       spinlock_t time_event_lock;
+
+       /*
+        * A bitmap indicating the index of the key in use. The firmware
+        * can hold 16 keys at most. Reflect this fact.
+        */
+       unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
+       u8 vif_count;
+
+       struct led_classdev led;
+
+       struct ieee80211_vif *p2p_device_vif;
+};
+
+/* Extract MVM priv from op_mode and _hw */
+#define IWL_OP_MODE_GET_MVM(_iwl_op_mode)              \
+       ((struct iwl_mvm *)(_iwl_op_mode)->op_mode_specific)
+
+#define IWL_MAC80211_GET_MVM(_hw)                      \
+       IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv))
+
+extern const u8 iwl_mvm_ac_to_tx_fifo[];
+
+struct iwl_rate_info {
+       u8 plcp;        /* uCode API:  IWL_RATE_6M_PLCP, etc. */
+       u8 plcp_siso;   /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
+       u8 plcp_mimo2;  /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
+       u8 plcp_mimo3;  /* uCode API:  IWL_RATE_MIMO3_6M_PLCP, etc. */
+       u8 ieee;        /* MAC header:  IWL_RATE_6M_IEEE, etc. */
+};
+
+/******************
+ * MVM Methods
+ ******************/
+/* uCode */
+int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm);
+
+/* Utils */
+int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
+                                       enum ieee80211_band band);
+u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
+void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
+u8 first_antenna(u8 mask);
+u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
+
+/* Tx / Host Commands */
+int __must_check iwl_mvm_send_cmd(struct iwl_mvm *mvm,
+                                 struct iwl_host_cmd *cmd);
+int __must_check iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id,
+                                     u32 flags, u16 len, const void *data);
+int __must_check iwl_mvm_send_cmd_status(struct iwl_mvm *mvm,
+                                        struct iwl_host_cmd *cmd,
+                                        u32 *status);
+int __must_check iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id,
+                                            u16 len, const void *data,
+                                            u32 *status);
+int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
+                  struct ieee80211_sta *sta);
+int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_mvm_get_tx_fail_reason(u32 status);
+#else
+static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; }
+#endif
+int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync);
+void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm);
+
+/* Statistics */
+int iwl_mvm_rx_reply_statistics(struct iwl_mvm *mvm,
+                               struct iwl_rx_cmd_buffer *rxb,
+                               struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
+                         struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd);
+
+/* NVM */
+int iwl_nvm_init(struct iwl_mvm *mvm);
+
+int iwl_mvm_up(struct iwl_mvm *mvm);
+int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm);
+
+int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm);
+
+/*
+ * FW notifications / CMD responses handlers
+ * Convention: iwl_mvm_rx_<NAME OF THE CMD>
+ */
+int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                      struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                     struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                       struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                        struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm,
+                               struct iwl_rx_cmd_buffer *rxb,
+                               struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                        struct iwl_device_cmd *cmd);
+
+/* MVM PHY */
+int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+                        struct cfg80211_chan_def *chandef,
+                        u8 chains_static, u8 chains_dynamic);
+int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+                            struct cfg80211_chan_def *chandef,
+                            u8 chains_static, u8 chains_dynamic);
+void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm,
+                            struct iwl_mvm_phy_ctxt *ctxt);
+
+/* MAC (virtual interface) programming */
+int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
+                               struct ieee80211_vif *vif);
+int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif);
+
+/* Bindings */
+int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+/* Quota management */
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif);
+
+/* Scanning */
+int iwl_mvm_scan_request(struct iwl_mvm *mvm,
+                        struct ieee80211_vif *vif,
+                        struct cfg80211_scan_request *req);
+int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                            struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                            struct iwl_device_cmd *cmd);
+void iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
+
+/* MVM debugfs */
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
+int iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                              struct dentry *dbgfs_dir);
+void iwl_power_get_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         struct iwl_powertable_cmd *cmd);
+#else
+static inline int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm,
+                                        struct dentry *dbgfs_dir)
+{
+       return 0;
+}
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
+
+/* rate scaling */
+int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
+                       u8 flags, bool init);
+
+/* power managment */
+int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+int iwl_mvm_leds_init(struct iwl_mvm *mvm);
+void iwl_mvm_leds_exit(struct iwl_mvm *mvm);
+
+/* D3 (WoWLAN, NetDetect) */
+int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
+int iwl_mvm_resume(struct ieee80211_hw *hw);
+void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled);
+void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif,
+                           struct cfg80211_gtk_rekey_data *data);
+void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
+                             struct inet6_dev *idev);
+void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif, int idx);
+
+#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
new file mode 100644 (file)
index 0000000..20016bc
--- /dev/null
@@ -0,0 +1,311 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "iwl-trans.h"
+#include "mvm.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-eeprom-read.h"
+#include "iwl-nvm-parse.h"
+
+/* list of NVM sections we are allowed/need to read */
+static const int nvm_to_read[] = {
+       NVM_SECTION_TYPE_HW,
+       NVM_SECTION_TYPE_SW,
+       NVM_SECTION_TYPE_CALIBRATION,
+       NVM_SECTION_TYPE_PRODUCTION,
+};
+
+/* used to simplify the shared operations on NCM_ACCESS_CMD versions */
+union iwl_nvm_access_cmd {
+       struct iwl_nvm_access_cmd_ver1 ver1;
+       struct iwl_nvm_access_cmd_ver2 ver2;
+};
+union iwl_nvm_access_resp {
+       struct iwl_nvm_access_resp_ver1 ver1;
+       struct iwl_nvm_access_resp_ver2 ver2;
+};
+
+static inline void iwl_nvm_fill_read_ver1(struct iwl_nvm_access_cmd_ver1 *cmd,
+                                         u16 offset, u16 length)
+{
+       cmd->offset = cpu_to_le16(offset);
+       cmd->length = cpu_to_le16(length);
+       cmd->cache_refresh = 1;
+}
+
+static inline void iwl_nvm_fill_read_ver2(struct iwl_nvm_access_cmd_ver2 *cmd,
+                                         u16 offset, u16 length, u16 section)
+{
+       cmd->offset = cpu_to_le16(offset);
+       cmd->length = cpu_to_le16(length);
+       cmd->type = cpu_to_le16(section);
+}
+
+static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
+                             u16 offset, u16 length, u8 *data)
+{
+       union iwl_nvm_access_cmd nvm_access_cmd;
+       union iwl_nvm_access_resp *nvm_resp;
+       struct iwl_rx_packet *pkt;
+       struct iwl_host_cmd cmd = {
+               .id = NVM_ACCESS_CMD,
+               .flags = CMD_SYNC | CMD_WANT_SKB,
+               .data = { &nvm_access_cmd, },
+       };
+       int ret, bytes_read, offset_read;
+       u8 *resp_data;
+
+       memset(&nvm_access_cmd, 0, sizeof(nvm_access_cmd));
+
+       /* TODO: not sure family should be the decider, maybe FW version? */
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               iwl_nvm_fill_read_ver2(&(nvm_access_cmd.ver2),
+                                      offset, length, section);
+               cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver2);
+       } else {
+               iwl_nvm_fill_read_ver1(&(nvm_access_cmd.ver1),
+                                      offset, length);
+               cmd.len[0] = sizeof(struct iwl_nvm_access_cmd_ver1);
+       }
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       if (ret)
+               return ret;
+
+       pkt = cmd.resp_pkt;
+       if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+               IWL_ERR(mvm, "Bad return from NVM_ACCES_COMMAND (0x%08X)\n",
+                       pkt->hdr.flags);
+               ret = -EIO;
+               goto exit;
+       }
+
+       /* Extract NVM response */
+       nvm_resp = (void *)pkt->data;
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               ret = le16_to_cpu(nvm_resp->ver2.status);
+               bytes_read = le16_to_cpu(nvm_resp->ver2.length);
+               offset_read = le16_to_cpu(nvm_resp->ver2.offset);
+               resp_data = nvm_resp->ver2.data;
+       } else {
+               ret = le16_to_cpu(nvm_resp->ver1.length) <= 0;
+               bytes_read = le16_to_cpu(nvm_resp->ver1.length);
+               offset_read = le16_to_cpu(nvm_resp->ver1.offset);
+               resp_data = nvm_resp->ver1.data;
+       }
+       if (ret) {
+               IWL_ERR(mvm,
+                       "NVM access command failed with status %d (device: %s)\n",
+                       ret, mvm->cfg->name);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       if (offset_read != offset) {
+               IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n",
+                       offset_read);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       /* Write data to NVM */
+       memcpy(data + offset, resp_data, bytes_read);
+       ret = bytes_read;
+
+exit:
+       iwl_free_resp(&cmd);
+       return ret;
+}
+
+/*
+ * Reads an NVM section completely.
+ * NICs prior to 7000 family doesn't have a real NVM, but just read
+ * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
+ * by uCode, we need to manually check in this case that we don't
+ * overflow and try to read more than the EEPROM size.
+ * For 7000 family NICs, we supply the maximal size we can read, and
+ * the uCode fills the response with as much data as we can,
+ * without overflowing, so no check is needed.
+ */
+static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
+                               u8 *data)
+{
+       u16 length, offset = 0;
+       int ret;
+       bool old_eeprom = mvm->cfg->device_family != IWL_DEVICE_FAMILY_7000;
+
+       length = (iwlwifi_mod_params.amsdu_size_8K ? (8 * 1024) : (4 * 1024))
+               - sizeof(union iwl_nvm_access_cmd)
+               - sizeof(struct iwl_rx_packet);
+       /*
+        * if length is greater than EEPROM size, truncate it because uCode
+        * doesn't check it by itself, and exit the loop when reached.
+        */
+       if (old_eeprom && length > mvm->cfg->base_params->eeprom_size)
+               length = mvm->cfg->base_params->eeprom_size;
+       ret = length;
+
+       /* Read the NVM until exhausted (reading less than requested) */
+       while (ret == length) {
+               ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
+               if (ret < 0) {
+                       IWL_ERR(mvm,
+                               "Cannot read NVM from section %d offset %d, length %d\n",
+                               section, offset, length);
+                       return ret;
+               }
+               offset += ret;
+               if (old_eeprom && offset == mvm->cfg->base_params->eeprom_size)
+                       break;
+       }
+
+       IWL_INFO(mvm, "NVM section %d read completed\n", section);
+       return offset;
+}
+
+static struct iwl_nvm_data *
+iwl_parse_nvm_sections(struct iwl_mvm *mvm)
+{
+       struct iwl_nvm_section *sections = mvm->nvm_sections;
+       const __le16 *hw, *sw, *calib;
+
+       /* Checking for required sections */
+       if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
+           !mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) {
+               IWL_ERR(mvm, "Can't parse empty NVM sections\n");
+               return NULL;
+       }
+
+       if (WARN_ON(!mvm->cfg))
+               return NULL;
+
+       hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data;
+       sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
+       calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
+       return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib);
+}
+
+int iwl_nvm_init(struct iwl_mvm *mvm)
+{
+       int ret, i, section;
+       u8 *nvm_buffer, *temp;
+
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               /* TODO: find correct NVM max size for a section */
+               nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
+                                    GFP_KERNEL);
+               if (!nvm_buffer)
+                       return -ENOMEM;
+               for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) {
+                       section = nvm_to_read[i];
+                       /* we override the constness for initial read */
+                       ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
+                       if (ret < 0)
+                               break;
+                       temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
+                       if (!temp) {
+                               ret = -ENOMEM;
+                               break;
+                       }
+                       mvm->nvm_sections[section].data = temp;
+                       mvm->nvm_sections[section].length = ret;
+               }
+               kfree(nvm_buffer);
+               if (ret < 0)
+                       return ret;
+       } else {
+               /* allocate eeprom */
+               mvm->eeprom_blob_size = mvm->cfg->base_params->eeprom_size;
+               IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM size = %zd\n",
+                                mvm->eeprom_blob_size);
+               mvm->eeprom_blob = kzalloc(mvm->eeprom_blob_size, GFP_KERNEL);
+               if (!mvm->eeprom_blob)
+                       return -ENOMEM;
+
+               ret = iwl_nvm_read_section(mvm, 0, mvm->eeprom_blob);
+               if (ret != mvm->eeprom_blob_size) {
+                       IWL_ERR(mvm, "Read partial NVM %d/%zd\n",
+                               ret, mvm->eeprom_blob_size);
+                       kfree(mvm->eeprom_blob);
+                       mvm->eeprom_blob = NULL;
+                       return -EINVAL;
+               }
+       }
+
+       ret = 0;
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
+               mvm->nvm_data = iwl_parse_nvm_sections(mvm);
+       else
+               mvm->nvm_data =
+                       iwl_parse_eeprom_data(mvm->trans->dev,
+                                             mvm->cfg,
+                                             mvm->eeprom_blob,
+                                             mvm->eeprom_blob_size);
+
+       if (!mvm->nvm_data) {
+               kfree(mvm->eeprom_blob);
+               mvm->eeprom_blob = NULL;
+               ret = -ENOMEM;
+       }
+
+       return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
new file mode 100644 (file)
index 0000000..983dca3
--- /dev/null
@@ -0,0 +1,679 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+#include "iwl-notif-wait.h"
+#include "iwl-trans.h"
+#include "iwl-op-mode.h"
+#include "iwl-fw.h"
+#include "iwl-debug.h"
+#include "iwl-drv.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-phy-db.h"
+#include "iwl-eeprom-parse.h"
+#include "iwl-csr.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+#include "rs.h"
+#include "fw-api-scan.h"
+#include "time-event.h"
+
+/*
+ * module name, copyright, version, etc.
+ */
+#define DRV_DESCRIPTION        "The new Intel(R) wireless AGN driver for Linux"
+
+#define DRV_VERSION     IWLWIFI_VERSION
+
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_LICENSE("GPL");
+
+static const struct iwl_op_mode_ops iwl_mvm_ops;
+
+struct iwl_mvm_mod_params iwlmvm_mod_params = {
+       .power_scheme = IWL_POWER_SCHEME_BPS,
+       /* rest of fields are 0 by default */
+};
+
+module_param_named(init_dbg, iwlmvm_mod_params.init_dbg, bool, S_IRUGO);
+MODULE_PARM_DESC(init_dbg,
+                "set to true to debug an ASSERT in INIT fw (default: false");
+module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO);
+MODULE_PARM_DESC(power_scheme,
+                "power management scheme: 1-active, 2-balanced, 3-low power, default: 2");
+
+/*
+ * module init and exit functions
+ */
+static int __init iwl_mvm_init(void)
+{
+       int ret;
+
+       ret = iwl_mvm_rate_control_register();
+       if (ret) {
+               pr_err("Unable to register rate control algorithm: %d\n", ret);
+               return ret;
+       }
+
+       ret = iwl_opmode_register("iwlmvm", &iwl_mvm_ops);
+
+       if (ret) {
+               pr_err("Unable to register MVM op_mode: %d\n", ret);
+               iwl_mvm_rate_control_unregister();
+       }
+
+       return ret;
+}
+module_init(iwl_mvm_init);
+
+static void __exit iwl_mvm_exit(void)
+{
+       iwl_opmode_deregister("iwlmvm");
+       iwl_mvm_rate_control_unregister();
+}
+module_exit(iwl_mvm_exit);
+
+static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash;
+       u32 reg_val = 0;
+
+       /*
+        * We can't upload the correct value to the INIT image
+        * as we don't have nvm_data by that time.
+        *
+        * TODO: Figure out what we should do here
+        */
+       if (mvm->nvm_data) {
+               radio_cfg_type = mvm->nvm_data->radio_cfg_type;
+               radio_cfg_step = mvm->nvm_data->radio_cfg_step;
+               radio_cfg_dash = mvm->nvm_data->radio_cfg_dash;
+       } else {
+               radio_cfg_type = 0;
+               radio_cfg_step = 0;
+               radio_cfg_dash = 0;
+       }
+
+       /* SKU control */
+       reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) <<
+                               CSR_HW_IF_CONFIG_REG_POS_MAC_STEP;
+       reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) <<
+                               CSR_HW_IF_CONFIG_REG_POS_MAC_DASH;
+
+       /* radio configuration */
+       reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE;
+       reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP;
+       reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH;
+
+       WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) &
+                ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE);
+
+       /* silicon bits */
+       reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
+       reg_val |= CSR_HW_IF_CONFIG_REG_BIT_MAC_SI;
+
+       iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
+                               CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
+                               CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP |
+                               CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE |
+                               CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP |
+                               CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH |
+                               CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI |
+                               CSR_HW_IF_CONFIG_REG_BIT_MAC_SI,
+                               reg_val);
+
+       IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type,
+                      radio_cfg_step, radio_cfg_dash);
+
+       /*
+        * W/A : NIC is stuck in a reset state after Early PCIe power off
+        * (PCIe power is lost before PERST# is asserted), causing ME FW
+        * to lose ownership and not being able to obtain it back.
+        */
+       iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG,
+                              APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
+                              ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
+}
+
+struct iwl_rx_handlers {
+       u8 cmd_id;
+       bool async;
+       int (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                 struct iwl_device_cmd *cmd);
+};
+
+#define RX_HANDLER(_cmd_id, _fn, _async)       \
+       { .cmd_id = _cmd_id , .fn = _fn , .async = _async }
+
+/*
+ * Handlers for fw notifications
+ * Convention: RX_HANDLER(CMD_NAME, iwl_mvm_rx_CMD_NAME
+ * This list should be in order of frequency for performance purposes.
+ *
+ * The handler can be SYNC - this means that it will be called in the Rx path
+ * which can't acquire mvm->mutex. If the handler needs to hold mvm->mutex (and
+ * only in this case!), it should be set as ASYNC. In that case, it will be
+ * called from a worker with mvm->mutex held.
+ */
+static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
+       RX_HANDLER(REPLY_RX_MPDU_CMD, iwl_mvm_rx_rx_mpdu, false),
+       RX_HANDLER(REPLY_RX_PHY_CMD, iwl_mvm_rx_rx_phy_cmd, false),
+       RX_HANDLER(TX_CMD, iwl_mvm_rx_tx_cmd, false),
+       RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false),
+       RX_HANDLER(TIME_EVENT_NOTIFICATION, iwl_mvm_rx_time_event_notif, false),
+
+       RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
+       RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false),
+
+       RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
+       RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
+
+       RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
+};
+#undef RX_HANDLER
+#define CMD(x) [x] = #x
+
+static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
+       CMD(MVM_ALIVE),
+       CMD(REPLY_ERROR),
+       CMD(INIT_COMPLETE_NOTIF),
+       CMD(PHY_CONTEXT_CMD),
+       CMD(MGMT_MCAST_KEY),
+       CMD(TX_CMD),
+       CMD(TXPATH_FLUSH),
+       CMD(MAC_CONTEXT_CMD),
+       CMD(TIME_EVENT_CMD),
+       CMD(TIME_EVENT_NOTIFICATION),
+       CMD(BINDING_CONTEXT_CMD),
+       CMD(TIME_QUOTA_CMD),
+       CMD(RADIO_VERSION_NOTIFICATION),
+       CMD(SCAN_REQUEST_CMD),
+       CMD(SCAN_ABORT_CMD),
+       CMD(SCAN_START_NOTIFICATION),
+       CMD(SCAN_RESULTS_NOTIFICATION),
+       CMD(SCAN_COMPLETE_NOTIFICATION),
+       CMD(NVM_ACCESS_CMD),
+       CMD(PHY_CONFIGURATION_CMD),
+       CMD(CALIB_RES_NOTIF_PHY_DB),
+       CMD(SET_CALIB_DEFAULT_CMD),
+       CMD(CALIBRATION_COMPLETE_NOTIFICATION),
+       CMD(ADD_STA),
+       CMD(REMOVE_STA),
+       CMD(LQ_CMD),
+       CMD(SCAN_OFFLOAD_CONFIG_CMD),
+       CMD(SCAN_OFFLOAD_REQUEST_CMD),
+       CMD(SCAN_OFFLOAD_ABORT_CMD),
+       CMD(SCAN_OFFLOAD_COMPLETE),
+       CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
+       CMD(POWER_TABLE_CMD),
+       CMD(WEP_KEY),
+       CMD(REPLY_RX_PHY_CMD),
+       CMD(REPLY_RX_MPDU_CMD),
+       CMD(BEACON_TEMPLATE_CMD),
+       CMD(STATISTICS_NOTIFICATION),
+       CMD(TX_ANT_CONFIGURATION_CMD),
+       CMD(D3_CONFIG_CMD),
+       CMD(PROT_OFFLOAD_CONFIG_CMD),
+       CMD(OFFLOADS_QUERY_CMD),
+       CMD(REMOTE_WAKE_CONFIG_CMD),
+       CMD(WOWLAN_PATTERNS),
+       CMD(WOWLAN_CONFIGURATION),
+       CMD(WOWLAN_TSC_RSC_PARAM),
+       CMD(WOWLAN_TKIP_PARAM),
+       CMD(WOWLAN_KEK_KCK_MATERIAL),
+       CMD(WOWLAN_GET_STATUSES),
+       CMD(WOWLAN_TX_POWER_PER_DB),
+       CMD(NET_DETECT_CONFIG_CMD),
+       CMD(NET_DETECT_PROFILES_QUERY_CMD),
+       CMD(NET_DETECT_PROFILES_CMD),
+       CMD(NET_DETECT_HOTSPOTS_CMD),
+       CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
+};
+#undef CMD
+
+/* this forward declaration can avoid to export the function */
+static void iwl_mvm_async_handlers_wk(struct work_struct *wk);
+
+static struct iwl_op_mode *
+iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
+                     const struct iwl_fw *fw, struct dentry *dbgfs_dir)
+{
+       struct ieee80211_hw *hw;
+       struct iwl_op_mode *op_mode;
+       struct iwl_mvm *mvm;
+       struct iwl_trans_config trans_cfg = {};
+       static const u8 no_reclaim_cmds[] = {
+               TX_CMD,
+       };
+       int err, scan_size;
+
+       switch (cfg->device_family) {
+       case IWL_DEVICE_FAMILY_6030:
+       case IWL_DEVICE_FAMILY_6005:
+       case IWL_DEVICE_FAMILY_7000:
+               break;
+       default:
+               IWL_ERR(trans, "Trying to load mvm on an unsupported device\n");
+               return NULL;
+       }
+
+       /********************************
+        * 1. Allocating and configuring HW data
+        ********************************/
+       hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) +
+                               sizeof(struct iwl_mvm),
+                               &iwl_mvm_hw_ops);
+       if (!hw)
+               return NULL;
+
+       op_mode = hw->priv;
+       op_mode->ops = &iwl_mvm_ops;
+       op_mode->trans = trans;
+
+       mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       mvm->dev = trans->dev;
+       mvm->trans = trans;
+       mvm->cfg = cfg;
+       mvm->fw = fw;
+       mvm->hw = hw;
+
+       mutex_init(&mvm->mutex);
+       spin_lock_init(&mvm->async_handlers_lock);
+       INIT_LIST_HEAD(&mvm->time_event_list);
+       INIT_LIST_HEAD(&mvm->async_handlers_list);
+       spin_lock_init(&mvm->time_event_lock);
+
+       INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
+       INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
+       INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
+
+       SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev);
+
+       /*
+        * Populate the state variables that the transport layer needs
+        * to know about.
+        */
+       trans_cfg.op_mode = op_mode;
+       trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
+       trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
+       trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K;
+
+       /* TODO: this should really be a TLV */
+       if (cfg->device_family == IWL_DEVICE_FAMILY_7000)
+               trans_cfg.bc_table_dword = true;
+
+       if (!iwlwifi_mod_params.wd_disable)
+               trans_cfg.queue_watchdog_timeout = cfg->base_params->wd_timeout;
+       else
+               trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED;
+
+       trans_cfg.command_names = iwl_mvm_cmd_strings;
+
+       trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE;
+       trans_cfg.cmd_fifo = IWL_MVM_CMD_FIFO;
+
+       snprintf(mvm->hw->wiphy->fw_version,
+                sizeof(mvm->hw->wiphy->fw_version),
+                "%s", fw->fw_version);
+
+       /* Configure transport layer */
+       iwl_trans_configure(mvm->trans, &trans_cfg);
+
+       trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
+       trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+
+       /* set up notification wait support */
+       iwl_notification_wait_init(&mvm->notif_wait);
+
+       /* Init phy db */
+       mvm->phy_db = iwl_phy_db_init(trans);
+       if (!mvm->phy_db) {
+               IWL_ERR(mvm, "Cannot init phy_db\n");
+               goto out_free;
+       }
+
+       IWL_INFO(mvm, "Detected %s, REV=0x%X\n",
+                mvm->cfg->name, mvm->trans->hw_rev);
+
+       err = iwl_trans_start_hw(mvm->trans);
+       if (err)
+               goto out_free;
+
+       mutex_lock(&mvm->mutex);
+       err = iwl_run_init_mvm_ucode(mvm, true);
+       mutex_unlock(&mvm->mutex);
+       if (err && !iwlmvm_mod_params.init_dbg) {
+               IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
+               goto out_free;
+       }
+
+       /* Stop the hw after the ALIVE and NVM has been read */
+       if (!iwlmvm_mod_params.init_dbg)
+               iwl_trans_stop_hw(mvm->trans, false);
+
+       scan_size = sizeof(struct iwl_scan_cmd) +
+               mvm->fw->ucode_capa.max_probe_length +
+               (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel));
+       mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
+       if (!mvm->scan_cmd)
+               goto out_free;
+
+       err = iwl_mvm_mac_setup_register(mvm);
+       if (err)
+               goto out_free;
+
+       err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir);
+       if (err)
+               goto out_unregister;
+
+       return op_mode;
+
+ out_unregister:
+       ieee80211_unregister_hw(mvm->hw);
+ out_free:
+       iwl_phy_db_free(mvm->phy_db);
+       kfree(mvm->scan_cmd);
+       kfree(mvm->eeprom_blob);
+       iwl_trans_stop_hw(trans, true);
+       ieee80211_free_hw(mvm->hw);
+       return NULL;
+}
+
+static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       int i;
+
+       iwl_mvm_leds_exit(mvm);
+
+       ieee80211_unregister_hw(mvm->hw);
+
+       kfree(mvm->scan_cmd);
+
+       iwl_trans_stop_hw(mvm->trans, true);
+
+       iwl_phy_db_free(mvm->phy_db);
+       mvm->phy_db = NULL;
+
+       kfree(mvm->eeprom_blob);
+       iwl_free_nvm_data(mvm->nvm_data);
+       for (i = 0; i < NVM_NUM_OF_SECTIONS; i++)
+               kfree(mvm->nvm_sections[i].data);
+
+       ieee80211_free_hw(mvm->hw);
+}
+
+struct iwl_async_handler_entry {
+       struct list_head list;
+       struct iwl_rx_cmd_buffer rxb;
+       int (*fn)(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                 struct iwl_device_cmd *cmd);
+};
+
+void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm)
+{
+       struct iwl_async_handler_entry *entry, *tmp;
+
+       spin_lock_bh(&mvm->async_handlers_lock);
+       list_for_each_entry_safe(entry, tmp, &mvm->async_handlers_list, list) {
+               iwl_free_rxb(&entry->rxb);
+               list_del(&entry->list);
+               kfree(entry);
+       }
+       spin_unlock_bh(&mvm->async_handlers_lock);
+}
+
+static void iwl_mvm_async_handlers_wk(struct work_struct *wk)
+{
+       struct iwl_mvm *mvm =
+               container_of(wk, struct iwl_mvm, async_handlers_wk);
+       struct iwl_async_handler_entry *entry, *tmp;
+       struct list_head local_list;
+
+       INIT_LIST_HEAD(&local_list);
+
+       /* Ensure that we are not in stop flow (check iwl_mvm_mac_stop) */
+       mutex_lock(&mvm->mutex);
+
+       /*
+        * Sync with Rx path with a lock. Remove all the entries from this list,
+        * add them to a local one (lock free), and then handle them.
+        */
+       spin_lock_bh(&mvm->async_handlers_lock);
+       list_splice_init(&mvm->async_handlers_list, &local_list);
+       spin_unlock_bh(&mvm->async_handlers_lock);
+
+       list_for_each_entry_safe(entry, tmp, &local_list, list) {
+               if (entry->fn(mvm, &entry->rxb, NULL))
+                       IWL_WARN(mvm,
+                                "returned value from ASYNC handlers are ignored\n");
+               iwl_free_rxb(&entry->rxb);
+               list_del(&entry->list);
+               kfree(entry);
+       }
+       mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode,
+                              struct iwl_rx_cmd_buffer *rxb,
+                              struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       u8 i;
+
+       /*
+        * Do the notification wait before RX handlers so
+        * even if the RX handler consumes the RXB we have
+        * access to it in the notification wait entry.
+        */
+       iwl_notification_wait_notify(&mvm->notif_wait, pkt);
+
+       for (i = 0; i < ARRAY_SIZE(iwl_mvm_rx_handlers); i++) {
+               const struct iwl_rx_handlers *rx_h = &iwl_mvm_rx_handlers[i];
+               if (rx_h->cmd_id == pkt->hdr.cmd) {
+                       struct iwl_async_handler_entry *entry;
+                       if (!rx_h->async)
+                               return rx_h->fn(mvm, rxb, cmd);
+
+                       entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+                       /* we can't do much... */
+                       if (!entry)
+                               return 0;
+
+                       entry->rxb._page = rxb_steal_page(rxb);
+                       entry->rxb._offset = rxb->_offset;
+                       entry->rxb._rx_page_order = rxb->_rx_page_order;
+                       entry->fn = rx_h->fn;
+                       spin_lock(&mvm->async_handlers_lock);
+                       list_add_tail(&entry->list, &mvm->async_handlers_list);
+                       spin_unlock(&mvm->async_handlers_lock);
+                       schedule_work(&mvm->async_handlers_wk);
+               }
+       }
+
+       return 0;
+}
+
+static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int queue)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       int mq = mvm->queue_to_mac80211[queue];
+
+       if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
+               return;
+
+       if (atomic_inc_return(&mvm->queue_stop_count[mq]) > 1) {
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "queue %d (mac80211 %d) already stopped\n",
+                                   queue, mq);
+               return;
+       }
+
+       set_bit(mq, &mvm->transport_queue_stop);
+       ieee80211_stop_queue(mvm->hw, mq);
+}
+
+static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       int mq = mvm->queue_to_mac80211[queue];
+
+       if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE))
+               return;
+
+       if (atomic_dec_return(&mvm->queue_stop_count[mq]) > 0) {
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "queue %d (mac80211 %d) already awake\n",
+                                   queue, mq);
+               return;
+       }
+
+       clear_bit(mq, &mvm->transport_queue_stop);
+
+       ieee80211_wake_queue(mvm->hw, mq);
+}
+
+static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       if (state)
+               set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
+       else
+               clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
+
+       wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state);
+}
+
+static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       struct ieee80211_tx_info *info;
+
+       info = IEEE80211_SKB_CB(skb);
+       iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+       ieee80211_free_txskb(mvm->hw, skb);
+}
+
+static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
+{
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+
+       iwl_mvm_dump_nic_error_log(mvm);
+
+       iwl_abort_notification_waits(&mvm->notif_wait);
+
+       /*
+        * If we're restarting already, don't cycle restarts.
+        * If INIT fw asserted, it will likely fail again.
+        * If WoWLAN fw asserted, don't restart either, mac80211
+        * can't recover this since we're already half suspended.
+        */
+       if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               IWL_ERR(mvm, "Firmware error during reconfiguration! Abort.\n");
+       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
+                  iwlwifi_mod_params.restart_fw) {
+               /*
+                * This is a bit racy, but worst case we tell mac80211 about
+                * a stopped/aborted (sched) scan when that was already done
+                * which is not a problem. It is necessary to abort any scan
+                * here because mac80211 requires having the scan cleared
+                * before restarting.
+                * We'll reset the scan_status to NONE in restart cleanup in
+                * the next start() call from mac80211.
+                */
+               switch (mvm->scan_status) {
+               case IWL_MVM_SCAN_NONE:
+                       break;
+               case IWL_MVM_SCAN_OS:
+                       ieee80211_scan_completed(mvm->hw, true);
+                       break;
+               }
+
+               ieee80211_restart_hw(mvm->hw);
+       }
+}
+
+static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
+{
+       WARN_ON(1);
+}
+
+static const struct iwl_op_mode_ops iwl_mvm_ops = {
+       .start = iwl_op_mode_mvm_start,
+       .stop = iwl_op_mode_mvm_stop,
+       .rx = iwl_mvm_rx_dispatch,
+       .queue_full = iwl_mvm_stop_sw_queue,
+       .queue_not_full = iwl_mvm_wake_sw_queue,
+       .hw_rf_kill = iwl_mvm_set_hw_rfkill_state,
+       .free_skb = iwl_mvm_free_skb,
+       .nic_error = iwl_mvm_nic_error,
+       .cmd_queue_full = iwl_mvm_cmd_queue_full,
+       .nic_config = iwl_mvm_nic_config,
+};
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
new file mode 100644 (file)
index 0000000..b428448
--- /dev/null
@@ -0,0 +1,292 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+/* Maps the driver specific channel width definition to the the fw values */
+static inline u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_20:
+               return PHY_VHT_CHANNEL_MODE20;
+       case NL80211_CHAN_WIDTH_40:
+               return PHY_VHT_CHANNEL_MODE40;
+       case NL80211_CHAN_WIDTH_80:
+               return PHY_VHT_CHANNEL_MODE80;
+       case NL80211_CHAN_WIDTH_160:
+               return PHY_VHT_CHANNEL_MODE160;
+       default:
+               WARN(1, "Invalid channel width=%u", chandef->width);
+               return PHY_VHT_CHANNEL_MODE20;
+       }
+}
+
+/*
+ * Maps the driver specific control channel position (relative to the center
+ * freq) definitions to the the fw values
+ */
+static inline u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
+{
+       switch (chandef->chan->center_freq - chandef->center_freq1) {
+       case -70:
+               return PHY_VHT_CTRL_POS_4_BELOW;
+       case -50:
+               return PHY_VHT_CTRL_POS_3_BELOW;
+       case -30:
+               return PHY_VHT_CTRL_POS_2_BELOW;
+       case -10:
+               return PHY_VHT_CTRL_POS_1_BELOW;
+       case  10:
+               return PHY_VHT_CTRL_POS_1_ABOVE;
+       case  30:
+               return PHY_VHT_CTRL_POS_2_ABOVE;
+       case  50:
+               return PHY_VHT_CTRL_POS_3_ABOVE;
+       case  70:
+               return PHY_VHT_CTRL_POS_4_ABOVE;
+       default:
+               WARN(1, "Invalid channel definition");
+       case 0:
+               /*
+                * The FW is expected to check the control channel position only
+                * when in HT/VHT and the channel width is not 20MHz. Return
+                * this value as the default one.
+                */
+               return PHY_VHT_CTRL_POS_1_BELOW;
+       }
+}
+
+/*
+ * Construct the generic fields of the PHY context command
+ */
+static void iwl_mvm_phy_ctxt_cmd_hdr(struct iwl_mvm_phy_ctxt *ctxt,
+                                    struct iwl_phy_context_cmd *cmd,
+                                    u32 action, u32 apply_time)
+{
+       memset(cmd, 0, sizeof(struct iwl_phy_context_cmd));
+
+       cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(ctxt->id,
+                                                           ctxt->color));
+       cmd->action = cpu_to_le32(action);
+       cmd->apply_time = cpu_to_le32(apply_time);
+}
+
+/*
+ * Add the phy configuration to the PHY context command
+ */
+static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
+                                     struct iwl_phy_context_cmd *cmd,
+                                     struct cfg80211_chan_def *chandef,
+                                     u8 chains_static, u8 chains_dynamic)
+{
+       u8 valid_rx_chains, active_cnt, idle_cnt;
+
+       /* Set the channel info data */
+       cmd->ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
+             PHY_BAND_24 : PHY_BAND_5);
+
+       cmd->ci.channel = chandef->chan->hw_value;
+       cmd->ci.width = iwl_mvm_get_channel_width(chandef);
+       cmd->ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
+
+       /* Set rx the chains */
+
+       /* TODO:
+        * Need to add on chain noise calibration limitations, and
+        * BT coex considerations.
+        */
+       valid_rx_chains = mvm->nvm_data->valid_rx_ant;
+       idle_cnt = chains_static;
+       active_cnt = chains_dynamic;
+
+       cmd->rxchain_info = cpu_to_le32(valid_rx_chains <<
+                                       PHY_RX_CHAIN_VALID_POS);
+       cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
+       cmd->rxchain_info |= cpu_to_le32(active_cnt <<
+                                        PHY_RX_CHAIN_MIMO_CNT_POS);
+
+       cmd->txchain_info = cpu_to_le32(mvm->nvm_data->valid_tx_ant);
+}
+
+/*
+ * Send a command to apply the current phy configuration. The command is send
+ * only if something in the configuration changed: in case that this is the
+ * first time that the phy configuration is applied or in case that the phy
+ * configuration changed from the previous apply.
+ */
+static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
+                                 struct iwl_mvm_phy_ctxt *ctxt,
+                                 struct cfg80211_chan_def *chandef,
+                                 u8 chains_static, u8 chains_dynamic,
+                                 u32 action, u32 apply_time)
+{
+       struct iwl_phy_context_cmd cmd;
+       int ret;
+
+       /* Set the command header fields */
+       iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, action, apply_time);
+
+       /* Set the command data */
+       iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef,
+                                 chains_static, chains_dynamic);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
+                                  sizeof(struct iwl_phy_context_cmd),
+                                  &cmd);
+       if (ret)
+               IWL_ERR(mvm, "PHY ctxt cmd error. ret=%d\n", ret);
+       return ret;
+}
+
+
+struct phy_ctx_used_data {
+       unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)];
+};
+
+static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw,
+                                     struct ieee80211_chanctx_conf *ctx,
+                                     void *_data)
+{
+       struct phy_ctx_used_data *data = _data;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv;
+
+       __set_bit(phy_ctxt->id, data->used);
+}
+
+/*
+ * Send a command to add a PHY context based on the current HW configuration.
+ */
+int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+                        struct cfg80211_chan_def *chandef,
+                        u8 chains_static, u8 chains_dynamic)
+{
+       struct phy_ctx_used_data data = {
+               .used = { },
+       };
+
+       /*
+        * If this is a regular PHY context (not the ROC one)
+        * skip the ROC PHY context's ID.
+        */
+       if (ctxt != &mvm->phy_ctxt_roc)
+               __set_bit(mvm->phy_ctxt_roc.id, data.used);
+
+       lockdep_assert_held(&mvm->mutex);
+       ctxt->color++;
+
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               ieee80211_iter_chan_contexts_atomic(
+                       mvm->hw, iwl_mvm_phy_ctx_used_iter, &data);
+
+               ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX);
+               if (WARN_ONCE(ctxt->id == NUM_PHY_CTX,
+                             "Failed to init PHY context - no free ID!\n"))
+                       return -EIO;
+       }
+
+       ctxt->channel = chandef->chan;
+       return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+                                     chains_static, chains_dynamic,
+                                     FW_CTXT_ACTION_ADD, 0);
+}
+
+/*
+ * Send a command to modify the PHY context based on the current HW
+ * configuration. Note that the function does not check that the configuration
+ * changed.
+ */
+int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
+                            struct cfg80211_chan_def *chandef,
+                            u8 chains_static, u8 chains_dynamic)
+{
+       lockdep_assert_held(&mvm->mutex);
+
+       ctxt->channel = chandef->chan;
+       return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
+                                     chains_static, chains_dynamic,
+                                     FW_CTXT_ACTION_MODIFY, 0);
+}
+
+/*
+ * Send a command to the FW to remove the given phy context.
+ * Once the command is sent, regardless of success or failure, the context is
+ * marked as invalid
+ */
+void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
+{
+       struct iwl_phy_context_cmd cmd;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, FW_CTXT_ACTION_REMOVE, 0);
+       ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC,
+                                  sizeof(struct iwl_phy_context_cmd),
+                                  &cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n",
+                       ctxt->id);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
new file mode 100644 (file)
index 0000000..6362873
--- /dev/null
@@ -0,0 +1,207 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "mvm.h"
+#include "iwl-modparams.h"
+#include "fw-api-power.h"
+
+#define POWER_KEEP_ALIVE_PERIOD_SEC    25
+
+static void iwl_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               struct iwl_powertable_cmd *cmd)
+{
+       struct ieee80211_hw *hw = mvm->hw;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *chan;
+       int dtimper, dtimper_msec;
+       int keep_alive;
+       bool radar_detect = false;
+
+       cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                           mvmvif->color));
+       cmd->action = cpu_to_le32(FW_CTXT_ACTION_MODIFY);
+
+       if ((!vif->bss_conf.ps) ||
+           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM))
+               return;
+
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+
+       dtimper = hw->conf.ps_dtim_period ?: 1;
+
+       /* Check if radar detection is required on current channel */
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       WARN_ON(!chanctx_conf);
+       if (chanctx_conf) {
+               chan = chanctx_conf->def.chan;
+               radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
+       }
+       rcu_read_unlock();
+
+       /* Check skip over DTIM conditions */
+       if (!radar_detect && (dtimper <= 10) &&
+           (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP)) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_SLEEP_OVER_DTIM_MSK);
+               cmd->num_skip_dtim = 2;
+       }
+
+       /* Check that keep alive period is at least 3 * DTIM */
+       dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+       keep_alive = max_t(int, 3 * dtimper_msec,
+                          MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
+       keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+
+       cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
+
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP) {
+               /* TODO: Also for D3 (device sleep / WoWLAN) */
+               cmd->rx_data_timeout = cpu_to_le32(10);
+               cmd->tx_data_timeout = cpu_to_le32(10);
+       } else {
+               cmd->rx_data_timeout = cpu_to_le32(50);
+               cmd->tx_data_timeout = cpu_to_le32(50);
+       }
+}
+
+int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_powertable_cmd cmd = {};
+
+       if (!iwlwifi_mod_params.power_save) {
+               IWL_DEBUG_POWER(mvm, "Power management is not allowed\n");
+               return 0;
+       }
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       iwl_power_build_cmd(mvm, vif, &cmd);
+
+       IWL_DEBUG_POWER(mvm,
+                       "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
+                       cmd.id_and_color, iwlmvm_mod_params.power_scheme,
+                       le16_to_cpu(cmd.flags));
+
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
+               IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
+                               le16_to_cpu(cmd.keep_alive_seconds));
+               IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
+                               le32_to_cpu(cmd.rx_data_timeout));
+               IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+                               le32_to_cpu(cmd.tx_data_timeout));
+               IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",
+                               le32_to_cpu(cmd.rx_data_timeout_uapsd));
+               IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
+                               le32_to_cpu(cmd.tx_data_timeout_uapsd));
+               IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
+                               cmd.lprx_rssi_threshold);
+               IWL_DEBUG_POWER(mvm, "DTIMs to skip = %u\n", cmd.num_skip_dtim);
+       }
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_powertable_cmd cmd = {};
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (!iwlwifi_mod_params.power_save) {
+               IWL_DEBUG_POWER(mvm, "Power management is not allowed\n");
+               return 0;
+       }
+
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+               return 0;
+
+       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                           mvmvif->color));
+       cmd.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY);
+
+       IWL_DEBUG_POWER(mvm,
+                       "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
+                       cmd.id_and_color, iwlmvm_mod_params.power_scheme,
+                       le16_to_cpu(cmd.flags));
+
+       return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+                                   sizeof(cmd), &cmd);
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+void iwl_power_get_params(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         struct iwl_powertable_cmd *cmd)
+{
+       iwl_power_build_cmd(mvm, vif, cmd);
+}
+#endif /* CONFIG_IWLWIFI_DEBUGFS */
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c
new file mode 100644 (file)
index 0000000..2d4611a
--- /dev/null
@@ -0,0 +1,178 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/mac80211.h>
+#include "fw-api.h"
+#include "mvm.h"
+
+struct iwl_mvm_quota_iterator_data {
+       int n_interfaces[MAX_BINDINGS];
+       int colors[MAX_BINDINGS];
+       struct ieee80211_vif *new_vif;
+};
+
+static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
+                                  struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_quota_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u16 id;
+
+       /*
+        * We'll account for the new interface (if any) below,
+        * skip it here in case we're not called from within
+        * the add_interface callback (otherwise it won't show
+        * up in iteration)
+        */
+       if (vif == data->new_vif)
+               return;
+
+       if (!mvmvif->phy_ctxt)
+               return;
+
+       /* currently, PHY ID == binding ID */
+       id = mvmvif->phy_ctxt->id;
+
+       /* need at least one binding per PHY */
+       BUILD_BUG_ON(NUM_PHY_CTX > MAX_BINDINGS);
+
+       if (WARN_ON_ONCE(id >= MAX_BINDINGS))
+               return;
+
+       if (data->colors[id] < 0)
+               data->colors[id] = mvmvif->phy_ctxt->color;
+       else
+               WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               if (vif->bss_conf.assoc)
+                       data->n_interfaces[id]++;
+               break;
+       case NL80211_IFTYPE_AP:
+               if (mvmvif->ap_active)
+                       data->n_interfaces[id]++;
+               break;
+       case NL80211_IFTYPE_MONITOR:
+               data->n_interfaces[id]++;
+               break;
+       case NL80211_IFTYPE_P2P_DEVICE:
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (vif->bss_conf.ibss_joined)
+                       data->n_interfaces[id]++;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               break;
+       }
+}
+
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
+{
+       struct iwl_time_quota_cmd cmd;
+       int i, idx, ret;
+       struct iwl_mvm_quota_iterator_data data = {
+               .n_interfaces = {},
+               .colors = { -1, -1, -1, -1 },
+               .new_vif = newvif,
+       };
+
+       /* update all upon completion */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               return 0;
+
+       BUILD_BUG_ON(data.colors[MAX_BINDINGS - 1] != -1);
+
+       lockdep_assert_held(&mvm->mutex);
+
+       memset(&cmd, 0, sizeof(cmd));
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_quota_iterator, &data);
+       if (newvif) {
+               data.new_vif = NULL;
+               iwl_mvm_quota_iterator(&data, newvif->addr, newvif);
+       }
+
+       for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
+               if (data.n_interfaces[i] <= 0)
+                       continue;
+
+               cmd.quotas[idx].id_and_color =
+                       cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i]));
+               cmd.quotas[idx].quota = cpu_to_le32(100);
+               cmd.quotas[idx].max_duration = cpu_to_le32(1000);
+               idx++;
+       }
+
+       for (i = idx; i < MAX_BINDINGS; i++)
+               cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC,
+                                  sizeof(cmd), &cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send quota: %d\n", ret);
+       return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
new file mode 100644 (file)
index 0000000..60a4291
--- /dev/null
@@ -0,0 +1,3096 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <net/mac80211.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+#include <linux/workqueue.h>
+#include "rs.h"
+#include "fw-api.h"
+#include "sta.h"
+#include "iwl-op-mode.h"
+#include "mvm.h"
+
+#define RS_NAME "iwl-mvm-rs"
+
+#define NUM_TRY_BEFORE_ANT_TOGGLE 1
+#define IWL_NUMBER_TRY      1
+#define IWL_HT_NUMBER_TRY   3
+
+#define IWL_RATE_MAX_WINDOW            62      /* # tx in history window */
+#define IWL_RATE_MIN_FAILURE_TH                6       /* min failures to calc tpt */
+#define IWL_RATE_MIN_SUCCESS_TH                8       /* min successes to calc tpt */
+
+/* max allowed rate miss before sync LQ cmd */
+#define IWL_MISSED_RATE_MAX            15
+/* max time to accum history 2 seconds */
+#define IWL_RATE_SCALE_FLUSH_INTVL   (3*HZ)
+
+static u8 rs_ht_to_legacy[] = {
+       IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
+       IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
+       IWL_RATE_6M_INDEX,
+       IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX,
+       IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX,
+       IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX,
+       IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX
+};
+
+static const u8 ant_toggle_lookup[] = {
+       /*ANT_NONE -> */ ANT_NONE,
+       /*ANT_A    -> */ ANT_B,
+       /*ANT_B    -> */ ANT_C,
+       /*ANT_AB   -> */ ANT_BC,
+       /*ANT_C    -> */ ANT_A,
+       /*ANT_AC   -> */ ANT_AB,
+       /*ANT_BC   -> */ ANT_AC,
+       /*ANT_ABC  -> */ ANT_ABC,
+};
+
+#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np)    \
+       [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP,      \
+                                   IWL_RATE_SISO_##s##M_PLCP, \
+                                   IWL_RATE_MIMO2_##s##M_PLCP,\
+                                   IWL_RATE_MIMO3_##s##M_PLCP,\
+                                   IWL_RATE_##r##M_IEEE,      \
+                                   IWL_RATE_##ip##M_INDEX,    \
+                                   IWL_RATE_##in##M_INDEX,    \
+                                   IWL_RATE_##rp##M_INDEX,    \
+                                   IWL_RATE_##rn##M_INDEX,    \
+                                   IWL_RATE_##pp##M_INDEX,    \
+                                   IWL_RATE_##np##M_INDEX }
+
+/*
+ * Parameter order:
+ *   rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
+ *
+ * If there isn't a valid next or previous rate then INV is used which
+ * maps to IWL_RATE_INVALID
+ *
+ */
+static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
+       IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2),    /*  1mbps */
+       IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5),          /*  2mbps */
+       IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11),        /*5.5mbps */
+       IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18),      /* 11mbps */
+       IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11),        /*  6mbps */
+       IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11),       /*  9mbps */
+       IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18),   /* 12mbps */
+       IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24),   /* 18mbps */
+       IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36),   /* 24mbps */
+       IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48),   /* 36mbps */
+       IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54),   /* 48mbps */
+       IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */
+       IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
+       /* FIXME:RS:          ^^    should be INV (legacy) */
+};
+
+static inline u8 rs_extract_rate(u32 rate_n_flags)
+{
+       /* also works for HT because bits 7:6 are zero there */
+       return (u8)(rate_n_flags & RATE_LEGACY_RATE_MSK);
+}
+
+static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags)
+{
+       int idx = 0;
+
+       /* HT rate format */
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               idx = rs_extract_rate(rate_n_flags);
+
+               if (idx >= IWL_RATE_MIMO3_6M_PLCP)
+                       idx = idx - IWL_RATE_MIMO3_6M_PLCP;
+               else if (idx >= IWL_RATE_MIMO2_6M_PLCP)
+                       idx = idx - IWL_RATE_MIMO2_6M_PLCP;
+
+               idx += IWL_FIRST_OFDM_RATE;
+               /* skip 9M not supported in ht*/
+               if (idx >= IWL_RATE_9M_INDEX)
+                       idx += 1;
+               if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE))
+                       return idx;
+
+       /* legacy rate format, search for match in table */
+       } else {
+               for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++)
+                       if (iwl_rates[idx].plcp ==
+                                       rs_extract_rate(rate_n_flags))
+                               return idx;
+       }
+
+       return -1;
+}
+
+static void rs_rate_scale_perform(struct iwl_mvm *mvm,
+                                  struct sk_buff *skb,
+                                  struct ieee80211_sta *sta,
+                                  struct iwl_lq_sta *lq_sta);
+static void rs_fill_link_cmd(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
+static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
+
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+                            u32 *rate_n_flags, int index);
+#else
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+                            u32 *rate_n_flags, int index)
+{}
+#endif
+
+/**
+ * The following tables contain the expected throughput metrics for all rates
+ *
+ *     1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits
+ *
+ * where invalid entries are zeros.
+ *
+ * CCK rates are only valid in legacy table and will only be used in G
+ * (2.4 GHz) band.
+ */
+
+static s32 expected_tpt_legacy[IWL_RATE_COUNT] = {
+       7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0
+};
+
+static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 42, 0,  76, 102, 124, 159, 183, 193, 202}, /* Norm */
+       {0, 0, 0, 0, 46, 0,  82, 110, 132, 168, 192, 202, 210}, /* SGI */
+       {0, 0, 0, 0, 47, 0,  91, 133, 171, 242, 305, 334, 362}, /* AGG */
+       {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */
+};
+
+static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0,  77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */
+       {0, 0, 0, 0,  83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */
+       {0, 0, 0, 0,  94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */
+       {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */
+};
+
+static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0,  74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */
+       {0, 0, 0, 0,  81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */
+       {0, 0, 0, 0,  89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */
+       {0, 0, 0, 0,  97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/
+};
+
+static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */
+       {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */
+       {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */
+       {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */
+};
+
+static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0,  99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */
+       {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */
+       {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */
+       {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */
+};
+
+static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = {
+       {0, 0, 0, 0, 152, 0, 211, 239, 255, 279,  290,  294,  297}, /* Norm */
+       {0, 0, 0, 0, 160, 0, 219, 245, 261, 284,  294,  297,  300}, /* SGI */
+       {0, 0, 0, 0, 254, 0, 443, 584, 695, 868,  984, 1030, 1070}, /* AGG */
+       {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */
+};
+
+/* mbps, mcs */
+static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
+       {  "1", "BPSK DSSS"},
+       {  "2", "QPSK DSSS"},
+       {"5.5", "BPSK CCK"},
+       { "11", "QPSK CCK"},
+       {  "6", "BPSK 1/2"},
+       {  "9", "BPSK 1/2"},
+       { "12", "QPSK 1/2"},
+       { "18", "QPSK 3/4"},
+       { "24", "16QAM 1/2"},
+       { "36", "16QAM 3/4"},
+       { "48", "64QAM 2/3"},
+       { "54", "64QAM 3/4"},
+       { "60", "64QAM 5/6"},
+};
+
+#define MCS_INDEX_PER_STREAM   (8)
+
+static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
+{
+       window->data = 0;
+       window->success_counter = 0;
+       window->success_ratio = IWL_INVALID_VALUE;
+       window->counter = 0;
+       window->average_tpt = IWL_INVALID_VALUE;
+       window->stamp = 0;
+}
+
+static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
+{
+       return (ant_type & valid_antenna) == ant_type;
+}
+
+/*
+ *     removes the old data from the statistics. All data that is older than
+ *     TID_MAX_TIME_DIFF, will be deleted.
+ */
+static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time)
+{
+       /* The oldest age we want to keep */
+       u32 oldest_time = curr_time - TID_MAX_TIME_DIFF;
+
+       while (tl->queue_count &&
+              (tl->time_stamp < oldest_time)) {
+               tl->total -= tl->packet_count[tl->head];
+               tl->packet_count[tl->head] = 0;
+               tl->time_stamp += TID_QUEUE_CELL_SPACING;
+               tl->queue_count--;
+               tl->head++;
+               if (tl->head >= TID_QUEUE_MAX_SIZE)
+                       tl->head = 0;
+       }
+}
+
+/*
+ *     increment traffic load value for tid and also remove
+ *     any old values if passed the certain time period
+ */
+static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
+                          struct ieee80211_hdr *hdr)
+{
+       u32 curr_time = jiffies_to_msecs(jiffies);
+       u32 time_diff;
+       s32 index;
+       struct iwl_traffic_load *tl = NULL;
+       u8 tid;
+
+       if (ieee80211_is_data_qos(hdr->frame_control)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               tid = qc[0] & 0xf;
+       } else {
+               return IWL_MAX_TID_COUNT;
+       }
+
+       if (unlikely(tid >= IWL_MAX_TID_COUNT))
+               return IWL_MAX_TID_COUNT;
+
+       tl = &lq_data->load[tid];
+
+       curr_time -= curr_time % TID_ROUND_VALUE;
+
+       /* Happens only for the first packet. Initialize the data */
+       if (!(tl->queue_count)) {
+               tl->total = 1;
+               tl->time_stamp = curr_time;
+               tl->queue_count = 1;
+               tl->head = 0;
+               tl->packet_count[0] = 1;
+               return IWL_MAX_TID_COUNT;
+       }
+
+       time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
+       index = time_diff / TID_QUEUE_CELL_SPACING;
+
+       /* The history is too long: remove data that is older than */
+       /* TID_MAX_TIME_DIFF */
+       if (index >= TID_QUEUE_MAX_SIZE)
+               rs_tl_rm_old_stats(tl, curr_time);
+
+       index = (tl->head + index) % TID_QUEUE_MAX_SIZE;
+       tl->packet_count[index] = tl->packet_count[index] + 1;
+       tl->total = tl->total + 1;
+
+       if ((index + 1) > tl->queue_count)
+               tl->queue_count = index + 1;
+
+       return tid;
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+/**
+ * Program the device to use fixed rate for frame transmit
+ * This is for debugging/testing only
+ * once the device start use fixed rate, we need to reload the module
+ * to being back the normal operation.
+ */
+static void rs_program_fix_rate(struct iwl_mvm *mvm,
+                               struct iwl_lq_sta *lq_sta)
+{
+       lq_sta->active_legacy_rate = 0x0FFF;    /* 1 - 54 MBits, includes CCK */
+       lq_sta->active_siso_rate   = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+       lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+       lq_sta->active_mimo3_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+
+       IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
+                      lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
+
+       if (lq_sta->dbg_fixed_rate) {
+               rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
+               iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false);
+       }
+}
+#endif
+
+/*
+       get the traffic load value for tid
+*/
+static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid)
+{
+       u32 curr_time = jiffies_to_msecs(jiffies);
+       u32 time_diff;
+       s32 index;
+       struct iwl_traffic_load *tl = NULL;
+
+       if (tid >= IWL_MAX_TID_COUNT)
+               return 0;
+
+       tl = &(lq_data->load[tid]);
+
+       curr_time -= curr_time % TID_ROUND_VALUE;
+
+       if (!(tl->queue_count))
+               return 0;
+
+       time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
+       index = time_diff / TID_QUEUE_CELL_SPACING;
+
+       /* The history is too long: remove data that is older than */
+       /* TID_MAX_TIME_DIFF */
+       if (index >= TID_QUEUE_MAX_SIZE)
+               rs_tl_rm_old_stats(tl, curr_time);
+
+       return tl->total;
+}
+
+static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
+                                     struct iwl_lq_sta *lq_data, u8 tid,
+                                     struct ieee80211_sta *sta)
+{
+       int ret = -EAGAIN;
+       u32 load;
+
+       load = rs_tl_get_load(lq_data, tid);
+
+       if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) {
+               IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n",
+                            sta->addr, tid);
+               ret = ieee80211_start_tx_ba_session(sta, tid, 5000);
+               if (ret == -EAGAIN) {
+                       /*
+                        * driver and mac80211 is out of sync
+                        * this might be cause by reloading firmware
+                        * stop the tx ba session here
+                        */
+                       IWL_ERR(mvm, "Fail start Tx agg on tid: %d\n",
+                               tid);
+                       ieee80211_stop_tx_ba_session(sta, tid);
+               }
+       } else {
+               IWL_DEBUG_HT(mvm,
+                            "Aggregation not enabled for tid %d because load = %u\n",
+                            tid, load);
+       }
+       return ret;
+}
+
+static void rs_tl_turn_on_agg(struct iwl_mvm *mvm, u8 tid,
+                             struct iwl_lq_sta *lq_data,
+                             struct ieee80211_sta *sta)
+{
+       if (tid < IWL_MAX_TID_COUNT)
+               rs_tl_turn_on_agg_for_tid(mvm, lq_data, tid, sta);
+       else
+               IWL_ERR(mvm, "tid exceeds max TID count: %d/%d\n",
+                       tid, IWL_MAX_TID_COUNT);
+}
+
+static inline int get_num_of_ant_from_rate(u32 rate_n_flags)
+{
+       return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) +
+              !!(rate_n_flags & RATE_MCS_ANT_B_MSK) +
+              !!(rate_n_flags & RATE_MCS_ANT_C_MSK);
+}
+
+/*
+ * Static function to get the expected throughput from an iwl_scale_tbl_info
+ * that wraps a NULL pointer check
+ */
+static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index)
+{
+       if (tbl->expected_tpt)
+               return tbl->expected_tpt[rs_index];
+       return 0;
+}
+
+/**
+ * rs_collect_tx_data - Update the success/failure sliding window
+ *
+ * We keep a sliding window of the last 62 packets transmitted
+ * at this rate.  window->data contains the bitmask of successful
+ * packets.
+ */
+static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
+                             int scale_index, int attempts, int successes)
+{
+       struct iwl_rate_scale_data *window = NULL;
+       static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1));
+       s32 fail_count, tpt;
+
+       if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
+               return -EINVAL;
+
+       /* Select window for current tx bit rate */
+       window = &(tbl->win[scale_index]);
+
+       /* Get expected throughput */
+       tpt = get_expected_tpt(tbl, scale_index);
+
+       /*
+        * Keep track of only the latest 62 tx frame attempts in this rate's
+        * history window; anything older isn't really relevant any more.
+        * If we have filled up the sliding window, drop the oldest attempt;
+        * if the oldest attempt (highest bit in bitmap) shows "success",
+        * subtract "1" from the success counter (this is the main reason
+        * we keep these bitmaps!).
+        */
+       while (attempts > 0) {
+               if (window->counter >= IWL_RATE_MAX_WINDOW) {
+                       /* remove earliest */
+                       window->counter = IWL_RATE_MAX_WINDOW - 1;
+
+                       if (window->data & mask) {
+                               window->data &= ~mask;
+                               window->success_counter--;
+                       }
+               }
+
+               /* Increment frames-attempted counter */
+               window->counter++;
+
+               /* Shift bitmap by one frame to throw away oldest history */
+               window->data <<= 1;
+
+               /* Mark the most recent #successes attempts as successful */
+               if (successes > 0) {
+                       window->success_counter++;
+                       window->data |= 0x1;
+                       successes--;
+               }
+
+               attempts--;
+       }
+
+       /* Calculate current success ratio, avoid divide-by-0! */
+       if (window->counter > 0)
+               window->success_ratio = 128 * (100 * window->success_counter)
+                                       / window->counter;
+       else
+               window->success_ratio = IWL_INVALID_VALUE;
+
+       fail_count = window->counter - window->success_counter;
+
+       /* Calculate average throughput, if we have enough history. */
+       if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) ||
+           (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH))
+               window->average_tpt = (window->success_ratio * tpt + 64) / 128;
+       else
+               window->average_tpt = IWL_INVALID_VALUE;
+
+       /* Tag this window as having been updated */
+       window->stamp = jiffies;
+
+       return 0;
+}
+
+/*
+ * Fill uCode API rate_n_flags field, based on "search" or "active" table.
+ */
+/* FIXME:RS:remove this function and put the flags statically in the table */
+static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm,
+                                struct iwl_scale_tbl_info *tbl,
+                                int index, u8 use_green)
+{
+       u32 rate_n_flags = 0;
+
+       if (is_legacy(tbl->lq_type)) {
+               rate_n_flags = iwl_rates[index].plcp;
+               if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE)
+                       rate_n_flags |= RATE_MCS_CCK_MSK;
+       } else if (is_Ht(tbl->lq_type)) {
+               if (index > IWL_LAST_OFDM_RATE) {
+                       IWL_ERR(mvm, "Invalid HT rate index %d\n", index);
+                       index = IWL_LAST_OFDM_RATE;
+               }
+               rate_n_flags = RATE_MCS_HT_MSK;
+
+               if (is_siso(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_siso;
+               else if (is_mimo2(tbl->lq_type))
+                       rate_n_flags |= iwl_rates[index].plcp_mimo2;
+               else
+                       rate_n_flags |= iwl_rates[index].plcp_mimo3;
+       } else {
+               IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type);
+       }
+
+       rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) &
+                                                    RATE_MCS_ANT_ABC_MSK);
+
+       if (is_Ht(tbl->lq_type)) {
+               if (tbl->is_ht40)
+                       rate_n_flags |= RATE_MCS_CHAN_WIDTH_40;
+               if (tbl->is_SGI)
+                       rate_n_flags |= RATE_MCS_SGI_MSK;
+
+               if (use_green) {
+                       rate_n_flags |= RATE_HT_MCS_GF_MSK;
+                       if (is_siso(tbl->lq_type) && tbl->is_SGI) {
+                               rate_n_flags &= ~RATE_MCS_SGI_MSK;
+                               IWL_ERR(mvm, "GF was set with SGI:SISO\n");
+                       }
+               }
+       }
+       return rate_n_flags;
+}
+
+/*
+ * Interpret uCode API's rate_n_flags format,
+ * fill "search" or "active" tx mode table.
+ */
+static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags,
+                                   enum ieee80211_band band,
+                                   struct iwl_scale_tbl_info *tbl,
+                                   int *rate_idx)
+{
+       u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK);
+       u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
+       u8 mcs;
+
+       memset(tbl, 0, sizeof(struct iwl_scale_tbl_info));
+       *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
+
+       if (*rate_idx  == IWL_RATE_INVALID) {
+               *rate_idx = -1;
+               return -EINVAL;
+       }
+       tbl->is_SGI = 0;        /* default legacy setup */
+       tbl->is_ht40 = 0;
+       tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS);
+       tbl->lq_type = LQ_NONE;
+       tbl->max_search = IWL_MAX_SEARCH;
+
+       /* legacy rate format */
+       if (!(rate_n_flags & RATE_MCS_HT_MSK)) {
+               if (num_of_ant == 1) {
+                       if (band == IEEE80211_BAND_5GHZ)
+                               tbl->lq_type = LQ_A;
+                       else
+                               tbl->lq_type = LQ_G;
+               }
+       /* HT rate format */
+       } else {
+               if (rate_n_flags & RATE_MCS_SGI_MSK)
+                       tbl->is_SGI = 1;
+
+               if (rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */
+                       tbl->is_ht40 = 1;
+
+               mcs = rs_extract_rate(rate_n_flags);
+
+               /* SISO */
+               if (mcs <= IWL_RATE_SISO_60M_PLCP) {
+                       if (num_of_ant == 1)
+                               tbl->lq_type = LQ_SISO; /*else NONE*/
+               /* MIMO2 */
+               } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) {
+                       if (num_of_ant == 2)
+                               tbl->lq_type = LQ_MIMO2;
+               /* MIMO3 */
+               } else {
+                       if (num_of_ant == 3) {
+                               tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
+                               tbl->lq_type = LQ_MIMO3;
+                       }
+               }
+       }
+       return 0;
+}
+
+/* switch to another antenna/antennas and return 1 */
+/* if no other valid antenna found, return 0 */
+static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
+                            struct iwl_scale_tbl_info *tbl)
+{
+       u8 new_ant_type;
+
+       if (!tbl->ant_type || tbl->ant_type > ANT_ABC)
+               return 0;
+
+       if (!rs_is_valid_ant(valid_ant, tbl->ant_type))
+               return 0;
+
+       new_ant_type = ant_toggle_lookup[tbl->ant_type];
+
+       while ((new_ant_type != tbl->ant_type) &&
+              !rs_is_valid_ant(valid_ant, new_ant_type))
+               new_ant_type = ant_toggle_lookup[new_ant_type];
+
+       if (new_ant_type == tbl->ant_type)
+               return 0;
+
+       tbl->ant_type = new_ant_type;
+       *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK;
+       *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS;
+       return 1;
+}
+
+/**
+ * Green-field mode is valid if the station supports it and
+ * there are no non-GF stations present in the BSS.
+ */
+static bool rs_use_green(struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv;
+
+       bool use_green = !(sta_priv->vif->bss_conf.ht_operation_mode &
+                               IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+
+       return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && use_green;
+}
+
+/**
+ * rs_get_supported_rates - get the available rates
+ *
+ * if management frame or broadcast frame only return
+ * basic available rates.
+ *
+ */
+static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta,
+                                 struct ieee80211_hdr *hdr,
+                                 enum iwl_table_type rate_type)
+{
+       if (is_legacy(rate_type)) {
+               return lq_sta->active_legacy_rate;
+       } else {
+               if (is_siso(rate_type))
+                       return lq_sta->active_siso_rate;
+               else if (is_mimo2(rate_type))
+                       return lq_sta->active_mimo2_rate;
+               else
+                       return lq_sta->active_mimo3_rate;
+       }
+}
+
+static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
+                               int rate_type)
+{
+       u8 high = IWL_RATE_INVALID;
+       u8 low = IWL_RATE_INVALID;
+
+       /* 802.11A or ht walks to the next literal adjacent rate in
+        * the rate table */
+       if (is_a_band(rate_type) || !is_legacy(rate_type)) {
+               int i;
+               u32 mask;
+
+               /* Find the previous rate that is in the rate mask */
+               i = index - 1;
+               for (mask = (1 << i); i >= 0; i--, mask >>= 1) {
+                       if (rate_mask & mask) {
+                               low = i;
+                               break;
+                       }
+               }
+
+               /* Find the next rate that is in the rate mask */
+               i = index + 1;
+               for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) {
+                       if (rate_mask & mask) {
+                               high = i;
+                               break;
+                       }
+               }
+
+               return (high << 8) | low;
+       }
+
+       low = index;
+       while (low != IWL_RATE_INVALID) {
+               low = iwl_rates[low].prev_rs;
+               if (low == IWL_RATE_INVALID)
+                       break;
+               if (rate_mask & (1 << low))
+                       break;
+               IWL_DEBUG_RATE(mvm, "Skipping masked lower rate: %d\n", low);
+       }
+
+       high = index;
+       while (high != IWL_RATE_INVALID) {
+               high = iwl_rates[high].next_rs;
+               if (high == IWL_RATE_INVALID)
+                       break;
+               if (rate_mask & (1 << high))
+                       break;
+               IWL_DEBUG_RATE(mvm, "Skipping masked higher rate: %d\n", high);
+       }
+
+       return (high << 8) | low;
+}
+
+static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
+                            struct iwl_scale_tbl_info *tbl,
+                            u8 scale_index, u8 ht_possible)
+{
+       s32 low;
+       u16 rate_mask;
+       u16 high_low;
+       u8 switch_to_legacy = 0;
+       u8 is_green = lq_sta->is_green;
+       struct iwl_mvm *mvm = lq_sta->drv;
+
+       /* check if we need to switch from HT to legacy rates.
+        * assumption is that mandatory rates (1Mbps or 6Mbps)
+        * are always supported (spec demand) */
+       if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) {
+               switch_to_legacy = 1;
+               scale_index = rs_ht_to_legacy[scale_index];
+               if (lq_sta->band == IEEE80211_BAND_5GHZ)
+                       tbl->lq_type = LQ_A;
+               else
+                       tbl->lq_type = LQ_G;
+
+               if (num_of_ant(tbl->ant_type) > 1)
+                       tbl->ant_type =
+                           first_antenna(mvm->nvm_data->valid_tx_ant);
+
+               tbl->is_ht40 = 0;
+               tbl->is_SGI = 0;
+               tbl->max_search = IWL_MAX_SEARCH;
+       }
+
+       rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type);
+
+       /* Mask with station rate restriction */
+       if (is_legacy(tbl->lq_type)) {
+               /* supp_rates has no CCK bits in A mode */
+               if (lq_sta->band == IEEE80211_BAND_5GHZ)
+                       rate_mask  = (u16)(rate_mask &
+                          (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
+               else
+                       rate_mask = (u16)(rate_mask & lq_sta->supp_rates);
+       }
+
+       /* If we switched from HT to legacy, check current rate */
+       if (switch_to_legacy && (rate_mask & (1 << scale_index))) {
+               low = scale_index;
+               goto out;
+       }
+
+       high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask,
+                                       tbl->lq_type);
+       low = high_low & 0xff;
+
+       if (low == IWL_RATE_INVALID)
+               low = scale_index;
+
+out:
+       return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green);
+}
+
+/*
+ * Simple function to compare two rate scale table types
+ */
+static bool table_type_matches(struct iwl_scale_tbl_info *a,
+                              struct iwl_scale_tbl_info *b)
+{
+       return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) &&
+               (a->is_SGI == b->is_SGI);
+}
+
+/*
+ * mac80211 sends us Tx status
+ */
+static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
+                        struct ieee80211_sta *sta, void *priv_sta,
+                        struct sk_buff *skb)
+{
+       int legacy_success;
+       int retries;
+       int rs_index, mac_index, i;
+       struct iwl_lq_sta *lq_sta = priv_sta;
+       struct iwl_lq_cmd *table;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_r;
+       struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       enum mac80211_rate_control_flags mac_flags;
+       u32 tx_rate;
+       struct iwl_scale_tbl_info tbl_type;
+       struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
+
+       IWL_DEBUG_RATE_LIMIT(mvm,
+                            "get frame ack response, update rate scale window\n");
+
+       /* Treat uninitialized rate scaling data same as non-existing. */
+       if (!lq_sta) {
+               IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
+               return;
+       } else if (!lq_sta->drv) {
+               IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
+               return;
+       }
+
+       if (!ieee80211_is_data(hdr->frame_control) ||
+           info->flags & IEEE80211_TX_CTL_NO_ACK)
+               return;
+
+       /* This packet was aggregated but doesn't carry status info */
+       if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
+           !(info->flags & IEEE80211_TX_STAT_AMPDU))
+               return;
+
+       /*
+        * Ignore this Tx frame response if its initial rate doesn't match
+        * that of latest Link Quality command.  There may be stragglers
+        * from a previous Link Quality command, but we're no longer interested
+        * in those; they're either from the "active" mode while we're trying
+        * to check "search" mode, or a prior "search" mode after we've moved
+        * to a new "search" mode (which might become the new "active" mode).
+        */
+       table = &lq_sta->lq;
+       tx_rate = le32_to_cpu(table->rs_table[0]);
+       rs_get_tbl_info_from_mcs(tx_rate, info->band, &tbl_type, &rs_index);
+       if (info->band == IEEE80211_BAND_5GHZ)
+               rs_index -= IWL_FIRST_OFDM_RATE;
+       mac_flags = info->status.rates[0].flags;
+       mac_index = info->status.rates[0].idx;
+       /* For HT packets, map MCS to PLCP */
+       if (mac_flags & IEEE80211_TX_RC_MCS) {
+               /* Remove # of streams */
+               mac_index &= RATE_HT_MCS_RATE_CODE_MSK;
+               if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE))
+                       mac_index++;
+               /*
+                * mac80211 HT index is always zero-indexed; we need to move
+                * HT OFDM rates after CCK rates in 2.4 GHz band
+                */
+               if (info->band == IEEE80211_BAND_2GHZ)
+                       mac_index += IWL_FIRST_OFDM_RATE;
+       }
+       /* Here we actually compare this rate to the latest LQ command */
+       if ((mac_index < 0) ||
+           (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
+           (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) ||
+           (tbl_type.ant_type != info->status.antenna) ||
+           (!!(tx_rate & RATE_MCS_HT_MSK) !=
+                               !!(mac_flags & IEEE80211_TX_RC_MCS)) ||
+           (!!(tx_rate & RATE_HT_MCS_GF_MSK) !=
+                               !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) ||
+           (rs_index != mac_index)) {
+               IWL_DEBUG_RATE(mvm,
+                              "initial rate %d does not match %d (0x%x)\n",
+                              mac_index, rs_index, tx_rate);
+               /*
+                * Since rates mis-match, the last LQ command may have failed.
+                * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with
+                * ... driver.
+                */
+               lq_sta->missed_rate_counter++;
+               if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
+                       lq_sta->missed_rate_counter = 0;
+                       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
+               }
+               /* Regardless, ignore this status info for outdated rate */
+               return;
+       } else
+               /* Rate did match, so reset the missed_rate_counter */
+               lq_sta->missed_rate_counter = 0;
+
+       /* Figure out if rate scale algorithm is in active or search table */
+       if (table_type_matches(&tbl_type,
+                              &(lq_sta->lq_info[lq_sta->active_tbl]))) {
+               curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+               other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+       } else if (table_type_matches(
+                       &tbl_type, &lq_sta->lq_info[1 - lq_sta->active_tbl])) {
+               curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+               other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       } else {
+               IWL_DEBUG_RATE(mvm,
+                              "Neither active nor search matches tx rate\n");
+               tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+               IWL_DEBUG_RATE(mvm, "active- lq:%x, ant:%x, SGI:%d\n",
+                              tmp_tbl->lq_type, tmp_tbl->ant_type,
+                              tmp_tbl->is_SGI);
+               tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
+               IWL_DEBUG_RATE(mvm, "search- lq:%x, ant:%x, SGI:%d\n",
+                              tmp_tbl->lq_type, tmp_tbl->ant_type,
+                              tmp_tbl->is_SGI);
+               IWL_DEBUG_RATE(mvm, "actual- lq:%x, ant:%x, SGI:%d\n",
+                              tbl_type.lq_type, tbl_type.ant_type,
+                              tbl_type.is_SGI);
+               /*
+                * no matching table found, let's by-pass the data collection
+                * and continue to perform rate scale to find the rate table
+                */
+               rs_stay_in_table(lq_sta, true);
+               goto done;
+       }
+
+       /*
+        * Updating the frame history depends on whether packets were
+        * aggregated.
+        *
+        * For aggregation, all packets were transmitted at the same rate, the
+        * first index into rate scale table.
+        */
+       if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+               tx_rate = le32_to_cpu(table->rs_table[0]);
+               rs_get_tbl_info_from_mcs(tx_rate, info->band, &tbl_type,
+                                        &rs_index);
+               rs_collect_tx_data(curr_tbl, rs_index,
+                                  info->status.ampdu_len,
+                                  info->status.ampdu_ack_len);
+
+               /* Update success/fail counts if not searching for new mode */
+               if (lq_sta->stay_in_tbl) {
+                       lq_sta->total_success += info->status.ampdu_ack_len;
+                       lq_sta->total_failed += (info->status.ampdu_len -
+                                       info->status.ampdu_ack_len);
+               }
+       } else {
+       /*
+        * For legacy, update frame history with for each Tx retry.
+        */
+               retries = info->status.rates[0].count - 1;
+               /* HW doesn't send more than 15 retries */
+               retries = min(retries, 15);
+
+               /* The last transmission may have been successful */
+               legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK);
+               /* Collect data for each rate used during failed TX attempts */
+               for (i = 0; i <= retries; ++i) {
+                       tx_rate = le32_to_cpu(table->rs_table[i]);
+                       rs_get_tbl_info_from_mcs(tx_rate, info->band,
+                                                &tbl_type, &rs_index);
+                       /*
+                        * Only collect stats if retried rate is in the same RS
+                        * table as active/search.
+                        */
+                       if (table_type_matches(&tbl_type, curr_tbl))
+                               tmp_tbl = curr_tbl;
+                       else if (table_type_matches(&tbl_type, other_tbl))
+                               tmp_tbl = other_tbl;
+                       else
+                               continue;
+                       rs_collect_tx_data(tmp_tbl, rs_index, 1,
+                                          i < retries ? 0 : legacy_success);
+               }
+
+               /* Update success/fail counts if not searching for new mode */
+               if (lq_sta->stay_in_tbl) {
+                       lq_sta->total_success += legacy_success;
+                       lq_sta->total_failed += retries + (1 - legacy_success);
+               }
+       }
+       /* The last TX rate is cached in lq_sta; it's set in if/else above */
+       lq_sta->last_rate_n_flags = tx_rate;
+done:
+       /* See if there's a better rate or modulation mode to try. */
+       if (sta && sta->supp_rates[sband->band])
+               rs_rate_scale_perform(mvm, skb, sta, lq_sta);
+}
+
+/*
+ * Begin a period of staying with a selected modulation mode.
+ * Set "stay_in_tbl" flag to prevent any mode switches.
+ * Set frame tx success limits according to legacy vs. high-throughput,
+ * and reset overall (spanning all rates) tx success history statistics.
+ * These control how long we stay using same modulation mode before
+ * searching for a new mode.
+ */
+static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy,
+                                struct iwl_lq_sta *lq_sta)
+{
+       IWL_DEBUG_RATE(mvm, "we are staying in the same table\n");
+       lq_sta->stay_in_tbl = 1;        /* only place this gets set */
+       if (is_legacy) {
+               lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT;
+               lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT;
+               lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT;
+       } else {
+               lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT;
+               lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT;
+               lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT;
+       }
+       lq_sta->table_count = 0;
+       lq_sta->total_failed = 0;
+       lq_sta->total_success = 0;
+       lq_sta->flush_timer = jiffies;
+       lq_sta->action_counter = 0;
+}
+
+/*
+ * Find correct throughput table for given mode of modulation
+ */
+static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta,
+                                     struct iwl_scale_tbl_info *tbl)
+{
+       /* Used to choose among HT tables */
+       s32 (*ht_tbl_pointer)[IWL_RATE_COUNT];
+
+       /* Check for invalid LQ type */
+       if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) {
+               tbl->expected_tpt = expected_tpt_legacy;
+               return;
+       }
+
+       /* Legacy rates have only one table */
+       if (is_legacy(tbl->lq_type)) {
+               tbl->expected_tpt = expected_tpt_legacy;
+               return;
+       }
+
+       /* Choose among many HT tables depending on number of streams
+        * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation
+        * status */
+       if (is_siso(tbl->lq_type) && !tbl->is_ht40)
+               ht_tbl_pointer = expected_tpt_siso20MHz;
+       else if (is_siso(tbl->lq_type))
+               ht_tbl_pointer = expected_tpt_siso40MHz;
+       else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40)
+               ht_tbl_pointer = expected_tpt_mimo2_20MHz;
+       else if (is_mimo2(tbl->lq_type))
+               ht_tbl_pointer = expected_tpt_mimo2_40MHz;
+       else if (is_mimo3(tbl->lq_type) && !tbl->is_ht40)
+               ht_tbl_pointer = expected_tpt_mimo3_20MHz;
+       else /* if (is_mimo3(tbl->lq_type)) <-- must be true */
+               ht_tbl_pointer = expected_tpt_mimo3_40MHz;
+
+       if (!tbl->is_SGI && !lq_sta->is_agg)            /* Normal */
+               tbl->expected_tpt = ht_tbl_pointer[0];
+       else if (tbl->is_SGI && !lq_sta->is_agg)        /* SGI */
+               tbl->expected_tpt = ht_tbl_pointer[1];
+       else if (!tbl->is_SGI && lq_sta->is_agg)        /* AGG */
+               tbl->expected_tpt = ht_tbl_pointer[2];
+       else                                            /* AGG+SGI */
+               tbl->expected_tpt = ht_tbl_pointer[3];
+}
+
+/*
+ * Find starting rate for new "search" high-throughput mode of modulation.
+ * Goal is to find lowest expected rate (under perfect conditions) that is
+ * above the current measured throughput of "active" mode, to give new mode
+ * a fair chance to prove itself without too many challenges.
+ *
+ * This gets called when transitioning to more aggressive modulation
+ * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive
+ * (i.e. MIMO to SISO).  When moving to MIMO, bit rate will typically need
+ * to decrease to match "active" throughput.  When moving from MIMO to SISO,
+ * bit rate will typically need to increase, but not if performance was bad.
+ */
+static s32 rs_get_best_rate(struct iwl_mvm *mvm,
+                           struct iwl_lq_sta *lq_sta,
+                           struct iwl_scale_tbl_info *tbl,     /* "search" */
+                           u16 rate_mask, s8 index)
+{
+       /* "active" values */
+       struct iwl_scale_tbl_info *active_tbl =
+           &(lq_sta->lq_info[lq_sta->active_tbl]);
+       s32 active_sr = active_tbl->win[index].success_ratio;
+       s32 active_tpt = active_tbl->expected_tpt[index];
+
+       /* expected "search" throughput */
+       s32 *tpt_tbl = tbl->expected_tpt;
+
+       s32 new_rate, high, low, start_hi;
+       u16 high_low;
+       s8 rate = index;
+
+       new_rate = high = low = start_hi = IWL_RATE_INVALID;
+
+       while (1) {
+               high_low = rs_get_adjacent_rate(mvm, rate, rate_mask,
+                                               tbl->lq_type);
+
+               low = high_low & 0xff;
+               high = (high_low >> 8) & 0xff;
+
+               /*
+                * Lower the "search" bit rate, to give new "search" mode
+                * approximately the same throughput as "active" if:
+                *
+                * 1) "Active" mode has been working modestly well (but not
+                *    great), and expected "search" throughput (under perfect
+                *    conditions) at candidate rate is above the actual
+                *    measured "active" throughput (but less than expected
+                *    "active" throughput under perfect conditions).
+                * OR
+                * 2) "Active" mode has been working perfectly or very well
+                *    and expected "search" throughput (under perfect
+                *    conditions) at candidate rate is above expected
+                *    "active" throughput (under perfect conditions).
+                */
+               if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) &&
+                    ((active_sr > IWL_RATE_DECREASE_TH) &&
+                     (active_sr <= IWL_RATE_HIGH_TH) &&
+                     (tpt_tbl[rate] <= active_tpt))) ||
+                   ((active_sr >= IWL_RATE_SCALE_SWITCH) &&
+                    (tpt_tbl[rate] > active_tpt))) {
+                       /* (2nd or later pass)
+                        * If we've already tried to raise the rate, and are
+                        * now trying to lower it, use the higher rate. */
+                       if (start_hi != IWL_RATE_INVALID) {
+                               new_rate = start_hi;
+                               break;
+                       }
+
+                       new_rate = rate;
+
+                       /* Loop again with lower rate */
+                       if (low != IWL_RATE_INVALID)
+                               rate = low;
+
+                       /* Lower rate not available, use the original */
+                       else
+                               break;
+
+               /* Else try to raise the "search" rate to match "active" */
+               } else {
+                       /* (2nd or later pass)
+                        * If we've already tried to lower the rate, and are
+                        * now trying to raise it, use the lower rate. */
+                       if (new_rate != IWL_RATE_INVALID)
+                               break;
+
+                       /* Loop again with higher rate */
+                       else if (high != IWL_RATE_INVALID) {
+                               start_hi = high;
+                               rate = high;
+
+                       /* Higher rate not available, use the original */
+                       } else {
+                               new_rate = rate;
+                               break;
+                       }
+               }
+       }
+
+       return new_rate;
+}
+
+static bool iwl_is_ht40_tx_allowed(struct iwl_mvm *mvm,
+                           struct ieee80211_sta_ht_cap *ht_cap)
+{
+       /*
+        * Remainder of this function checks ht_cap, but if it's
+        * NULL then we can do HT40 (special case for RXON)
+        */
+       if (!ht_cap)
+               return true;
+
+       if (!ht_cap->ht_supported)
+               return false;
+
+       if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+               return false;
+
+       return true;
+}
+
+/*
+ * Set up search table for MIMO2
+ */
+static int rs_switch_to_mimo2(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta,
+                            struct ieee80211_sta *sta,
+                            struct iwl_scale_tbl_info *tbl, int index)
+{
+       u16 rate_mask;
+       s32 rate;
+       s8 is_green = lq_sta->is_green;
+
+       if (!sta->ht_cap.ht_supported)
+               return -1;
+
+       if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
+                                               == WLAN_HT_CAP_SM_PS_STATIC)
+               return -1;
+
+       /* Need both Tx chains/antennas to support MIMO */
+       if (num_of_ant(mvm->nvm_data->valid_tx_ant) < 2)
+               return -1;
+
+       IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO2\n");
+
+       tbl->lq_type = LQ_MIMO2;
+       tbl->action = 0;
+       tbl->max_search = IWL_MAX_SEARCH;
+       rate_mask = lq_sta->active_mimo2_rate;
+
+       if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap))
+               tbl->is_ht40 = 1;
+       else
+               tbl->is_ht40 = 0;
+
+       rs_set_expected_tpt_table(lq_sta, tbl);
+
+       rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
+
+       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 best rate %d mask %X\n",
+                      rate, rate_mask);
+       if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+               IWL_DEBUG_RATE(mvm, "Can't switch with index %d rate mask %x\n",
+                              rate, rate_mask);
+               return -1;
+       }
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
+
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
+                      tbl->current_rate, is_green);
+       return 0;
+}
+
+/*
+ * Set up search table for MIMO3
+ */
+static int rs_switch_to_mimo3(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta,
+                            struct ieee80211_sta *sta,
+                            struct iwl_scale_tbl_info *tbl, int index)
+{
+       u16 rate_mask;
+       s32 rate;
+       s8 is_green = lq_sta->is_green;
+
+       if (!sta->ht_cap.ht_supported)
+               return -1;
+
+       if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2)
+                                               == WLAN_HT_CAP_SM_PS_STATIC)
+               return -1;
+
+       /* Need both Tx chains/antennas to support MIMO */
+       if (num_of_ant(mvm->nvm_data->valid_tx_ant) < 3)
+               return -1;
+
+       IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO3\n");
+
+       tbl->lq_type = LQ_MIMO3;
+       tbl->action = 0;
+       tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
+       rate_mask = lq_sta->active_mimo3_rate;
+
+       if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap))
+               tbl->is_ht40 = 1;
+       else
+               tbl->is_ht40 = 0;
+
+       rs_set_expected_tpt_table(lq_sta, tbl);
+
+       rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
+
+       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 best rate %d mask %X\n",
+                      rate, rate_mask);
+       if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+               IWL_DEBUG_RATE(mvm, "Can't switch with index %d rate mask %x\n",
+                              rate, rate_mask);
+               return -1;
+       }
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
+
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
+                      tbl->current_rate, is_green);
+       return 0;
+}
+
+/*
+ * Set up search table for SISO
+ */
+static int rs_switch_to_siso(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta,
+                            struct ieee80211_sta *sta,
+                            struct iwl_scale_tbl_info *tbl, int index)
+{
+       u16 rate_mask;
+       u8 is_green = lq_sta->is_green;
+       s32 rate;
+
+       if (!sta->ht_cap.ht_supported)
+               return -1;
+
+       IWL_DEBUG_RATE(mvm, "LQ: try to switch to SISO\n");
+
+       tbl->lq_type = LQ_SISO;
+       tbl->action = 0;
+       tbl->max_search = IWL_MAX_SEARCH;
+       rate_mask = lq_sta->active_siso_rate;
+
+       if (iwl_is_ht40_tx_allowed(mvm, &sta->ht_cap))
+               tbl->is_ht40 = 1;
+       else
+               tbl->is_ht40 = 0;
+
+       if (is_green)
+               tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/
+
+       rs_set_expected_tpt_table(lq_sta, tbl);
+       rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index);
+
+       IWL_DEBUG_RATE(mvm, "LQ: get best rate %d mask %X\n", rate, rate_mask);
+       if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) {
+               IWL_DEBUG_RATE(mvm,
+                              "can not switch with index %d rate mask %x\n",
+                              rate, rate_mask);
+               return -1;
+       }
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green);
+       IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n",
+                      tbl->current_rate, is_green);
+       return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from legacy
+ */
+static int rs_move_legacy_other(struct iwl_mvm *mvm,
+                               struct iwl_lq_sta *lq_sta,
+                               struct ieee80211_sta *sta,
+                               int index)
+{
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *search_tbl =
+                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
+       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+       u8 start_action;
+       u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       u8 tx_chains_num = num_of_ant(valid_tx_ant);
+       int ret;
+       u8 update_search_tbl_counter = 0;
+
+       start_action = tbl->action;
+       while (1) {
+               lq_sta->action_counter++;
+               switch (tbl->action) {
+               case IWL_LEGACY_SWITCH_ANTENNA1:
+               case IWL_LEGACY_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE(mvm, "LQ: Legacy toggle Antenna\n");
+
+                       if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 &&
+                            tx_chains_num <= 1) ||
+                           (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 &&
+                            tx_chains_num <= 2))
+                               break;
+
+                       /* Don't change antenna if success has been great */
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       /* Set up search table to try other antenna */
+                       memcpy(search_tbl, tbl, sz);
+
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                             &search_tbl->current_rate,
+                                             search_tbl)) {
+                               update_search_tbl_counter = 1;
+                               rs_set_expected_tpt_table(lq_sta, search_tbl);
+                               goto out;
+                       }
+                       break;
+               case IWL_LEGACY_SWITCH_SISO:
+                       IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to SISO\n");
+
+                       /* Set up search table to try SISO */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+                       ret = rs_switch_to_siso(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret) {
+                               lq_sta->action_counter = 0;
+                               goto out;
+                       }
+
+                       break;
+               case IWL_LEGACY_SWITCH_MIMO2_AB:
+               case IWL_LEGACY_SWITCH_MIMO2_AC:
+               case IWL_LEGACY_SWITCH_MIMO2_BC:
+                       IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO2\n");
+
+                       /* Set up search table to try MIMO */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+
+                       if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB)
+                               search_tbl->ant_type = ANT_AB;
+                       else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC)
+                               search_tbl->ant_type = ANT_AC;
+                       else
+                               search_tbl->ant_type = ANT_BC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo2(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret) {
+                               lq_sta->action_counter = 0;
+                               goto out;
+                       }
+                       break;
+
+               case IWL_LEGACY_SWITCH_MIMO3_ABC:
+                       IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO3\n");
+
+                       /* Set up search table to try MIMO3 */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+
+                       search_tbl->ant_type = ANT_ABC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret) {
+                               lq_sta->action_counter = 0;
+                               goto out;
+                       }
+                       break;
+               }
+               tbl->action++;
+               if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+                       tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+
+               if (tbl->action == start_action)
+                       break;
+       }
+       search_tbl->lq_type = LQ_NONE;
+       return 0;
+
+out:
+       lq_sta->search_better_tbl = 1;
+       tbl->action++;
+       if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+               tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
+       if (update_search_tbl_counter)
+               search_tbl->action = tbl->action;
+       return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from SISO
+ */
+static int rs_move_siso_to_other(struct iwl_mvm *mvm,
+                                struct iwl_lq_sta *lq_sta,
+                                struct ieee80211_sta *sta, int index)
+{
+       u8 is_green = lq_sta->is_green;
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *search_tbl =
+                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+       u8 start_action;
+       u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       u8 tx_chains_num = num_of_ant(valid_tx_ant);
+       u8 update_search_tbl_counter = 0;
+       int ret;
+
+       start_action = tbl->action;
+       while (1) {
+               lq_sta->action_counter++;
+               switch (tbl->action) {
+               case IWL_SISO_SWITCH_ANTENNA1:
+               case IWL_SISO_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE(mvm, "LQ: SISO toggle Antenna\n");
+                       if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 &&
+                            tx_chains_num <= 1) ||
+                           (tbl->action == IWL_SISO_SWITCH_ANTENNA2 &&
+                            tx_chains_num <= 2))
+                               break;
+
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                             &search_tbl->current_rate,
+                                             search_tbl)) {
+                               update_search_tbl_counter = 1;
+                               goto out;
+                       }
+                       break;
+               case IWL_SISO_SWITCH_MIMO2_AB:
+               case IWL_SISO_SWITCH_MIMO2_AC:
+               case IWL_SISO_SWITCH_MIMO2_BC:
+                       IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO2\n");
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+
+                       if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB)
+                               search_tbl->ant_type = ANT_AB;
+                       else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC)
+                               search_tbl->ant_type = ANT_AC;
+                       else
+                               search_tbl->ant_type = ANT_BC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo2(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+                       break;
+               case IWL_SISO_SWITCH_GI:
+                       if (!tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_20))
+                               break;
+                       if (tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_40))
+                               break;
+
+                       IWL_DEBUG_RATE(mvm, "LQ: SISO toggle SGI/NGI\n");
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (is_green) {
+                               if (!tbl->is_SGI)
+                                       break;
+                               else
+                                       IWL_ERR(mvm,
+                                               "SGI was set in GF+SISO\n");
+                       }
+                       search_tbl->is_SGI = !tbl->is_SGI;
+                       rs_set_expected_tpt_table(lq_sta, search_tbl);
+                       if (tbl->is_SGI) {
+                               s32 tpt = lq_sta->last_tpt / 100;
+                               if (tpt >= search_tbl->expected_tpt[index])
+                                       break;
+                       }
+                       search_tbl->current_rate =
+                               rate_n_flags_from_tbl(mvm, search_tbl,
+                                                     index, is_green);
+                       update_search_tbl_counter = 1;
+                       goto out;
+               case IWL_SISO_SWITCH_MIMO3_ABC:
+                       IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO3\n");
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+                       search_tbl->ant_type = ANT_ABC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+                       break;
+               }
+               tbl->action++;
+               if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+                       tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+
+               if (tbl->action == start_action)
+                       break;
+       }
+       search_tbl->lq_type = LQ_NONE;
+       return 0;
+
+ out:
+       lq_sta->search_better_tbl = 1;
+       tbl->action++;
+       if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC)
+               tbl->action = IWL_SISO_SWITCH_ANTENNA1;
+       if (update_search_tbl_counter)
+               search_tbl->action = tbl->action;
+
+       return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from MIMO2
+ */
+static int rs_move_mimo2_to_other(struct iwl_mvm *mvm,
+                                struct iwl_lq_sta *lq_sta,
+                                struct ieee80211_sta *sta, int index)
+{
+       s8 is_green = lq_sta->is_green;
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *search_tbl =
+                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+       u8 start_action;
+       u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       u8 tx_chains_num = num_of_ant(valid_tx_ant);
+       u8 update_search_tbl_counter = 0;
+       int ret;
+
+       start_action = tbl->action;
+       while (1) {
+               lq_sta->action_counter++;
+               switch (tbl->action) {
+               case IWL_MIMO2_SWITCH_ANTENNA1:
+               case IWL_MIMO2_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle Antennas\n");
+
+                       if (tx_chains_num <= 2)
+                               break;
+
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                             &search_tbl->current_rate,
+                                             search_tbl)) {
+                               update_search_tbl_counter = 1;
+                               goto out;
+                       }
+                       break;
+               case IWL_MIMO2_SWITCH_SISO_A:
+               case IWL_MIMO2_SWITCH_SISO_B:
+               case IWL_MIMO2_SWITCH_SISO_C:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n");
+
+                       /* Set up new search table for SISO */
+                       memcpy(search_tbl, tbl, sz);
+
+                       if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
+                               search_tbl->ant_type = ANT_A;
+                       else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
+                               search_tbl->ant_type = ANT_B;
+                       else
+                               search_tbl->ant_type = ANT_C;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_siso(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+
+                       break;
+
+               case IWL_MIMO2_SWITCH_GI:
+                       if (!tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_20))
+                               break;
+                       if (tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_40))
+                               break;
+
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle SGI/NGI\n");
+
+                       /* Set up new search table for MIMO2 */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = !tbl->is_SGI;
+                       rs_set_expected_tpt_table(lq_sta, search_tbl);
+                       /*
+                        * If active table already uses the fastest possible
+                        * modulation (dual stream with short guard interval),
+                        * and it's working well, there's no need to look
+                        * for a better type of modulation!
+                        */
+                       if (tbl->is_SGI) {
+                               s32 tpt = lq_sta->last_tpt / 100;
+                               if (tpt >= search_tbl->expected_tpt[index])
+                                       break;
+                       }
+                       search_tbl->current_rate =
+                               rate_n_flags_from_tbl(mvm, search_tbl,
+                                                     index, is_green);
+                       update_search_tbl_counter = 1;
+                       goto out;
+
+               case IWL_MIMO2_SWITCH_MIMO3_ABC:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to MIMO3\n");
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+                       search_tbl->ant_type = ANT_ABC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+
+                       break;
+               }
+               tbl->action++;
+               if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+                       tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+
+               if (tbl->action == start_action)
+                       break;
+       }
+       search_tbl->lq_type = LQ_NONE;
+       return 0;
+ out:
+       lq_sta->search_better_tbl = 1;
+       tbl->action++;
+       if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+               tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
+       if (update_search_tbl_counter)
+               search_tbl->action = tbl->action;
+
+       return 0;
+}
+
+/*
+ * Try to switch to new modulation mode from MIMO3
+ */
+static int rs_move_mimo3_to_other(struct iwl_mvm *mvm,
+                                struct iwl_lq_sta *lq_sta,
+                                struct ieee80211_sta *sta, int index)
+{
+       s8 is_green = lq_sta->is_green;
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+       struct iwl_scale_tbl_info *search_tbl =
+                               &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+       struct iwl_rate_scale_data *window = &(tbl->win[index]);
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       u32 sz = (sizeof(struct iwl_scale_tbl_info) -
+                 (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT));
+       u8 start_action;
+       u8 valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       u8 tx_chains_num = num_of_ant(valid_tx_ant);
+       int ret;
+       u8 update_search_tbl_counter = 0;
+
+       start_action = tbl->action;
+       while (1) {
+               lq_sta->action_counter++;
+               switch (tbl->action) {
+               case IWL_MIMO3_SWITCH_ANTENNA1:
+               case IWL_MIMO3_SWITCH_ANTENNA2:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle Antennas\n");
+
+                       if (tx_chains_num <= 3)
+                               break;
+
+                       if (window->success_ratio >= IWL_RS_GOOD_RATIO)
+                               break;
+
+                       memcpy(search_tbl, tbl, sz);
+                       if (rs_toggle_antenna(valid_tx_ant,
+                                             &search_tbl->current_rate,
+                                             search_tbl))
+                               goto out;
+                       break;
+               case IWL_MIMO3_SWITCH_SISO_A:
+               case IWL_MIMO3_SWITCH_SISO_B:
+               case IWL_MIMO3_SWITCH_SISO_C:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to SISO\n");
+
+                       /* Set up new search table for SISO */
+                       memcpy(search_tbl, tbl, sz);
+
+                       if (tbl->action == IWL_MIMO3_SWITCH_SISO_A)
+                               search_tbl->ant_type = ANT_A;
+                       else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B)
+                               search_tbl->ant_type = ANT_B;
+                       else
+                               search_tbl->ant_type = ANT_C;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_siso(mvm, lq_sta, sta,
+                                               search_tbl, index);
+                       if (!ret)
+                               goto out;
+
+                       break;
+
+               case IWL_MIMO3_SWITCH_MIMO2_AB:
+               case IWL_MIMO3_SWITCH_MIMO2_AC:
+               case IWL_MIMO3_SWITCH_MIMO2_BC:
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to MIMO2\n");
+
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = 0;
+                       if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB)
+                               search_tbl->ant_type = ANT_AB;
+                       else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC)
+                               search_tbl->ant_type = ANT_AC;
+                       else
+                               search_tbl->ant_type = ANT_BC;
+
+                       if (!rs_is_valid_ant(valid_tx_ant,
+                                            search_tbl->ant_type))
+                               break;
+
+                       ret = rs_switch_to_mimo2(mvm, lq_sta, sta,
+                                                search_tbl, index);
+                       if (!ret)
+                               goto out;
+
+                       break;
+
+               case IWL_MIMO3_SWITCH_GI:
+                       if (!tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_20))
+                               break;
+                       if (tbl->is_ht40 && !(ht_cap->cap &
+                                               IEEE80211_HT_CAP_SGI_40))
+                               break;
+
+                       IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle SGI/NGI\n");
+
+                       /* Set up new search table for MIMO */
+                       memcpy(search_tbl, tbl, sz);
+                       search_tbl->is_SGI = !tbl->is_SGI;
+                       rs_set_expected_tpt_table(lq_sta, search_tbl);
+                       /*
+                        * If active table already uses the fastest possible
+                        * modulation (dual stream with short guard interval),
+                        * and it's working well, there's no need to look
+                        * for a better type of modulation!
+                        */
+                       if (tbl->is_SGI) {
+                               s32 tpt = lq_sta->last_tpt / 100;
+                               if (tpt >= search_tbl->expected_tpt[index])
+                                       break;
+                       }
+                       search_tbl->current_rate =
+                               rate_n_flags_from_tbl(mvm, search_tbl,
+                                                     index, is_green);
+                       update_search_tbl_counter = 1;
+                       goto out;
+               }
+               tbl->action++;
+               if (tbl->action > IWL_MIMO3_SWITCH_GI)
+                       tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
+
+               if (tbl->action == start_action)
+                       break;
+       }
+       search_tbl->lq_type = LQ_NONE;
+       return 0;
+ out:
+       lq_sta->search_better_tbl = 1;
+       tbl->action++;
+       if (tbl->action > IWL_MIMO3_SWITCH_GI)
+               tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
+       if (update_search_tbl_counter)
+               search_tbl->action = tbl->action;
+
+       return 0;
+}
+
+/*
+ * Check whether we should continue using same modulation mode, or
+ * begin search for a new mode, based on:
+ * 1) # tx successes or failures while using this mode
+ * 2) # times calling this function
+ * 3) elapsed time in this mode (not used, for now)
+ */
+static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
+{
+       struct iwl_scale_tbl_info *tbl;
+       int i;
+       int active_tbl;
+       int flush_interval_passed = 0;
+       struct iwl_mvm *mvm;
+
+       mvm = lq_sta->drv;
+       active_tbl = lq_sta->active_tbl;
+
+       tbl = &(lq_sta->lq_info[active_tbl]);
+
+       /* If we've been disallowing search, see if we should now allow it */
+       if (lq_sta->stay_in_tbl) {
+               /* Elapsed time using current modulation mode */
+               if (lq_sta->flush_timer)
+                       flush_interval_passed =
+                               time_after(jiffies,
+                                          (unsigned long)(lq_sta->flush_timer +
+                                               IWL_RATE_SCALE_FLUSH_INTVL));
+
+               /*
+                * Check if we should allow search for new modulation mode.
+                * If many frames have failed or succeeded, or we've used
+                * this same modulation for a long time, allow search, and
+                * reset history stats that keep track of whether we should
+                * allow a new search.  Also (below) reset all bitmaps and
+                * stats in active history.
+                */
+               if (force_search ||
+                   (lq_sta->total_failed > lq_sta->max_failure_limit) ||
+                   (lq_sta->total_success > lq_sta->max_success_limit) ||
+                   ((!lq_sta->search_better_tbl) &&
+                    (lq_sta->flush_timer) && (flush_interval_passed))) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "LQ: stay is expired %d %d %d\n",
+                                    lq_sta->total_failed,
+                                    lq_sta->total_success,
+                                    flush_interval_passed);
+
+                       /* Allow search for new mode */
+                       lq_sta->stay_in_tbl = 0;        /* only place reset */
+                       lq_sta->total_failed = 0;
+                       lq_sta->total_success = 0;
+                       lq_sta->flush_timer = 0;
+               /*
+                * Else if we've used this modulation mode enough repetitions
+                * (regardless of elapsed time or success/failure), reset
+                * history bitmaps and rate-specific stats for all rates in
+                * active table.
+                */
+               } else {
+                       lq_sta->table_count++;
+                       if (lq_sta->table_count >=
+                           lq_sta->table_count_limit) {
+                               lq_sta->table_count = 0;
+
+                               IWL_DEBUG_RATE(mvm,
+                                              "LQ: stay in table clear win\n");
+                               for (i = 0; i < IWL_RATE_COUNT; i++)
+                                       rs_rate_scale_clear_window(
+                                               &(tbl->win[i]));
+                       }
+               }
+
+               /* If transitioning to allow "search", reset all history
+                * bitmaps and stats in active table (this will become the new
+                * "search" table). */
+               if (!lq_sta->stay_in_tbl) {
+                       for (i = 0; i < IWL_RATE_COUNT; i++)
+                               rs_rate_scale_clear_window(&(tbl->win[i]));
+               }
+       }
+}
+
+/*
+ * setup rate table in uCode
+ */
+static void rs_update_rate_tbl(struct iwl_mvm *mvm,
+                              struct iwl_lq_sta *lq_sta,
+                              struct iwl_scale_tbl_info *tbl,
+                              int index, u8 is_green)
+{
+       u32 rate;
+
+       /* Update uCode's rate table. */
+       rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green);
+       rs_fill_link_cmd(mvm, lq_sta, rate);
+       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
+}
+
+/*
+ * Do rate scaling and search for new modulation mode.
+ */
+static void rs_rate_scale_perform(struct iwl_mvm *mvm,
+                                 struct sk_buff *skb,
+                                 struct ieee80211_sta *sta,
+                                 struct iwl_lq_sta *lq_sta)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       int low = IWL_RATE_INVALID;
+       int high = IWL_RATE_INVALID;
+       int index;
+       int i;
+       struct iwl_rate_scale_data *window = NULL;
+       int current_tpt = IWL_INVALID_VALUE;
+       int low_tpt = IWL_INVALID_VALUE;
+       int high_tpt = IWL_INVALID_VALUE;
+       u32 fail_count;
+       s8 scale_action = 0;
+       u16 rate_mask;
+       u8 update_lq = 0;
+       struct iwl_scale_tbl_info *tbl, *tbl1;
+       u16 rate_scale_index_msk = 0;
+       u8 is_green = 0;
+       u8 active_tbl = 0;
+       u8 done_search = 0;
+       u16 high_low;
+       s32 sr;
+       u8 tid = IWL_MAX_TID_COUNT;
+       struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data;
+
+       IWL_DEBUG_RATE(mvm, "rate scale calculate new rate for skb\n");
+
+       /* Send management frames and NO_ACK data using lowest rate. */
+       /* TODO: this could probably be improved.. */
+       if (!ieee80211_is_data(hdr->frame_control) ||
+           info->flags & IEEE80211_TX_CTL_NO_ACK)
+               return;
+
+       lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
+
+       tid = rs_tl_add_packet(lq_sta, hdr);
+       if ((tid != IWL_MAX_TID_COUNT) &&
+           (lq_sta->tx_agg_tid_en & (1 << tid))) {
+               tid_data = &sta_priv->tid_data[tid];
+               if (tid_data->state == IWL_AGG_OFF)
+                       lq_sta->is_agg = 0;
+               else
+                       lq_sta->is_agg = 1;
+       } else {
+               lq_sta->is_agg = 0;
+       }
+
+       /*
+        * Select rate-scale / modulation-mode table to work with in
+        * the rest of this function:  "search" if searching for better
+        * modulation mode, or "active" if doing rate scaling within a mode.
+        */
+       if (!lq_sta->search_better_tbl)
+               active_tbl = lq_sta->active_tbl;
+       else
+               active_tbl = 1 - lq_sta->active_tbl;
+
+       tbl = &(lq_sta->lq_info[active_tbl]);
+       if (is_legacy(tbl->lq_type))
+               lq_sta->is_green = 0;
+       else
+               lq_sta->is_green = rs_use_green(sta);
+       is_green = lq_sta->is_green;
+
+       /* current tx rate */
+       index = lq_sta->last_txrate_idx;
+
+       IWL_DEBUG_RATE(mvm, "Rate scale index %d for type %d\n", index,
+                      tbl->lq_type);
+
+       /* rates available for this association, and for modulation mode */
+       rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type);
+
+       IWL_DEBUG_RATE(mvm, "mask 0x%04X\n", rate_mask);
+
+       /* mask with station rate restriction */
+       if (is_legacy(tbl->lq_type)) {
+               if (lq_sta->band == IEEE80211_BAND_5GHZ)
+                       /* supp_rates has no CCK bits in A mode */
+                       rate_scale_index_msk = (u16) (rate_mask &
+                               (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
+               else
+                       rate_scale_index_msk = (u16) (rate_mask &
+                                                     lq_sta->supp_rates);
+
+       } else {
+               rate_scale_index_msk = rate_mask;
+       }
+
+       if (!rate_scale_index_msk)
+               rate_scale_index_msk = rate_mask;
+
+       if (!((1 << index) & rate_scale_index_msk)) {
+               IWL_ERR(mvm, "Current Rate is not valid\n");
+               if (lq_sta->search_better_tbl) {
+                       /* revert to active table if search table is not valid*/
+                       tbl->lq_type = LQ_NONE;
+                       lq_sta->search_better_tbl = 0;
+                       tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+                       /* get "active" rate info */
+                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+                       rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green);
+               }
+               return;
+       }
+
+       /* Get expected throughput table and history window for current rate */
+       if (!tbl->expected_tpt) {
+               IWL_ERR(mvm, "tbl->expected_tpt is NULL\n");
+               return;
+       }
+
+       /* force user max rate if set by user */
+       if ((lq_sta->max_rate_idx != -1) &&
+           (lq_sta->max_rate_idx < index)) {
+               index = lq_sta->max_rate_idx;
+               update_lq = 1;
+               window = &(tbl->win[index]);
+               goto lq_update;
+       }
+
+       window = &(tbl->win[index]);
+
+       /*
+        * If there is not enough history to calculate actual average
+        * throughput, keep analyzing results of more tx frames, without
+        * changing rate or mode (bypass most of the rest of this function).
+        * Set up new rate table in uCode only if old rate is not supported
+        * in current association (use new rate found above).
+        */
+       fail_count = window->counter - window->success_counter;
+       if ((fail_count < IWL_RATE_MIN_FAILURE_TH) &&
+           (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) {
+               IWL_DEBUG_RATE(mvm,
+                              "LQ: still below TH. succ=%d total=%d for index %d\n",
+                              window->success_counter, window->counter, index);
+
+               /* Can't calculate this yet; not enough history */
+               window->average_tpt = IWL_INVALID_VALUE;
+
+               /* Should we stay with this modulation mode,
+                * or search for a new one? */
+               rs_stay_in_table(lq_sta, false);
+
+               goto out;
+       }
+       /* Else we have enough samples; calculate estimate of
+        * actual average throughput */
+       if (window->average_tpt != ((window->success_ratio *
+                       tbl->expected_tpt[index] + 64) / 128)) {
+               IWL_ERR(mvm,
+                       "expected_tpt should have been calculated by now\n");
+               window->average_tpt = ((window->success_ratio *
+                                       tbl->expected_tpt[index] + 64) / 128);
+       }
+
+       /* If we are searching for better modulation mode, check success. */
+       if (lq_sta->search_better_tbl) {
+               /* If good success, continue using the "search" mode;
+                * no need to send new link quality command, since we're
+                * continuing to use the setup that we've been trying. */
+               if (window->average_tpt > lq_sta->last_tpt) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "LQ: SWITCHING TO NEW TABLE suc=%d cur-tpt=%d old-tpt=%d\n",
+                                      window->success_ratio,
+                                      window->average_tpt,
+                                      lq_sta->last_tpt);
+
+                       if (!is_legacy(tbl->lq_type))
+                               lq_sta->enable_counter = 1;
+
+                       /* Swap tables; "search" becomes "active" */
+                       lq_sta->active_tbl = active_tbl;
+                       current_tpt = window->average_tpt;
+               /* Else poor success; go back to mode in "active" table */
+               } else {
+                       IWL_DEBUG_RATE(mvm,
+                                      "LQ: GOING BACK TO THE OLD TABLE suc=%d cur-tpt=%d old-tpt=%d\n",
+                                      window->success_ratio,
+                                      window->average_tpt,
+                                      lq_sta->last_tpt);
+
+                       /* Nullify "search" table */
+                       tbl->lq_type = LQ_NONE;
+
+                       /* Revert to "active" table */
+                       active_tbl = lq_sta->active_tbl;
+                       tbl = &(lq_sta->lq_info[active_tbl]);
+
+                       /* Revert to "active" rate and throughput info */
+                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+                       current_tpt = lq_sta->last_tpt;
+
+                       /* Need to set up a new rate table in uCode */
+                       update_lq = 1;
+               }
+
+               /* Either way, we've made a decision; modulation mode
+                * search is done, allow rate adjustment next time. */
+               lq_sta->search_better_tbl = 0;
+               done_search = 1;        /* Don't switch modes below! */
+               goto lq_update;
+       }
+
+       /* (Else) not in search of better modulation mode, try for better
+        * starting rate, while staying in this mode. */
+       high_low = rs_get_adjacent_rate(mvm, index, rate_scale_index_msk,
+                                       tbl->lq_type);
+       low = high_low & 0xff;
+       high = (high_low >> 8) & 0xff;
+
+       /* If user set max rate, dont allow higher than user constrain */
+       if ((lq_sta->max_rate_idx != -1) &&
+           (lq_sta->max_rate_idx < high))
+               high = IWL_RATE_INVALID;
+
+       sr = window->success_ratio;
+
+       /* Collect measured throughputs for current and adjacent rates */
+       current_tpt = window->average_tpt;
+       if (low != IWL_RATE_INVALID)
+               low_tpt = tbl->win[low].average_tpt;
+       if (high != IWL_RATE_INVALID)
+               high_tpt = tbl->win[high].average_tpt;
+
+       scale_action = 0;
+
+       /* Too many failures, decrease rate */
+       if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) {
+               IWL_DEBUG_RATE(mvm,
+                              "decrease rate because of low success_ratio\n");
+               scale_action = -1;
+       /* No throughput measured yet for adjacent rates; try increase. */
+       } else if ((low_tpt == IWL_INVALID_VALUE) &&
+                  (high_tpt == IWL_INVALID_VALUE)) {
+               if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH)
+                       scale_action = 1;
+               else if (low != IWL_RATE_INVALID)
+                       scale_action = 0;
+       }
+
+       /* Both adjacent throughputs are measured, but neither one has better
+        * throughput; we're using the best rate, don't change it! */
+       else if ((low_tpt != IWL_INVALID_VALUE) &&
+                (high_tpt != IWL_INVALID_VALUE) &&
+                (low_tpt < current_tpt) &&
+                (high_tpt < current_tpt))
+               scale_action = 0;
+
+       /* At least one adjacent rate's throughput is measured,
+        * and may have better performance. */
+       else {
+               /* Higher adjacent rate's throughput is measured */
+               if (high_tpt != IWL_INVALID_VALUE) {
+                       /* Higher rate has better throughput */
+                       if (high_tpt > current_tpt &&
+                           sr >= IWL_RATE_INCREASE_TH) {
+                               scale_action = 1;
+                       } else {
+                               scale_action = 0;
+                       }
+
+               /* Lower adjacent rate's throughput is measured */
+               } else if (low_tpt != IWL_INVALID_VALUE) {
+                       /* Lower rate has better throughput */
+                       if (low_tpt > current_tpt) {
+                               IWL_DEBUG_RATE(mvm,
+                                              "decrease rate because of low tpt\n");
+                               scale_action = -1;
+                       } else if (sr >= IWL_RATE_INCREASE_TH) {
+                               scale_action = 1;
+                       }
+               }
+       }
+
+       /* Sanity check; asked for decrease, but success rate or throughput
+        * has been good at old rate.  Don't change it. */
+       if ((scale_action == -1) && (low != IWL_RATE_INVALID) &&
+           ((sr > IWL_RATE_HIGH_TH) ||
+            (current_tpt > (100 * tbl->expected_tpt[low]))))
+               scale_action = 0;
+
+       switch (scale_action) {
+       case -1:
+               /* Decrease starting rate, update uCode's rate table */
+               if (low != IWL_RATE_INVALID) {
+                       update_lq = 1;
+                       index = low;
+               }
+
+               break;
+       case 1:
+               /* Increase starting rate, update uCode's rate table */
+               if (high != IWL_RATE_INVALID) {
+                       update_lq = 1;
+                       index = high;
+               }
+
+               break;
+       case 0:
+               /* No change */
+       default:
+               break;
+       }
+
+       IWL_DEBUG_RATE(mvm,
+                      "choose rate scale index %d action %d low %d high %d type %d\n",
+                      index, scale_action, low, high, tbl->lq_type);
+
+lq_update:
+       /* Replace uCode's rate table for the destination station. */
+       if (update_lq)
+               rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green);
+
+       rs_stay_in_table(lq_sta, false);
+
+       /*
+        * Search for new modulation mode if we're:
+        * 1)  Not changing rates right now
+        * 2)  Not just finishing up a search
+        * 3)  Allowing a new search
+        */
+       if (!update_lq && !done_search &&
+           !lq_sta->stay_in_tbl && window->counter) {
+               /* Save current throughput to compare with "search" throughput*/
+               lq_sta->last_tpt = current_tpt;
+
+               /* Select a new "search" modulation mode to try.
+                * If one is found, set up the new "search" table. */
+               if (is_legacy(tbl->lq_type))
+                       rs_move_legacy_other(mvm, lq_sta, sta, index);
+               else if (is_siso(tbl->lq_type))
+                       rs_move_siso_to_other(mvm, lq_sta, sta, index);
+               else if (is_mimo2(tbl->lq_type))
+                       rs_move_mimo2_to_other(mvm, lq_sta, sta, index);
+               else
+                       rs_move_mimo3_to_other(mvm, lq_sta, sta, index);
+
+               /* If new "search" mode was selected, set up in uCode table */
+               if (lq_sta->search_better_tbl) {
+                       /* Access the "search" table, clear its history. */
+                       tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
+                       for (i = 0; i < IWL_RATE_COUNT; i++)
+                               rs_rate_scale_clear_window(&(tbl->win[i]));
+
+                       /* Use new "search" start rate */
+                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+
+                       IWL_DEBUG_RATE(mvm,
+                                      "Switch current  mcs: %X index: %d\n",
+                                      tbl->current_rate, index);
+                       rs_fill_link_cmd(mvm, lq_sta, tbl->current_rate);
+                       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
+               } else {
+                       done_search = 1;
+               }
+       }
+
+       if (done_search && !lq_sta->stay_in_tbl) {
+               /* If the "active" (non-search) mode was legacy,
+                * and we've tried switching antennas,
+                * but we haven't been able to try HT modes (not available),
+                * stay with best antenna legacy modulation for a while
+                * before next round of mode comparisons. */
+               tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]);
+               if (is_legacy(tbl1->lq_type) && !sta->ht_cap.ht_supported &&
+                   lq_sta->action_counter > tbl1->max_search) {
+                       IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n");
+                       rs_set_stay_in_table(mvm, 1, lq_sta);
+               }
+
+               /* If we're in an HT mode, and all 3 mode switch actions
+                * have been tried and compared, stay in this best modulation
+                * mode for a while before next round of mode comparisons. */
+               if (lq_sta->enable_counter &&
+                   (lq_sta->action_counter >= tbl1->max_search)) {
+                       if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) &&
+                           (lq_sta->tx_agg_tid_en & (1 << tid)) &&
+                           (tid != IWL_MAX_TID_COUNT)) {
+                               tid_data = &sta_priv->tid_data[tid];
+                               if (tid_data->state == IWL_AGG_OFF) {
+                                       IWL_DEBUG_RATE(mvm,
+                                                      "try to aggregate tid %d\n",
+                                                      tid);
+                                       rs_tl_turn_on_agg(mvm, tid,
+                                                         lq_sta, sta);
+                               }
+                       }
+                       rs_set_stay_in_table(mvm, 0, lq_sta);
+               }
+       }
+
+out:
+       tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green);
+       lq_sta->last_txrate_idx = index;
+}
+
+/**
+ * rs_initialize_lq - Initialize a station's hardware rate table
+ *
+ * The uCode's station table contains a table of fallback rates
+ * for automatic fallback during transmission.
+ *
+ * NOTE: This sets up a default set of values.  These will be replaced later
+ *       if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
+ *       rc80211_simple.
+ *
+ * NOTE: Run REPLY_ADD_STA command to set up station table entry, before
+ *       calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
+ *       which requires station table entry to exist).
+ */
+static void rs_initialize_lq(struct iwl_mvm *mvm,
+                            struct ieee80211_sta *sta,
+                            struct iwl_lq_sta *lq_sta,
+                            enum ieee80211_band band)
+{
+       struct iwl_scale_tbl_info *tbl;
+       int rate_idx;
+       int i;
+       u32 rate;
+       u8 use_green = rs_use_green(sta);
+       u8 active_tbl = 0;
+       u8 valid_tx_ant;
+
+       if (!sta || !lq_sta)
+               return;
+
+       i = lq_sta->last_txrate_idx;
+
+       valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+
+       if (!lq_sta->search_better_tbl)
+               active_tbl = lq_sta->active_tbl;
+       else
+               active_tbl = 1 - lq_sta->active_tbl;
+
+       tbl = &(lq_sta->lq_info[active_tbl]);
+
+       if ((i < 0) || (i >= IWL_RATE_COUNT))
+               i = 0;
+
+       rate = iwl_rates[i].plcp;
+       tbl->ant_type = first_antenna(valid_tx_ant);
+       rate |= tbl->ant_type << RATE_MCS_ANT_POS;
+
+       if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE)
+               rate |= RATE_MCS_CCK_MSK;
+
+       rs_get_tbl_info_from_mcs(rate, band, tbl, &rate_idx);
+       if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type))
+               rs_toggle_antenna(valid_tx_ant, &rate, tbl);
+
+       rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx, use_green);
+       tbl->current_rate = rate;
+       rs_set_expected_tpt_table(lq_sta, tbl);
+       rs_fill_link_cmd(NULL, lq_sta, rate);
+       /* TODO restore station should remember the lq cmd */
+       iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_SYNC, true);
+}
+
+static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
+                       struct ieee80211_tx_rate_control *txrc)
+{
+       struct sk_buff *skb = txrc->skb;
+       struct ieee80211_supported_band *sband = txrc->sband;
+       struct iwl_op_mode *op_mode __maybe_unused =
+                       (struct iwl_op_mode *)mvm_r;
+       struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_lq_sta *lq_sta = mvm_sta;
+       int rate_idx;
+
+       IWL_DEBUG_RATE_LIMIT(mvm, "rate scale calculate new rate for skb\n");
+
+       /* Get max rate if user set max rate */
+       if (lq_sta) {
+               lq_sta->max_rate_idx = txrc->max_rate_idx;
+               if ((sband->band == IEEE80211_BAND_5GHZ) &&
+                   (lq_sta->max_rate_idx != -1))
+                       lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE;
+               if ((lq_sta->max_rate_idx < 0) ||
+                   (lq_sta->max_rate_idx >= IWL_RATE_COUNT))
+                       lq_sta->max_rate_idx = -1;
+       }
+
+       /* Treat uninitialized rate scaling data same as non-existing. */
+       if (lq_sta && !lq_sta->drv) {
+               IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
+               mvm_sta = NULL;
+       }
+
+       /* Send management frames and NO_ACK data using lowest rate. */
+       if (rate_control_send_low(sta, mvm_sta, txrc))
+               return;
+
+       rate_idx  = lq_sta->last_txrate_idx;
+
+       if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
+               rate_idx -= IWL_FIRST_OFDM_RATE;
+               /* 6M and 9M shared same MCS index */
+               rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
+               if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
+                   IWL_RATE_MIMO3_6M_PLCP)
+                       rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM);
+               else if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
+                        IWL_RATE_MIMO2_6M_PLCP)
+                       rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
+               info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
+               if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
+                       info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
+               if (lq_sta->last_rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */
+                       info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+               if (lq_sta->last_rate_n_flags & RATE_HT_MCS_GF_MSK)
+                       info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD;
+       } else {
+               /* Check for invalid rates */
+               if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) ||
+                   ((sband->band == IEEE80211_BAND_5GHZ) &&
+                    (rate_idx < IWL_FIRST_OFDM_RATE)))
+                       rate_idx = rate_lowest_index(sband, sta);
+               /* On valid 5 GHz rate, adjust index */
+               else if (sband->band == IEEE80211_BAND_5GHZ)
+                       rate_idx -= IWL_FIRST_OFDM_RATE;
+               info->control.rates[0].flags = 0;
+       }
+       info->control.rates[0].idx = rate_idx;
+}
+
+static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
+                         gfp_t gfp)
+{
+       struct iwl_mvm_sta *sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
+       struct iwl_op_mode *op_mode __maybe_unused =
+                       (struct iwl_op_mode *)mvm_rate;
+       struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+
+       IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
+
+       return &sta_priv->lq_sta;
+}
+
+/*
+ * Called after adding a new station to initialize rate scaling
+ */
+void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                         enum ieee80211_band band)
+{
+       int i, j;
+       struct ieee80211_hw *hw = mvm->hw;
+       struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
+       struct iwl_mvm_sta *sta_priv;
+       struct iwl_lq_sta *lq_sta;
+       struct ieee80211_supported_band *sband;
+       unsigned long supp; /* must be unsigned long for for_each_set_bit */
+
+       sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
+       lq_sta = &sta_priv->lq_sta;
+       sband = hw->wiphy->bands[band];
+
+       lq_sta->lq.sta_id = sta_priv->sta_id;
+
+       for (j = 0; j < LQ_SIZE; j++)
+               for (i = 0; i < IWL_RATE_COUNT; i++)
+                       rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
+
+       lq_sta->flush_timer = 0;
+       lq_sta->supp_rates = sta->supp_rates[sband->band];
+       for (j = 0; j < LQ_SIZE; j++)
+               for (i = 0; i < IWL_RATE_COUNT; i++)
+                       rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
+
+       IWL_DEBUG_RATE(mvm,
+                      "LQ: *** rate scale station global init for station %d ***\n",
+                      sta_priv->sta_id);
+       /* TODO: what is a good starting rate for STA? About middle? Maybe not
+        * the lowest or the highest rate.. Could consider using RSSI from
+        * previous packets? Need to have IEEE 802.1X auth succeed immediately
+        * after assoc.. */
+
+       lq_sta->max_rate_idx = -1;
+       lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
+       lq_sta->is_green = rs_use_green(sta);
+       lq_sta->band = sband->band;
+       /*
+        * active legacy rates as per supported rates bitmap
+        */
+       supp = sta->supp_rates[sband->band];
+       lq_sta->active_legacy_rate = 0;
+       for_each_set_bit(i, &supp, BITS_PER_LONG)
+               lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value);
+
+       /*
+        * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3),
+        * supp_rates[] does not; shift to convert format, force 9 MBits off.
+        */
+       lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1;
+       lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1;
+       lq_sta->active_siso_rate &= ~((u16)0x2);
+       lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE;
+
+       /* Same here */
+       lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1;
+       lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1;
+       lq_sta->active_mimo2_rate &= ~((u16)0x2);
+       lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
+
+       lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1;
+       lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1;
+       lq_sta->active_mimo3_rate &= ~((u16)0x2);
+       lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE;
+
+       IWL_DEBUG_RATE(mvm,
+                      "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n",
+                      lq_sta->active_siso_rate,
+                      lq_sta->active_mimo2_rate,
+                      lq_sta->active_mimo3_rate);
+
+       /* These values will be overridden later */
+       lq_sta->lq.single_stream_ant_msk =
+               first_antenna(mvm->nvm_data->valid_tx_ant);
+       lq_sta->lq.dual_stream_ant_msk =
+               mvm->nvm_data->valid_tx_ant &
+               ~first_antenna(mvm->nvm_data->valid_tx_ant);
+       if (!lq_sta->lq.dual_stream_ant_msk) {
+               lq_sta->lq.dual_stream_ant_msk = ANT_AB;
+       } else if (num_of_ant(mvm->nvm_data->valid_tx_ant) == 2) {
+               lq_sta->lq.dual_stream_ant_msk =
+                       mvm->nvm_data->valid_tx_ant;
+       }
+
+       /* as default allow aggregation for all tids */
+       lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
+       lq_sta->drv = mvm;
+
+       /* Set last_txrate_idx to lowest rate */
+       lq_sta->last_txrate_idx = rate_lowest_index(sband, sta);
+       if (sband->band == IEEE80211_BAND_5GHZ)
+               lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
+       lq_sta->is_agg = 0;
+#ifdef CONFIG_MAC80211_DEBUGFS
+       lq_sta->dbg_fixed_rate = 0;
+#endif
+
+       rs_initialize_lq(mvm, sta, lq_sta, band);
+}
+
+static void rs_fill_link_cmd(struct iwl_mvm *mvm,
+                            struct iwl_lq_sta *lq_sta, u32 new_rate)
+{
+       struct iwl_scale_tbl_info tbl_type;
+       int index = 0;
+       int rate_idx;
+       int repeat_rate = 0;
+       u8 ant_toggle_cnt = 0;
+       u8 use_ht_possible = 1;
+       u8 valid_tx_ant = 0;
+       struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+
+       /* Override starting rate (index 0) if needed for debug purposes */
+       rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+       /* Interpret new_rate (rate_n_flags) */
+       rs_get_tbl_info_from_mcs(new_rate, lq_sta->band,
+                                &tbl_type, &rate_idx);
+
+       /* How many times should we repeat the initial rate? */
+       if (is_legacy(tbl_type.lq_type)) {
+               ant_toggle_cnt = 1;
+               repeat_rate = IWL_NUMBER_TRY;
+       } else {
+               repeat_rate = min(IWL_HT_NUMBER_TRY,
+                                 LINK_QUAL_AGG_DISABLE_START_DEF - 1);
+       }
+
+       lq_cmd->mimo_delim = is_mimo(tbl_type.lq_type) ? 1 : 0;
+
+       /* Fill 1st table entry (index 0) */
+       lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
+
+       if (num_of_ant(tbl_type.ant_type) == 1)
+               lq_cmd->single_stream_ant_msk = tbl_type.ant_type;
+       else if (num_of_ant(tbl_type.ant_type) == 2)
+               lq_cmd->dual_stream_ant_msk = tbl_type.ant_type;
+       /* otherwise we don't modify the existing value */
+
+       index++;
+       repeat_rate--;
+       if (mvm)
+               valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+
+       /* Fill rest of rate table */
+       while (index < LINK_QUAL_MAX_RETRY_NUM) {
+               /* Repeat initial/next rate.
+                * For legacy IWL_NUMBER_TRY == 1, this loop will not execute.
+                * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */
+               while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) {
+                       if (is_legacy(tbl_type.lq_type)) {
+                               if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
+                                       ant_toggle_cnt++;
+                               else if (mvm &&
+                                        rs_toggle_antenna(valid_tx_ant,
+                                                       &new_rate, &tbl_type))
+                                       ant_toggle_cnt = 1;
+                       }
+
+                       /* Override next rate if needed for debug purposes */
+                       rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+                       /* Fill next table entry */
+                       lq_cmd->rs_table[index] =
+                                       cpu_to_le32(new_rate);
+                       repeat_rate--;
+                       index++;
+               }
+
+               rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type,
+                                        &rate_idx);
+
+
+               /* Indicate to uCode which entries might be MIMO.
+                * If initial rate was MIMO, this will finally end up
+                * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */
+               if (is_mimo(tbl_type.lq_type))
+                       lq_cmd->mimo_delim = index;
+
+               /* Get next rate */
+               new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx,
+                                            use_ht_possible);
+
+               /* How many times should we repeat the next rate? */
+               if (is_legacy(tbl_type.lq_type)) {
+                       if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
+                               ant_toggle_cnt++;
+                       else if (mvm &&
+                                rs_toggle_antenna(valid_tx_ant,
+                                                  &new_rate, &tbl_type))
+                               ant_toggle_cnt = 1;
+
+                       repeat_rate = IWL_NUMBER_TRY;
+               } else {
+                       repeat_rate = IWL_HT_NUMBER_TRY;
+               }
+
+               /* Don't allow HT rates after next pass.
+                * rs_get_lower_rate() will change type to LQ_A or LQ_G. */
+               use_ht_possible = 0;
+
+               /* Override next rate if needed for debug purposes */
+               rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+
+               /* Fill next table entry */
+               lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
+
+               index++;
+               repeat_rate--;
+       }
+
+       lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+       lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
+
+       lq_cmd->agg_time_limit =
+               cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
+}
+
+static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
+{
+       return hw->priv;
+}
+/* rate scale requires free function to be implemented */
+static void rs_free(void *mvm_rate)
+{
+       return;
+}
+
+static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
+                       void *mvm_sta)
+{
+       struct iwl_op_mode *op_mode __maybe_unused = mvm_r;
+       struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+
+       IWL_DEBUG_RATE(mvm, "enter\n");
+       IWL_DEBUG_RATE(mvm, "leave\n");
+}
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
+                            u32 *rate_n_flags, int index)
+{
+       struct iwl_mvm *mvm;
+       u8 valid_tx_ant;
+       u8 ant_sel_tx;
+
+       mvm = lq_sta->drv;
+       valid_tx_ant = mvm->nvm_data->valid_tx_ant;
+       if (lq_sta->dbg_fixed_rate) {
+               ant_sel_tx =
+                 ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
+                 >> RATE_MCS_ANT_POS);
+               if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) {
+                       *rate_n_flags = lq_sta->dbg_fixed_rate;
+                       IWL_DEBUG_RATE(mvm, "Fixed rate ON\n");
+               } else {
+                       lq_sta->dbg_fixed_rate = 0;
+                       IWL_ERR(mvm,
+                               "Invalid antenna selection 0x%X, Valid is 0x%X\n",
+                               ant_sel_tx, valid_tx_ant);
+                       IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n");
+               }
+       } else {
+               IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n");
+       }
+}
+
+static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
+                       const char __user *user_buf, size_t count, loff_t *ppos)
+{
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       struct iwl_mvm *mvm;
+       char buf[64];
+       size_t buf_size;
+       u32 parsed_rate;
+
+
+       mvm = lq_sta->drv;
+       memset(buf, 0, sizeof(buf));
+       buf_size = min(count, sizeof(buf) -  1);
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       if (sscanf(buf, "%x", &parsed_rate) == 1)
+               lq_sta->dbg_fixed_rate = parsed_rate;
+       else
+               lq_sta->dbg_fixed_rate = 0;
+
+       rs_program_fix_rate(mvm, lq_sta);
+
+       return count;
+}
+
+static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
+                       char __user *user_buf, size_t count, loff_t *ppos)
+{
+       char *buff;
+       int desc = 0;
+       int i = 0;
+       int index = 0;
+       ssize_t ret;
+
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       struct iwl_mvm *mvm;
+       struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
+
+       mvm = lq_sta->drv;
+       buff = kmalloc(1024, GFP_KERNEL);
+       if (!buff)
+               return -ENOMEM;
+
+       desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id);
+       desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n",
+                       lq_sta->total_failed, lq_sta->total_success,
+                       lq_sta->active_legacy_rate);
+       desc += sprintf(buff+desc, "fixed rate 0x%X\n",
+                       lq_sta->dbg_fixed_rate);
+       desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
+           (mvm->nvm_data->valid_tx_ant & ANT_A) ? "ANT_A," : "",
+           (mvm->nvm_data->valid_tx_ant & ANT_B) ? "ANT_B," : "",
+           (mvm->nvm_data->valid_tx_ant & ANT_C) ? "ANT_C" : "");
+       desc += sprintf(buff+desc, "lq type %s\n",
+          (is_legacy(tbl->lq_type)) ? "legacy" : "HT");
+       if (is_Ht(tbl->lq_type)) {
+               desc += sprintf(buff+desc, " %s",
+                  (is_siso(tbl->lq_type)) ? "SISO" :
+                  ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
+                  desc += sprintf(buff+desc, " %s",
+                  (tbl->is_ht40) ? "40MHz" : "20MHz");
+                  desc += sprintf(buff+desc, " %s %s %s\n",
+                                  (tbl->is_SGI) ? "SGI" : "",
+                  (lq_sta->is_green) ? "GF enabled" : "",
+                  (lq_sta->is_agg) ? "AGG on" : "");
+       }
+       desc += sprintf(buff+desc, "last tx rate=0x%X\n",
+                       lq_sta->last_rate_n_flags);
+       desc += sprintf(buff+desc,
+                       "general: flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n",
+                       lq_sta->lq.flags,
+                       lq_sta->lq.mimo_delim,
+                       lq_sta->lq.single_stream_ant_msk,
+                       lq_sta->lq.dual_stream_ant_msk);
+
+       desc += sprintf(buff+desc,
+                       "agg: time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n",
+                       le16_to_cpu(lq_sta->lq.agg_time_limit),
+                       lq_sta->lq.agg_disable_start_th,
+                       lq_sta->lq.agg_frame_cnt_limit);
+
+       desc += sprintf(buff+desc,
+                       "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
+                       lq_sta->lq.initial_rate_index[0],
+                       lq_sta->lq.initial_rate_index[1],
+                       lq_sta->lq.initial_rate_index[2],
+                       lq_sta->lq.initial_rate_index[3]);
+
+       for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+               index = iwl_hwrate_to_plcp_idx(
+                       le32_to_cpu(lq_sta->lq.rs_table[i]));
+               if (is_legacy(tbl->lq_type)) {
+                       desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n",
+                                       i, le32_to_cpu(lq_sta->lq.rs_table[i]),
+                                       iwl_rate_mcs[index].mbps);
+               } else {
+                       desc += sprintf(buff+desc,
+                                       " rate[%d] 0x%X %smbps (%s)\n",
+                                       i, le32_to_cpu(lq_sta->lq.rs_table[i]),
+                                       iwl_rate_mcs[index].mbps,
+                                       iwl_rate_mcs[index].mcs);
+               }
+       }
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+       kfree(buff);
+       return ret;
+}
+
+static const struct file_operations rs_sta_dbgfs_scale_table_ops = {
+       .write = rs_sta_dbgfs_scale_table_write,
+       .read = rs_sta_dbgfs_scale_table_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
+                       char __user *user_buf, size_t count, loff_t *ppos)
+{
+       char *buff;
+       int desc = 0;
+       int i, j;
+       ssize_t ret;
+
+       struct iwl_lq_sta *lq_sta = file->private_data;
+
+       buff = kmalloc(1024, GFP_KERNEL);
+       if (!buff)
+               return -ENOMEM;
+
+       for (i = 0; i < LQ_SIZE; i++) {
+               desc += sprintf(buff+desc,
+                               "%s type=%d SGI=%d HT40=%d DUP=0 GF=%d\n"
+                               "rate=0x%X\n",
+                               lq_sta->active_tbl == i ? "*" : "x",
+                               lq_sta->lq_info[i].lq_type,
+                               lq_sta->lq_info[i].is_SGI,
+                               lq_sta->lq_info[i].is_ht40,
+                               lq_sta->is_green,
+                               lq_sta->lq_info[i].current_rate);
+               for (j = 0; j < IWL_RATE_COUNT; j++) {
+                       desc += sprintf(buff+desc,
+                               "counter=%d success=%d %%=%d\n",
+                               lq_sta->lq_info[i].win[j].counter,
+                               lq_sta->lq_info[i].win[j].success_counter,
+                               lq_sta->lq_info[i].win[j].success_ratio);
+               }
+       }
+       ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+       kfree(buff);
+       return ret;
+}
+
+static const struct file_operations rs_sta_dbgfs_stats_table_ops = {
+       .read = rs_sta_dbgfs_stats_table_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
+static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file,
+                       char __user *user_buf, size_t count, loff_t *ppos)
+{
+       struct iwl_lq_sta *lq_sta = file->private_data;
+       struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl];
+       char buff[120];
+       int desc = 0;
+
+       if (is_Ht(tbl->lq_type))
+               desc += sprintf(buff+desc,
+                               "Bit Rate= %d Mb/s\n",
+                               tbl->expected_tpt[lq_sta->last_txrate_idx]);
+       else
+               desc += sprintf(buff+desc,
+                               "Bit Rate= %d Mb/s\n",
+                               iwl_rates[lq_sta->last_txrate_idx].ieee >> 1);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buff, desc);
+}
+
+static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = {
+       .read = rs_sta_dbgfs_rate_scale_data_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
+static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
+{
+       struct iwl_lq_sta *lq_sta = mvm_sta;
+       lq_sta->rs_sta_dbgfs_scale_table_file =
+               debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+                                   lq_sta, &rs_sta_dbgfs_scale_table_ops);
+       lq_sta->rs_sta_dbgfs_stats_table_file =
+               debugfs_create_file("rate_stats_table", S_IRUSR, dir,
+                                   lq_sta, &rs_sta_dbgfs_stats_table_ops);
+       lq_sta->rs_sta_dbgfs_rate_scale_data_file =
+               debugfs_create_file("rate_scale_data", S_IRUSR, dir,
+                                   lq_sta, &rs_sta_dbgfs_rate_scale_data_ops);
+       lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
+               debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+                                 &lq_sta->tx_agg_tid_en);
+}
+
+static void rs_remove_debugfs(void *mvm, void *mvm_sta)
+{
+       struct iwl_lq_sta *lq_sta = mvm_sta;
+       debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file);
+       debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
+}
+#endif
+
+/*
+ * Initialization of rate scaling information is done by driver after
+ * the station is added. Since mac80211 calls this function before a
+ * station is added we ignore it.
+ */
+static void rs_rate_init_stub(void *mvm_r,
+                                struct ieee80211_supported_band *sband,
+                                struct ieee80211_sta *sta, void *mvm_sta)
+{
+}
+static struct rate_control_ops rs_mvm_ops = {
+       .module = NULL,
+       .name = RS_NAME,
+       .tx_status = rs_tx_status,
+       .get_rate = rs_get_rate,
+       .rate_init = rs_rate_init_stub,
+       .alloc = rs_alloc,
+       .free = rs_free,
+       .alloc_sta = rs_alloc_sta,
+       .free_sta = rs_free_sta,
+#ifdef CONFIG_MAC80211_DEBUGFS
+       .add_sta_debugfs = rs_add_debugfs,
+       .remove_sta_debugfs = rs_remove_debugfs,
+#endif
+};
+
+int iwl_mvm_rate_control_register(void)
+{
+       return ieee80211_rate_control_register(&rs_mvm_ops);
+}
+
+void iwl_mvm_rate_control_unregister(void)
+{
+       ieee80211_rate_control_unregister(&rs_mvm_ops);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
new file mode 100644 (file)
index 0000000..219c685
--- /dev/null
@@ -0,0 +1,393 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#ifndef __rs_h__
+#define __rs_h__
+
+#include <net/mac80211.h>
+
+#include "iwl-config.h"
+
+#include "fw-api.h"
+#include "iwl-trans.h"
+
+struct iwl_rs_rate_info {
+       u8 plcp;        /* uCode API:  IWL_RATE_6M_PLCP, etc. */
+       u8 plcp_siso;   /* uCode API:  IWL_RATE_SISO_6M_PLCP, etc. */
+       u8 plcp_mimo2;  /* uCode API:  IWL_RATE_MIMO2_6M_PLCP, etc. */
+       u8 plcp_mimo3;  /* uCode API:  IWL_RATE_MIMO3_6M_PLCP, etc. */
+       u8 ieee;        /* MAC header:  IWL_RATE_6M_IEEE, etc. */
+       u8 prev_ieee;    /* previous rate in IEEE speeds */
+       u8 next_ieee;    /* next rate in IEEE speeds */
+       u8 prev_rs;      /* previous rate used in rs algo */
+       u8 next_rs;      /* next rate used in rs algo */
+       u8 prev_rs_tgg;  /* previous rate used in TGG rs algo */
+       u8 next_rs_tgg;  /* next rate used in TGG rs algo */
+};
+
+#define IWL_RATE_60M_PLCP 3
+
+enum {
+       IWL_RATE_INVM_INDEX = IWL_RATE_COUNT,
+       IWL_RATE_INVALID = IWL_RATE_COUNT,
+};
+
+#define LINK_QUAL_MAX_RETRY_NUM 16
+
+enum {
+       IWL_RATE_6M_INDEX_TABLE = 0,
+       IWL_RATE_9M_INDEX_TABLE,
+       IWL_RATE_12M_INDEX_TABLE,
+       IWL_RATE_18M_INDEX_TABLE,
+       IWL_RATE_24M_INDEX_TABLE,
+       IWL_RATE_36M_INDEX_TABLE,
+       IWL_RATE_48M_INDEX_TABLE,
+       IWL_RATE_54M_INDEX_TABLE,
+       IWL_RATE_1M_INDEX_TABLE,
+       IWL_RATE_2M_INDEX_TABLE,
+       IWL_RATE_5M_INDEX_TABLE,
+       IWL_RATE_11M_INDEX_TABLE,
+       IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1,
+};
+
+/* #define vs. enum to keep from defaulting to 'large integer' */
+#define        IWL_RATE_6M_MASK   (1 << IWL_RATE_6M_INDEX)
+#define        IWL_RATE_9M_MASK   (1 << IWL_RATE_9M_INDEX)
+#define        IWL_RATE_12M_MASK  (1 << IWL_RATE_12M_INDEX)
+#define        IWL_RATE_18M_MASK  (1 << IWL_RATE_18M_INDEX)
+#define        IWL_RATE_24M_MASK  (1 << IWL_RATE_24M_INDEX)
+#define        IWL_RATE_36M_MASK  (1 << IWL_RATE_36M_INDEX)
+#define        IWL_RATE_48M_MASK  (1 << IWL_RATE_48M_INDEX)
+#define        IWL_RATE_54M_MASK  (1 << IWL_RATE_54M_INDEX)
+#define IWL_RATE_60M_MASK  (1 << IWL_RATE_60M_INDEX)
+#define        IWL_RATE_1M_MASK   (1 << IWL_RATE_1M_INDEX)
+#define        IWL_RATE_2M_MASK   (1 << IWL_RATE_2M_INDEX)
+#define        IWL_RATE_5M_MASK   (1 << IWL_RATE_5M_INDEX)
+#define        IWL_RATE_11M_MASK  (1 << IWL_RATE_11M_INDEX)
+
+
+/* uCode API values for OFDM high-throughput (HT) bit rates */
+enum {
+       IWL_RATE_SISO_6M_PLCP = 0,
+       IWL_RATE_SISO_12M_PLCP = 1,
+       IWL_RATE_SISO_18M_PLCP = 2,
+       IWL_RATE_SISO_24M_PLCP = 3,
+       IWL_RATE_SISO_36M_PLCP = 4,
+       IWL_RATE_SISO_48M_PLCP = 5,
+       IWL_RATE_SISO_54M_PLCP = 6,
+       IWL_RATE_SISO_60M_PLCP = 7,
+       IWL_RATE_MIMO2_6M_PLCP  = 0x8,
+       IWL_RATE_MIMO2_12M_PLCP = 0x9,
+       IWL_RATE_MIMO2_18M_PLCP = 0xa,
+       IWL_RATE_MIMO2_24M_PLCP = 0xb,
+       IWL_RATE_MIMO2_36M_PLCP = 0xc,
+       IWL_RATE_MIMO2_48M_PLCP = 0xd,
+       IWL_RATE_MIMO2_54M_PLCP = 0xe,
+       IWL_RATE_MIMO2_60M_PLCP = 0xf,
+       IWL_RATE_MIMO3_6M_PLCP  = 0x10,
+       IWL_RATE_MIMO3_12M_PLCP = 0x11,
+       IWL_RATE_MIMO3_18M_PLCP = 0x12,
+       IWL_RATE_MIMO3_24M_PLCP = 0x13,
+       IWL_RATE_MIMO3_36M_PLCP = 0x14,
+       IWL_RATE_MIMO3_48M_PLCP = 0x15,
+       IWL_RATE_MIMO3_54M_PLCP = 0x16,
+       IWL_RATE_MIMO3_60M_PLCP = 0x17,
+       IWL_RATE_SISO_INVM_PLCP,
+       IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+       IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
+};
+
+/* MAC header values for bit rates */
+enum {
+       IWL_RATE_6M_IEEE  = 12,
+       IWL_RATE_9M_IEEE  = 18,
+       IWL_RATE_12M_IEEE = 24,
+       IWL_RATE_18M_IEEE = 36,
+       IWL_RATE_24M_IEEE = 48,
+       IWL_RATE_36M_IEEE = 72,
+       IWL_RATE_48M_IEEE = 96,
+       IWL_RATE_54M_IEEE = 108,
+       IWL_RATE_60M_IEEE = 120,
+       IWL_RATE_1M_IEEE  = 2,
+       IWL_RATE_2M_IEEE  = 4,
+       IWL_RATE_5M_IEEE  = 11,
+       IWL_RATE_11M_IEEE = 22,
+};
+
+#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
+
+#define IWL_INVALID_VALUE    -1
+
+#define IWL_MIN_RSSI_VAL                 -100
+#define IWL_MAX_RSSI_VAL                    0
+
+/* These values specify how many Tx frame attempts before
+ * searching for a new modulation mode */
+#define IWL_LEGACY_FAILURE_LIMIT       160
+#define IWL_LEGACY_SUCCESS_LIMIT       480
+#define IWL_LEGACY_TABLE_COUNT         160
+
+#define IWL_NONE_LEGACY_FAILURE_LIMIT  400
+#define IWL_NONE_LEGACY_SUCCESS_LIMIT  4500
+#define IWL_NONE_LEGACY_TABLE_COUNT    1500
+
+/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */
+#define IWL_RS_GOOD_RATIO              12800   /* 100% */
+#define IWL_RATE_SCALE_SWITCH          10880   /*  85% */
+#define IWL_RATE_HIGH_TH               10880   /*  85% */
+#define IWL_RATE_INCREASE_TH           6400    /*  50% */
+#define IWL_RATE_DECREASE_TH           1920    /*  15% */
+
+/* possible actions when in legacy mode */
+#define IWL_LEGACY_SWITCH_ANTENNA1      0
+#define IWL_LEGACY_SWITCH_ANTENNA2      1
+#define IWL_LEGACY_SWITCH_SISO          2
+#define IWL_LEGACY_SWITCH_MIMO2_AB      3
+#define IWL_LEGACY_SWITCH_MIMO2_AC      4
+#define IWL_LEGACY_SWITCH_MIMO2_BC      5
+#define IWL_LEGACY_SWITCH_MIMO3_ABC     6
+
+/* possible actions when in siso mode */
+#define IWL_SISO_SWITCH_ANTENNA1        0
+#define IWL_SISO_SWITCH_ANTENNA2        1
+#define IWL_SISO_SWITCH_MIMO2_AB        2
+#define IWL_SISO_SWITCH_MIMO2_AC        3
+#define IWL_SISO_SWITCH_MIMO2_BC        4
+#define IWL_SISO_SWITCH_GI              5
+#define IWL_SISO_SWITCH_MIMO3_ABC       6
+
+
+/* possible actions when in mimo mode */
+#define IWL_MIMO2_SWITCH_ANTENNA1       0
+#define IWL_MIMO2_SWITCH_ANTENNA2       1
+#define IWL_MIMO2_SWITCH_SISO_A         2
+#define IWL_MIMO2_SWITCH_SISO_B         3
+#define IWL_MIMO2_SWITCH_SISO_C         4
+#define IWL_MIMO2_SWITCH_GI             5
+#define IWL_MIMO2_SWITCH_MIMO3_ABC      6
+
+
+/* possible actions when in mimo3 mode */
+#define IWL_MIMO3_SWITCH_ANTENNA1       0
+#define IWL_MIMO3_SWITCH_ANTENNA2       1
+#define IWL_MIMO3_SWITCH_SISO_A         2
+#define IWL_MIMO3_SWITCH_SISO_B         3
+#define IWL_MIMO3_SWITCH_SISO_C         4
+#define IWL_MIMO3_SWITCH_MIMO2_AB       5
+#define IWL_MIMO3_SWITCH_MIMO2_AC       6
+#define IWL_MIMO3_SWITCH_MIMO2_BC       7
+#define IWL_MIMO3_SWITCH_GI             8
+
+
+#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI
+#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC
+
+/*FIXME:RS:add possible actions for MIMO3*/
+
+#define IWL_ACTION_LIMIT               3       /* # possible actions */
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF   (4000) /* 4 milliseconds */
+#define LINK_QUAL_AGG_TIME_LIMIT_MAX   (8000)
+#define LINK_QUAL_AGG_TIME_LIMIT_MIN   (100)
+
+#define LINK_QUAL_AGG_DISABLE_START_DEF        (3)
+#define LINK_QUAL_AGG_DISABLE_START_MAX        (255)
+#define LINK_QUAL_AGG_DISABLE_START_MIN        (0)
+
+#define LINK_QUAL_AGG_FRAME_LIMIT_DEF  (63)
+#define LINK_QUAL_AGG_FRAME_LIMIT_MAX  (63)
+#define LINK_QUAL_AGG_FRAME_LIMIT_MIN  (0)
+
+#define LQ_SIZE                2       /* 2 mode tables:  "Active" and "Search" */
+
+/* load per tid defines for A-MPDU activation */
+#define IWL_AGG_TPT_THREHOLD   0
+#define IWL_AGG_LOAD_THRESHOLD 10
+#define IWL_AGG_ALL_TID                0xff
+#define TID_QUEUE_CELL_SPACING 50      /*mS */
+#define TID_QUEUE_MAX_SIZE     20
+#define TID_ROUND_VALUE                5       /* mS */
+
+#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING)
+#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y))
+
+enum iwl_table_type {
+       LQ_NONE,
+       LQ_G,           /* legacy types */
+       LQ_A,
+       LQ_SISO,        /* high-throughput types */
+       LQ_MIMO2,
+       LQ_MIMO3,
+       LQ_MAX,
+};
+
+#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A))
+#define is_siso(tbl) ((tbl) == LQ_SISO)
+#define is_mimo2(tbl) ((tbl) == LQ_MIMO2)
+#define is_mimo3(tbl) ((tbl) == LQ_MIMO3)
+#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl))
+#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl))
+#define is_a_band(tbl) ((tbl) == LQ_A)
+#define is_g_and(tbl) ((tbl) == LQ_G)
+
+#define IWL_MAX_MCS_DISPLAY_SIZE       12
+
+struct iwl_rate_mcs_info {
+       char    mbps[IWL_MAX_MCS_DISPLAY_SIZE];
+       char    mcs[IWL_MAX_MCS_DISPLAY_SIZE];
+};
+
+/**
+ * struct iwl_rate_scale_data -- tx success history for one rate
+ */
+struct iwl_rate_scale_data {
+       u64 data;               /* bitmap of successful frames */
+       s32 success_counter;    /* number of frames successful */
+       s32 success_ratio;      /* per-cent * 128  */
+       s32 counter;            /* number of frames attempted */
+       s32 average_tpt;        /* success ratio * expected throughput */
+       unsigned long stamp;
+};
+
+/**
+ * struct iwl_scale_tbl_info -- tx params and success history for all rates
+ *
+ * There are two of these in struct iwl_lq_sta,
+ * one for "active", and one for "search".
+ */
+struct iwl_scale_tbl_info {
+       enum iwl_table_type lq_type;
+       u8 ant_type;
+       u8 is_SGI;      /* 1 = short guard interval */
+       u8 is_ht40;     /* 1 = 40 MHz channel width */
+       u8 action;      /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */
+       u8 max_search;  /* maximun number of tables we can search */
+       s32 *expected_tpt;      /* throughput metrics; expected_tpt_G, etc. */
+       u32 current_rate;  /* rate_n_flags, uCode API format */
+       struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
+};
+
+struct iwl_traffic_load {
+       unsigned long time_stamp;       /* age of the oldest statistics */
+       u32 packet_count[TID_QUEUE_MAX_SIZE];   /* packet count in this time
+                                                * slice */
+       u32 total;                      /* total num of packets during the
+                                        * last TID_MAX_TIME_DIFF */
+       u8 queue_count;                 /* number of queues that has
+                                        * been used since the last cleanup */
+       u8 head;                        /* start of the circular buffer */
+};
+
+/**
+ * struct iwl_lq_sta -- driver's rate scaling private structure
+ *
+ * Pointer to this gets passed back and forth between driver and mac80211.
+ */
+struct iwl_lq_sta {
+       u8 active_tbl;          /* index of active table, range 0-1 */
+       u8 enable_counter;      /* indicates HT mode */
+       u8 stay_in_tbl;         /* 1: disallow, 0: allow search for new mode */
+       u8 search_better_tbl;   /* 1: currently trying alternate mode */
+       s32 last_tpt;
+
+       /* The following determine when to search for a new mode */
+       u32 table_count_limit;
+       u32 max_failure_limit;  /* # failed frames before new search */
+       u32 max_success_limit;  /* # successful frames before new search */
+       u32 table_count;
+       u32 total_failed;       /* total failed frames, any/all rates */
+       u32 total_success;      /* total successful frames, any/all rates */
+       u64 flush_timer;        /* time staying in mode before new search */
+
+       u8 action_counter;      /* # mode-switch actions tried */
+       u8 is_green;
+       enum ieee80211_band band;
+
+       /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
+       u32 supp_rates;
+       u16 active_legacy_rate;
+       u16 active_siso_rate;
+       u16 active_mimo2_rate;
+       u16 active_mimo3_rate;
+       s8 max_rate_idx;     /* Max rate set by user */
+       u8 missed_rate_counter;
+
+       struct iwl_lq_cmd lq;
+       struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
+       struct iwl_traffic_load load[IWL_MAX_TID_COUNT];
+       u8 tx_agg_tid_en;
+#ifdef CONFIG_MAC80211_DEBUGFS
+       struct dentry *rs_sta_dbgfs_scale_table_file;
+       struct dentry *rs_sta_dbgfs_stats_table_file;
+       struct dentry *rs_sta_dbgfs_rate_scale_data_file;
+       struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
+       u32 dbg_fixed_rate;
+#endif
+       struct iwl_mvm *drv;
+
+       /* used to be in sta_info */
+       int last_txrate_idx;
+       /* last tx rate_n_flags */
+       u32 last_rate_n_flags;
+       /* packets destined for this STA are aggregated */
+       u8 is_agg;
+       /* BT traffic this sta was last updated in */
+       u8 last_bt_traffic;
+};
+
+static inline u8 num_of_ant(u8 mask)
+{
+       return  !!((mask) & ANT_A) +
+               !!((mask) & ANT_B) +
+               !!((mask) & ANT_C);
+}
+
+/* Initialize station's rate scaling information after adding station */
+extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm,
+                                struct ieee80211_sta *sta,
+                                enum ieee80211_band band);
+
+/**
+ * iwl_rate_control_register - Register the rate control algorithm callbacks
+ *
+ * Since the rate control algorithm is hardware specific, there is no need
+ * or reason to place it as a stand alone module.  The driver can call
+ * iwl_rate_control_register in order to register the rate control callbacks
+ * with the mac80211 subsystem.  This should be performed prior to calling
+ * ieee80211_register_hw
+ *
+ */
+extern int iwl_mvm_rate_control_register(void);
+
+/**
+ * iwl_rate_control_unregister - Unregister the rate control callbacks
+ *
+ * This should be called after calling ieee80211_unregister_hw, but before
+ * the driver is unloaded.
+ */
+extern void iwl_mvm_rate_control_unregister(void);
+
+#endif /* __rs__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
new file mode 100644 (file)
index 0000000..52da375
--- /dev/null
@@ -0,0 +1,355 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+#include "iwl-trans.h"
+
+#include "mvm.h"
+#include "fw-api.h"
+
+/*
+ * iwl_mvm_rx_rx_phy_cmd - REPLY_RX_PHY_CMD handler
+ *
+ * Copies the phy information in mvm->last_phy_info, it will be used when the
+ * actual data will come from the fw in the next packet.
+ */
+int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+
+       memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info));
+       mvm->ampdu_ref++;
+       return 0;
+}
+
+/*
+ * iwl_mvm_pass_packet_to_mac80211 - builds the packet for mac80211
+ *
+ * Adds the rxb to a new skb and give it to mac80211
+ */
+static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+                                           struct ieee80211_hdr *hdr, u16 len,
+                                           u32 ampdu_status,
+                                           struct iwl_rx_cmd_buffer *rxb,
+                                           struct ieee80211_rx_status *stats)
+{
+       struct sk_buff *skb;
+       unsigned int hdrlen, fraglen;
+
+       /* Dont use dev_alloc_skb(), we'll have enough headroom once
+        * ieee80211_hdr pulled.
+        */
+       skb = alloc_skb(128, GFP_ATOMIC);
+       if (!skb) {
+               IWL_ERR(mvm, "alloc_skb failed\n");
+               return;
+       }
+       /* If frame is small enough to fit in skb->head, pull it completely.
+        * If not, only pull ieee80211_hdr so that splice() or TCP coalesce
+        * are more efficient.
+        */
+       hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
+
+       memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
+       fraglen = len - hdrlen;
+
+       if (fraglen) {
+               int offset = (void *)hdr + hdrlen -
+                            rxb_addr(rxb) + rxb_offset(rxb);
+
+               skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
+                               fraglen, rxb->truesize);
+       }
+
+       memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
+
+       ieee80211_rx(mvm->hw, skb);
+}
+
+/*
+ * iwl_mvm_calc_rssi - calculate the rssi in dBm
+ * @phy_info: the phy information for the coming packet
+ */
+static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
+                            struct iwl_rx_phy_info *phy_info)
+{
+       u32 rssi_a, rssi_b, rssi_c, max_rssi, agc_db;
+       u32 val;
+
+       /* Find max rssi among 3 possible receivers.
+        * These values are measured by the Digital Signal Processor (DSP).
+        * They should stay fairly constant even as the signal strength varies,
+        * if the radio's Automatic Gain Control (AGC) is working right.
+        * AGC value (see below) will provide the "interesting" info.
+        */
+       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]);
+       rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS;
+       rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS;
+       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_C_IDX]);
+       rssi_c = (val & IWL_OFDM_RSSI_INBAND_C_MSK) >> IWL_OFDM_RSSI_C_POS;
+
+       val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]);
+       agc_db = (val & IWL_OFDM_AGC_DB_MSK) >> IWL_OFDM_AGC_DB_POS;
+
+       max_rssi = max_t(u32, rssi_a, rssi_b);
+       max_rssi = max_t(u32, max_rssi, rssi_c);
+
+       IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d C %d Max %d AGC dB %d\n",
+                       rssi_a, rssi_b, rssi_c, max_rssi, agc_db);
+
+       /* dBm = max_rssi dB - agc dB - constant.
+        * Higher AGC (higher radio gain) means lower signal. */
+       return max_rssi - agc_db - IWL_RSSI_OFFSET;
+}
+
+/*
+ * iwl_mvm_set_mac80211_rx_flag - translate fw status to mac80211 format
+ * @mvm: the mvm object
+ * @hdr: 80211 header
+ * @stats: status in mac80211's format
+ * @rx_pkt_status: status coming from fw
+ *
+ * returns non 0 value if the packet should be dropped
+ */
+static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
+                                       struct ieee80211_hdr *hdr,
+                                       struct ieee80211_rx_status *stats,
+                                       u32 rx_pkt_status)
+{
+       if (!ieee80211_has_protected(hdr->frame_control) ||
+           (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+                            RX_MPDU_RES_STATUS_SEC_NO_ENC)
+               return 0;
+
+       /* packet was encrypted with unknown alg */
+       if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+                                       RX_MPDU_RES_STATUS_SEC_ENC_ERR)
+               return 0;
+
+       switch (rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) {
+       case RX_MPDU_RES_STATUS_SEC_CCM_ENC:
+               /* alg is CCM: check MIC only */
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
+                       return -1;
+
+               stats->flag |= RX_FLAG_DECRYPTED;
+               IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
+               return 0;
+
+       case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
+               /* Don't drop the frame and decrypt it in SW */
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
+                       return 0;
+               /* fall through if TTAK OK */
+
+       case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK))
+                       return -1;
+
+               stats->flag |= RX_FLAG_DECRYPTED;
+               return 0;
+
+       default:
+               IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
+       }
+
+       return 0;
+}
+
+/*
+ * iwl_mvm_rx_rx_mpdu - REPLY_RX_MPDU_CMD handler
+ *
+ * Handles the actual data of the Rx packet from the fw
+ */
+int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                      struct iwl_device_cmd *cmd)
+{
+       struct ieee80211_hdr *hdr;
+       struct ieee80211_rx_status rx_status = {};
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_rx_phy_info *phy_info;
+       struct iwl_rx_mpdu_res_start *rx_res;
+       u32 len;
+       u32 ampdu_status;
+       u32 rate_n_flags;
+       u32 rx_pkt_status;
+
+       phy_info = &mvm->last_phy_info;
+       rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
+       hdr = (struct ieee80211_hdr *)(pkt->data + sizeof(*rx_res));
+       len = le16_to_cpu(rx_res->byte_count);
+       rx_pkt_status = le32_to_cpup((__le32 *)
+               (pkt->data + sizeof(*rx_res) + len));
+
+       memset(&rx_status, 0, sizeof(rx_status));
+
+       /*
+        * drop the packet if it has failed being decrypted by HW
+        */
+       if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
+               IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
+                              rx_pkt_status);
+               return 0;
+       }
+
+       if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
+               IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
+                              phy_info->cfg_phy_cnt);
+               return 0;
+       }
+
+       if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
+           !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
+               IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
+               return 0;
+       }
+
+       /* This will be used in several places later */
+       rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
+
+       /* rx_status carries information about the packet to mac80211 */
+       rx_status.mactime = le64_to_cpu(phy_info->timestamp);
+       rx_status.band =
+               (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
+                               IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+       rx_status.freq =
+               ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
+                                              rx_status.band);
+       /*
+        * TSF as indicated by the fw is at INA time, but mac80211 expects the
+        * TSF at the beginning of the MPDU.
+        */
+       /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
+
+       /* Find max signal strength (dBm) among 3 antenna/receiver chains */
+       rx_status.signal = iwl_mvm_calc_rssi(mvm, phy_info);
+
+       IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
+                             (unsigned long long)rx_status.mactime);
+
+       /*
+        * "antenna number"
+        *
+        * It seems that the antenna field in the phy flags value
+        * is actually a bit field. This is undefined by radiotap,
+        * it wants an actual antenna number but I always get "7"
+        * for most legacy frames I receive indicating that the
+        * same frame was received on all three RX chains.
+        *
+        * I think this field should be removed in favor of a
+        * new 802.11n radiotap field "RX chains" that is defined
+        * as a bitmask.
+        */
+       rx_status.antenna = (le16_to_cpu(phy_info->phy_flags) &
+                               RX_RES_PHY_FLAGS_ANTENNA)
+                               >> RX_RES_PHY_FLAGS_ANTENNA_POS;
+
+       /* set the preamble flag if appropriate */
+       if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
+               rx_status.flag |= RX_FLAG_SHORTPRE;
+
+       if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
+               /*
+                * We know which subframes of an A-MPDU belong
+                * together since we get a single PHY response
+                * from the firmware for all of them
+                */
+               rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
+               rx_status.ampdu_reference = mvm->ampdu_ref;
+       }
+
+       /* Set up the HT phy flags */
+       switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+       case RATE_MCS_CHAN_WIDTH_20:
+               break;
+       case RATE_MCS_CHAN_WIDTH_40:
+               rx_status.flag |= RX_FLAG_40MHZ;
+               break;
+       case RATE_MCS_CHAN_WIDTH_80:
+               rx_status.flag |= RX_FLAG_80MHZ;
+               break;
+       case RATE_MCS_CHAN_WIDTH_160:
+               rx_status.flag |= RX_FLAG_160MHZ;
+               break;
+       }
+       if (rate_n_flags & RATE_MCS_SGI_MSK)
+               rx_status.flag |= RX_FLAG_SHORT_GI;
+       if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+               rx_status.flag |= RX_FLAG_HT_GF;
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               rx_status.flag |= RX_FLAG_HT;
+               rx_status.rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+       } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+               rx_status.vht_nss =
+                       ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+                                               RATE_VHT_MCS_NSS_POS) + 1;
+               rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+               rx_status.flag |= RX_FLAG_VHT;
+       } else {
+               rx_status.rate_idx =
+                       iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+                                                           rx_status.band);
+       }
+
+       iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status,
+                                       rxb, &rx_status);
+       return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
new file mode 100644 (file)
index 0000000..406c53a
--- /dev/null
@@ -0,0 +1,437 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "mvm.h"
+#include "iwl-eeprom-parse.h"
+#include "fw-api-scan.h"
+
+#define IWL_PLCP_QUIET_THRESH 1
+#define IWL_ACTIVE_QUIET_TIME 10
+
+static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
+{
+       u16 rx_chain;
+       u8 rx_ant = mvm->nvm_data->valid_rx_ant;
+
+       rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
+       rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
+       rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
+       rx_chain |= 0x1 << PHY_RX_CHAIN_DRIVER_FORCE_POS;
+       return cpu_to_le16(rx_chain);
+}
+
+static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif)
+{
+       if (vif->bss_conf.assoc)
+               return cpu_to_le32(200 * 1024);
+       else
+               return 0;
+}
+
+static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif)
+{
+       if (vif->bss_conf.assoc)
+               return cpu_to_le32(vif->bss_conf.beacon_int);
+       else
+               return 0;
+}
+
+static inline __le32
+iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req)
+{
+       if (req->channels[0]->band == IEEE80211_BAND_2GHZ)
+               return cpu_to_le32(PHY_BAND_24);
+       else
+               return cpu_to_le32(PHY_BAND_5);
+}
+
+static inline __le32
+iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
+                         bool no_cck)
+{
+       u32 tx_ant;
+
+       mvm->scan_last_antenna_idx =
+               iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+                                    mvm->scan_last_antenna_idx);
+       tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS;
+
+       if (band == IEEE80211_BAND_2GHZ && !no_cck)
+               return cpu_to_le32(IWL_RATE_1M_PLCP | RATE_MCS_CCK_MSK |
+                                  tx_ant);
+       else
+               return cpu_to_le32(IWL_RATE_6M_PLCP | tx_ant);
+}
+
+/*
+ * We insert the SSIDs in an inverted order, because the FW will
+ * invert it back. The most prioritized SSID, which is first in the
+ * request list, is not copied here, but inserted directly to the probe
+ * request.
+ */
+static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
+                                   struct cfg80211_scan_request *req)
+{
+       int fw_idx, req_idx;
+
+       fw_idx = 0;
+       for (req_idx = req->n_ssids - 1; req_idx > 0; req_idx--) {
+               cmd->direct_scan[fw_idx].id = WLAN_EID_SSID;
+               cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len;
+               memcpy(cmd->direct_scan[fw_idx].ssid,
+                      req->ssids[req_idx].ssid,
+                      req->ssids[req_idx].ssid_len);
+       }
+}
+
+/*
+ * If req->n_ssids > 0, it means we should do an active scan.
+ * In case of active scan w/o directed scan, we receive a zero-length SSID
+ * just to notify that this scan is active and not passive.
+ * In order to notify the FW of the number of SSIDs we wish to scan (including
+ * the zero-length one), we need to set the corresponding bits in chan->type,
+ * one for each SSID, and set the active bit (first).
+ */
+static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids)
+{
+       if (band == IEEE80211_BAND_2GHZ)
+               return 30  + 3 * (n_ssids + 1);
+       return 20  + 2 * (n_ssids + 1);
+}
+
+static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band)
+{
+       return band == IEEE80211_BAND_2GHZ ? 100 + 20 : 100 + 10;
+}
+
+static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
+                                      struct cfg80211_scan_request *req)
+{
+       u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band);
+       u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band,
+                                                   req->n_ssids);
+       struct iwl_scan_channel *chan = (struct iwl_scan_channel *)
+               (cmd->data + le16_to_cpu(cmd->tx_cmd.len));
+       int i;
+       __le32 chan_type_value;
+
+       if (req->n_ssids > 0)
+               chan_type_value = cpu_to_le32(BIT(req->n_ssids + 1) - 1);
+       else
+               chan_type_value = SCAN_CHANNEL_TYPE_PASSIVE;
+
+       for (i = 0; i < cmd->channel_count; i++) {
+               chan->channel = cpu_to_le16(req->channels[i]->hw_value);
+               if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+                       chan->type = SCAN_CHANNEL_TYPE_PASSIVE;
+               else
+                       chan->type = chan_type_value;
+               chan->active_dwell = cpu_to_le16(active_dwell);
+               chan->passive_dwell = cpu_to_le16(passive_dwell);
+               chan->iteration_count = cpu_to_le16(1);
+               chan++;
+       }
+}
+
+/*
+ * Fill in probe request with the following parameters:
+ * TA is our vif HW address, which mac80211 ensures we have.
+ * Packet is broadcasted, so this is both SA and DA.
+ * The probe request IE is made out of two: first comes the most prioritized
+ * SSID if a directed scan is requested. Second comes whatever extra
+ * information was given to us as the scan request IE.
+ */
+static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
+                                 int n_ssids, const u8 *ssid, int ssid_len,
+                                 const u8 *ie, int ie_len,
+                                 int left)
+{
+       int len = 0;
+       u8 *pos = NULL;
+
+       /* Make sure there is enough space for the probe request,
+        * two mandatory IEs and the data */
+       left -= 24;
+       if (left < 0)
+               return 0;
+
+       frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+       eth_broadcast_addr(frame->da);
+       memcpy(frame->sa, ta, ETH_ALEN);
+       eth_broadcast_addr(frame->bssid);
+       frame->seq_ctrl = 0;
+
+       len += 24;
+
+       /* for passive scans, no need to fill anything */
+       if (n_ssids == 0)
+               return (u16)len;
+
+       /* points to the payload of the request */
+       pos = &frame->u.probe_req.variable[0];
+
+       /* fill in our SSID IE */
+       left -= ssid_len + 2;
+       if (left < 0)
+               return 0;
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = ssid_len;
+       if (ssid && ssid_len) { /* ssid_len may be == 0 even if ssid is valid */
+               memcpy(pos, ssid, ssid_len);
+               pos += ssid_len;
+       }
+
+       len += ssid_len + 2;
+
+       if (WARN_ON(left < ie_len))
+               return len;
+
+       if (ie && ie_len) {
+               memcpy(pos, ie, ie_len);
+               len += ie_len;
+       }
+
+       return (u16)len;
+}
+
+int iwl_mvm_scan_request(struct iwl_mvm *mvm,
+                        struct ieee80211_vif *vif,
+                        struct cfg80211_scan_request *req)
+{
+       struct iwl_host_cmd hcmd = {
+               .id = SCAN_REQUEST_CMD,
+               .len = { 0, },
+               .data = { mvm->scan_cmd, },
+               .flags = CMD_SYNC,
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       struct iwl_scan_cmd *cmd = mvm->scan_cmd;
+       int ret;
+       u32 status;
+       int ssid_len = 0;
+       u8 *ssid = NULL;
+
+       lockdep_assert_held(&mvm->mutex);
+       BUG_ON(mvm->scan_cmd == NULL);
+
+       IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n");
+       mvm->scan_status = IWL_MVM_SCAN_OS;
+       memset(cmd, 0, sizeof(struct iwl_scan_cmd) +
+              mvm->fw->ucode_capa.max_probe_length +
+              (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)));
+
+       cmd->channel_count = (u8)req->n_channels;
+       cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
+       cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
+       cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm);
+       cmd->max_out_time = iwl_mvm_scan_max_out_time(vif);
+       cmd->suspend_time = iwl_mvm_scan_suspend_time(vif);
+       cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req);
+       cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+                                       MAC_FILTER_IN_BEACON);
+       cmd->type = SCAN_TYPE_FORCED;
+       cmd->repeats = cpu_to_le32(1);
+
+       /*
+        * If the user asked for passive scan, don't change to active scan if
+        * you see any activity on the channel - remain passive.
+        */
+       if (req->n_ssids > 0) {
+               cmd->passive2active = cpu_to_le16(1);
+               ssid = req->ssids[0].ssid;
+               ssid_len = req->ssids[0].ssid_len;
+       } else {
+               cmd->passive2active = 0;
+       }
+
+       iwl_mvm_scan_fill_ssids(cmd, req);
+
+       cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
+       cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
+       cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+       cmd->tx_cmd.rate_n_flags =
+                       iwl_mvm_scan_rate_n_flags(mvm, req->channels[0]->band,
+                                                 req->no_cck);
+
+       cmd->tx_cmd.len =
+               cpu_to_le16(iwl_mvm_fill_probe_req(
+                           (struct ieee80211_mgmt *)cmd->data,
+                           vif->addr,
+                           req->n_ssids, ssid, ssid_len,
+                           req->ie, req->ie_len,
+                           mvm->fw->ucode_capa.max_probe_length));
+
+       iwl_mvm_scan_fill_channels(cmd, req);
+
+       cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) +
+               le16_to_cpu(cmd->tx_cmd.len) +
+               (cmd->channel_count * sizeof(struct iwl_scan_channel)));
+       hcmd.len[0] = le16_to_cpu(cmd->len);
+
+       status = SCAN_RESPONSE_OK;
+       ret = iwl_mvm_send_cmd_status(mvm, &hcmd, &status);
+       if (!ret && status == SCAN_RESPONSE_OK) {
+               IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
+       } else {
+               /*
+                * If the scan failed, it usually means that the FW was unable
+                * to allocate the time events. Warn on it, but maybe we
+                * should try to send the command again with different params.
+                */
+               IWL_ERR(mvm, "Scan failed! status 0x%x ret %d\n",
+                       status, ret);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               ret = -EIO;
+       }
+       return ret;
+}
+
+int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_cmd_response *resp = (void *)pkt->data;
+
+       IWL_DEBUG_SCAN(mvm, "Scan response received. status 0x%x\n",
+                      le32_to_cpu(resp->status));
+       return 0;
+}
+
+int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_scan_complete_notif *notif = (void *)pkt->data;
+
+       IWL_DEBUG_SCAN(mvm, "Scan complete: status=0x%x scanned channels=%d\n",
+                      notif->status, notif->scanned_channels);
+
+       mvm->scan_status = IWL_MVM_SCAN_NONE;
+       ieee80211_scan_completed(mvm->hw, notif->status != SCAN_COMP_STATUS_OK);
+
+       return 0;
+}
+
+static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
+                                    struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       struct iwl_scan_complete_notif *notif;
+       u32 *resp;
+
+       switch (pkt->hdr.cmd) {
+       case SCAN_ABORT_CMD:
+               resp = (void *)pkt->data;
+               if (*resp == CAN_ABORT_STATUS) {
+                       IWL_DEBUG_SCAN(mvm,
+                                      "Scan can be aborted, wait until completion\n");
+                       return false;
+               }
+
+               IWL_DEBUG_SCAN(mvm, "Scan cannot be aborted, exit now: %d\n",
+                              *resp);
+               return true;
+
+       case SCAN_COMPLETE_NOTIFICATION:
+               notif = (void *)pkt->data;
+               IWL_DEBUG_SCAN(mvm, "Scan aborted: status 0x%x\n",
+                              notif->status);
+               return true;
+
+       default:
+               WARN_ON(1);
+               return false;
+       };
+}
+
+void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+{
+       struct iwl_notification_wait wait_scan_abort;
+       static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD,
+                                              SCAN_COMPLETE_NOTIFICATION };
+       int ret;
+
+       iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
+                                  scan_abort_notif,
+                                  ARRAY_SIZE(scan_abort_notif),
+                                  iwl_mvm_scan_abort_notif, NULL);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
+       if (ret) {
+               IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
+               goto out_remove_notif;
+       }
+
+       ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_abort, 1 * HZ);
+       if (ret)
+               IWL_ERR(mvm, "%s - failed on timeout\n", __func__);
+
+       return;
+
+out_remove_notif:
+       iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
new file mode 100644 (file)
index 0000000..69603c3
--- /dev/null
@@ -0,0 +1,1211 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "mvm.h"
+#include "sta.h"
+
+static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm)
+{
+       int sta_id;
+
+       WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Don't take rcu_read_lock() since we are protected by mvm->mutex */
+       for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++)
+               if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                              lockdep_is_held(&mvm->mutex)))
+                       return sta_id;
+       return IWL_MVM_STATION_COUNT;
+}
+
+/* add a NEW station to fw */
+int iwl_mvm_sta_add_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_add_sta_cmd add_sta_cmd;
+       int ret;
+       u32 status;
+       u32 agg_size = 0, mpdu_dens = 0;
+
+       memset(&add_sta_cmd, 0, sizeof(add_sta_cmd));
+
+       add_sta_cmd.sta_id = mvm_sta->sta_id;
+       add_sta_cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
+       memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN);
+
+       /* STA_FLG_FAT_EN_MSK ? */
+       /* STA_FLG_MIMO_EN_MSK ? */
+
+       if (sta->ht_cap.ht_supported) {
+               add_sta_cmd.station_flags_msk |=
+                       cpu_to_le32(STA_FLG_MAX_AGG_SIZE_MSK |
+                                   STA_FLG_AGG_MPDU_DENS_MSK);
+
+               mpdu_dens = sta->ht_cap.ampdu_density;
+       }
+
+       if (sta->vht_cap.vht_supported) {
+               agg_size = sta->vht_cap.cap &
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+               agg_size >>=
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+       } else if (sta->ht_cap.ht_supported) {
+               agg_size = sta->ht_cap.ampdu_factor;
+       }
+
+       add_sta_cmd.station_flags |=
+               cpu_to_le32(agg_size << STA_FLG_MAX_AGG_SIZE_SHIFT);
+       add_sta_cmd.station_flags |=
+               cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT);
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd),
+                                         &add_sta_cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_ASSOC(mvm, "ADD_STA PASSED\n");
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "ADD_STA failed\n");
+               break;
+       }
+
+       return ret;
+}
+
+int iwl_mvm_add_sta(struct iwl_mvm *mvm,
+                   struct ieee80211_vif *vif,
+                   struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       int i, ret, sta_id;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               sta_id = iwl_mvm_find_free_sta_id(mvm);
+       else
+               sta_id = mvm_sta->sta_id;
+
+       if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
+               return -ENOSPC;
+
+       spin_lock_init(&mvm_sta->lock);
+
+       mvm_sta->sta_id = sta_id;
+       mvm_sta->mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id,
+                                                     mvmvif->color);
+       mvm_sta->vif = vif;
+       mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
+
+       /* HW restart, don't assume the memory has been zeroed */
+       atomic_set(&mvm_sta->pending_frames, 0);
+       mvm_sta->tid_disable_agg = 0;
+       mvm_sta->tfd_queue_msk = 0;
+       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+               if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
+                       mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+
+       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
+               mvm_sta->tfd_queue_msk |= BIT(vif->cab_queue);
+
+       /* for HW restart - need to reset the seq_number etc... */
+       memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data));
+
+       ret = iwl_mvm_sta_add_to_fw(mvm, sta);
+       if (ret)
+               return ret;
+
+       /* The first station added is the AP, the others are TDLS STAs */
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+               mvmvif->ap_sta_id = sta_id;
+
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
+
+       return 0;
+}
+
+int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+                     bool drain)
+{
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       int ret;
+       u32 status;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       cmd.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color);
+       cmd.sta_id = mvmsta->sta_id;
+       cmd.add_modify = STA_MODE_MODIFY;
+       cmd.station_flags = drain ? cpu_to_le32(STA_FLG_DRAIN_FLOW) : 0;
+       cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW);
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_INFO(mvm, "Frames for staid %d will drained in fw\n",
+                              mvmsta->sta_id);
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "Couldn't drain frames for staid %d\n",
+                       mvmsta->sta_id);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Remove a station from the FW table. Before sending the command to remove
+ * the station validate that the station is indeed known to the driver (sanity
+ * only).
+ */
+static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_rm_sta_cmd rm_sta_cmd = {
+               .sta_id = sta_id,
+       };
+       int ret;
+
+       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                       lockdep_is_held(&mvm->mutex));
+
+       /* Note: internal stations are marked as error values */
+       if (!sta) {
+               IWL_ERR(mvm, "Invalid station id\n");
+               return -EINVAL;
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, CMD_SYNC,
+                                  sizeof(rm_sta_cmd), &rm_sta_cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id);
+               return ret;
+       }
+
+       return 0;
+}
+
+void iwl_mvm_sta_drained_wk(struct work_struct *wk)
+{
+       struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, sta_drained_wk);
+       u8 sta_id;
+
+       /*
+        * The mutex is needed because of the SYNC cmd, but not only: if the
+        * work would run concurrently with iwl_mvm_rm_sta, it would run before
+        * iwl_mvm_rm_sta sets the station as busy, and exit. Then
+        * iwl_mvm_rm_sta would set the station as busy, and nobody will clean
+        * that later.
+        */
+       mutex_lock(&mvm->mutex);
+
+       for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT) {
+               int ret;
+               struct ieee80211_sta *sta =
+                       rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                                 lockdep_is_held(&mvm->mutex));
+
+               /* This station is in use */
+               if (!IS_ERR(sta))
+                       continue;
+
+               if (PTR_ERR(sta) == -EINVAL) {
+                       IWL_ERR(mvm, "Drained sta %d, but it is internal?\n",
+                               sta_id);
+                       continue;
+               }
+
+               if (!sta) {
+                       IWL_ERR(mvm, "Drained sta %d, but it was NULL?\n",
+                               sta_id);
+                       continue;
+               }
+
+               WARN_ON(PTR_ERR(sta) != -EBUSY);
+               /* This station was removed and we waited until it got drained,
+                * we can now proceed and remove it.
+                */
+               ret = iwl_mvm_rm_sta_common(mvm, sta_id);
+               if (ret) {
+                       IWL_ERR(mvm,
+                               "Couldn't remove sta %d after it was drained\n",
+                               sta_id);
+                       continue;
+               }
+               rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL);
+               clear_bit(sta_id, mvm->sta_drained);
+       }
+
+       mutex_unlock(&mvm->mutex);
+}
+
+int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           mvmvif->ap_sta_id == mvm_sta->sta_id) {
+               /*
+                * Put a non-NULL since the fw station isn't removed.
+                * It will be removed after the MAC will be set as
+                * unassoc.
+                */
+               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
+                                  ERR_PTR(-EINVAL));
+
+               /* flush its queues here since we are freeing mvm_sta */
+               ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true);
+
+               /* if we are associated - we can't remove the AP STA now */
+               if (vif->bss_conf.assoc)
+                       return ret;
+
+               /* unassoc - go ahead - remove the AP STA now */
+               mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
+       }
+
+       /*
+        * There are frames pending on the AC queues for this station.
+        * We need to wait until all the frames are drained...
+        */
+       if (atomic_read(&mvm_sta->pending_frames)) {
+               ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
+               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
+                                  ERR_PTR(-EBUSY));
+       } else {
+               ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
+               rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
+       }
+
+       return ret;
+}
+
+int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
+                     struct ieee80211_vif *vif,
+                     u8 sta_id)
+{
+       int ret = iwl_mvm_rm_sta_common(mvm, sta_id);
+
+       lockdep_assert_held(&mvm->mutex);
+
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL);
+       return ret;
+}
+
+int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
+                            u32 qmask)
+{
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               sta->sta_id = iwl_mvm_find_free_sta_id(mvm);
+               if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))
+                       return -ENOSPC;
+       }
+
+       sta->tfd_queue_msk = qmask;
+
+       /* put a non-NULL value so iterating over the stations won't stop */
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL));
+       return 0;
+}
+
+void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta)
+{
+       rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], NULL);
+       memset(sta, 0, sizeof(struct iwl_mvm_int_sta));
+       sta->sta_id = IWL_MVM_STATION_COUNT;
+}
+
+static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,
+                                     struct iwl_mvm_int_sta *sta,
+                                     const u8 *addr,
+                                     u16 mac_id, u16 color)
+{
+       struct iwl_mvm_add_sta_cmd cmd;
+       int ret;
+       u32 status;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd));
+       cmd.sta_id = sta->sta_id;
+       cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,
+                                                            color));
+
+       cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk);
+
+       if (addr)
+               memcpy(cmd.addr, addr, ETH_ALEN);
+
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_INFO(mvm, "Internal station added.\n");
+               return 0;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "Add internal station failed, status=0x%x\n",
+                       status);
+               break;
+       }
+       return ret;
+}
+
+int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
+{
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Add the aux station, but without any queues */
+       ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0);
+       if (ret)
+               return ret;
+
+       ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL,
+                                        MAC_INDEX_AUX, 0);
+
+       if (ret)
+               iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta);
+       return ret;
+}
+
+/*
+ * Send the add station command for the vif's broadcast station.
+ * Assumes that the station was already allocated.
+ *
+ * @mvm: the mvm component
+ * @vif: the interface to which the broadcast station is added
+ * @bsta: the broadcast station to add.
+ */
+int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                          struct iwl_mvm_int_sta *bsta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT))
+               return -ENOSPC;
+
+       return iwl_mvm_add_int_sta_common(mvm, bsta, baddr,
+                                         mvmvif->id, mvmvif->color);
+}
+
+/* Send the FW a request to remove the station from it's internal data
+ * structures, but DO NOT remove the entry from the local data structures. */
+int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm,
+                             struct iwl_mvm_int_sta *bsta)
+{
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_mvm_rm_sta_common(mvm, bsta->sta_id);
+       if (ret)
+               IWL_WARN(mvm, "Failed sending remove station\n");
+       return ret;
+}
+
+/* Allocate a new station entry for the broadcast station to the given vif,
+ * and send it to the FW.
+ * Note that each P2P mac should have its own broadcast station.
+ *
+ * @mvm: the mvm component
+ * @vif: the interface to which the broadcast station is added
+ * @bsta: the broadcast station to add. */
+int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         struct iwl_mvm_int_sta *bsta)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+       u32 qmask;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
+       ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask);
+       if (ret)
+               return ret;
+
+       ret = iwl_mvm_add_int_sta_common(mvm, bsta, baddr,
+                                        mvmvif->id, mvmvif->color);
+
+       if (ret)
+               iwl_mvm_dealloc_int_sta(mvm, bsta);
+       return ret;
+}
+
+/*
+ * Send the FW a request to remove the station from it's internal data
+ * structures, and in addition remove it from the local data structure.
+ */
+int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *bsta)
+{
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_mvm_rm_sta_common(mvm, bsta->sta_id);
+       if (ret)
+               return ret;
+
+       iwl_mvm_dealloc_int_sta(mvm, bsta);
+       return ret;
+}
+
+int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                      int tid, u16 ssn, bool start)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       int ret;
+       u32 status;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       cmd.sta_id = mvm_sta->sta_id;
+       cmd.add_modify = STA_MODE_MODIFY;
+       cmd.add_immediate_ba_tid = (u8) tid;
+       cmd.add_immediate_ba_ssn = cpu_to_le16(ssn);
+       cmd.modify_mask = start ? STA_MODIFY_ADD_BA_TID :
+                                 STA_MODIFY_REMOVE_BA_TID;
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_INFO(mvm, "RX BA Session %sed in fw\n",
+                              start ? "start" : "stopp");
+               break;
+       case ADD_STA_IMMEDIATE_BA_FAILURE:
+               IWL_WARN(mvm, "RX BA Session refused by fw\n");
+               ret = -ENOSPC;
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "RX BA Session failed %sing, status 0x%x\n",
+                       start ? "start" : "stopp", status);
+               break;
+       }
+
+       return ret;
+}
+
+static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                             int tid, u8 queue, bool start)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       int ret;
+       u32 status;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (start) {
+               mvm_sta->tfd_queue_msk |= BIT(queue);
+               mvm_sta->tid_disable_agg &= ~BIT(tid);
+       } else {
+               mvm_sta->tfd_queue_msk &= ~BIT(queue);
+               mvm_sta->tid_disable_agg |= BIT(tid);
+       }
+
+       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       cmd.sta_id = mvm_sta->sta_id;
+       cmd.add_modify = STA_MODE_MODIFY;
+       cmd.modify_mask = STA_MODIFY_QUEUES | STA_MODIFY_TID_DISABLE_TX;
+       cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
+       cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg);
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+       if (ret)
+               return ret;
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "TX BA Session failed %sing, status 0x%x\n",
+                       start ? "start" : "stopp", status);
+               break;
+       }
+
+       return ret;
+}
+
+static const u8 tid_to_ac[] = {
+       IEEE80211_AC_BE,
+       IEEE80211_AC_BK,
+       IEEE80211_AC_BK,
+       IEEE80211_AC_BE,
+       IEEE80211_AC_VI,
+       IEEE80211_AC_VI,
+       IEEE80211_AC_VO,
+       IEEE80211_AC_VO,
+};
+
+int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data;
+       int txq_id;
+
+       if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+               return -EINVAL;
+
+       if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) {
+               IWL_ERR(mvm, "Start AGG when state is not IWL_AGG_OFF %d!\n",
+                       mvmsta->tid_data[tid].state);
+               return -ENXIO;
+       }
+
+       lockdep_assert_held(&mvm->mutex);
+
+       for (txq_id = IWL_MVM_FIRST_AGG_QUEUE;
+            txq_id <= IWL_MVM_LAST_AGG_QUEUE; txq_id++)
+               if (mvm->queue_to_mac80211[txq_id] ==
+                   IWL_INVALID_MAC80211_QUEUE)
+                       break;
+
+       if (txq_id > IWL_MVM_LAST_AGG_QUEUE) {
+               IWL_ERR(mvm, "Failed to allocate agg queue\n");
+               return -EIO;
+       }
+
+       /* the new tx queue is still connected to the same mac80211 queue */
+       mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_ac[tid]];
+
+       spin_lock_bh(&mvmsta->lock);
+       tid_data = &mvmsta->tid_data[tid];
+       tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+       tid_data->txq_id = txq_id;
+       *ssn = tid_data->ssn;
+
+       IWL_DEBUG_TX_QUEUES(mvm,
+                           "Start AGG: sta %d tid %d queue %d - ssn = %d, next_recl = %d\n",
+                           mvmsta->sta_id, tid, txq_id, tid_data->ssn,
+                           tid_data->next_reclaimed);
+
+       if (tid_data->ssn == tid_data->next_reclaimed) {
+               tid_data->state = IWL_AGG_STARTING;
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+       } else {
+               tid_data->state = IWL_EMPTYING_HW_QUEUE_ADDBA;
+       }
+
+       spin_unlock_bh(&mvmsta->lock);
+
+       return 0;
+}
+
+int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid, u8 buf_size)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       int queue, fifo, ret;
+       u16 ssn;
+
+       buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF);
+
+       spin_lock_bh(&mvmsta->lock);
+       ssn = tid_data->ssn;
+       queue = tid_data->txq_id;
+       tid_data->state = IWL_AGG_ON;
+       tid_data->ssn = 0xffff;
+       spin_unlock_bh(&mvmsta->lock);
+
+       fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]];
+
+       ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);
+       if (ret)
+               return -EIO;
+
+       iwl_trans_txq_enable(mvm->trans, queue, fifo, mvmsta->sta_id, tid,
+                            buf_size, ssn);
+
+       /*
+        * Even though in theory the peer could have different
+        * aggregation reorder buffer sizes for different sessions,
+        * our ucode doesn't allow for that and has a global limit
+        * for each station. Therefore, use the minimum of all the
+        * aggregation sessions and our default value.
+        */
+       mvmsta->max_agg_bufsize =
+               min(mvmsta->max_agg_bufsize, buf_size);
+       mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize;
+
+       if (mvm->cfg->ht_params->use_rts_for_aggregation) {
+               /*
+                * switch to RTS/CTS if it is the prefer protection
+                * method for HT traffic
+                */
+               mvmsta->lq_sta.lq.flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK;
+               /*
+                * TODO: remove the TLC_RTS flag when we tear down the last
+                * AGG session (agg_tids_count in DVM)
+                */
+       }
+
+       IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
+                    sta->addr, tid);
+
+       return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false);
+}
+
+int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       u16 txq_id;
+       int err;
+
+       spin_lock_bh(&mvmsta->lock);
+
+       txq_id = tid_data->txq_id;
+
+       IWL_DEBUG_TX_QUEUES(mvm, "Stop AGG: sta %d tid %d q %d state %d\n",
+                           mvmsta->sta_id, tid, txq_id, tid_data->state);
+
+       switch (tid_data->state) {
+       case IWL_AGG_ON:
+               tid_data->ssn = SEQ_TO_SN(tid_data->seq_number);
+
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "ssn = %d, next_recl = %d\n",
+                                   tid_data->ssn, tid_data->next_reclaimed);
+
+               /* There are still packets for this RA / TID in the HW */
+               if (tid_data->ssn != tid_data->next_reclaimed) {
+                       tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA;
+                       err = 0;
+                       break;
+               }
+
+               tid_data->ssn = 0xffff;
+               iwl_trans_txq_disable(mvm->trans, txq_id);
+               /* fall through */
+       case IWL_AGG_STARTING:
+       case IWL_EMPTYING_HW_QUEUE_ADDBA:
+               /*
+                * The agg session has been stopped before it was set up. This
+                * can happen when the AddBA timer times out for example.
+                */
+
+               /* No barriers since we are under mutex */
+               lockdep_assert_held(&mvm->mutex);
+               mvm->queue_to_mac80211[txq_id] = IWL_INVALID_MAC80211_QUEUE;
+
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               tid_data->state = IWL_AGG_OFF;
+               err = 0;
+               break;
+       default:
+               IWL_ERR(mvm,
+                       "Stopping AGG while state not ON or starting for %d on %d (%d)\n",
+                       mvmsta->sta_id, tid, tid_data->state);
+               IWL_ERR(mvm,
+                       "\ttid_data->txq_id = %d\n", tid_data->txq_id);
+               err = -EINVAL;
+       }
+
+       spin_unlock_bh(&mvmsta->lock);
+
+       return err;
+}
+
+static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm)
+{
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       i = find_first_zero_bit(mvm->fw_key_table, STA_KEY_MAX_NUM);
+
+       if (i == STA_KEY_MAX_NUM)
+               return STA_KEY_IDX_INVALID;
+
+       __set_bit(i, mvm->fw_key_table);
+
+       return i;
+}
+
+static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+
+       if (sta) {
+               struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+
+               return mvm_sta->sta_id;
+       }
+
+       /*
+        * The device expects GTKs for station interfaces to be
+        * installed as GTKs for the AP station. If we have no
+        * station ID, then use AP's station ID.
+        */
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT)
+               return mvmvif->ap_sta_id;
+
+       return IWL_INVALID_STATION;
+}
+
+static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
+                               struct iwl_mvm_sta *mvm_sta,
+                               struct ieee80211_key_conf *keyconf,
+                               u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
+                               u32 cmd_flags)
+{
+       __le16 key_flags;
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       int ret, status;
+       u16 keyidx;
+       int i;
+
+       keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
+                STA_KEY_FLG_KEYID_MSK;
+       key_flags = cpu_to_le16(keyidx);
+       key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP);
+
+       switch (keyconf->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP);
+               cmd.key.tkip_rx_tsc_byte2 = tkip_iv32;
+               for (i = 0; i < 5; i++)
+                       cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]);
+               memcpy(cmd.key.key, keyconf->key, keyconf->keylen);
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
+               memcpy(cmd.key.key, keyconf->key, keyconf->keylen);
+               break;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+               key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       cmd.key.key_offset = keyconf->hw_key_idx;
+       cmd.key.key_flags = key_flags;
+       cmd.add_modify = STA_MODE_MODIFY;
+       cmd.modify_mask = STA_MODIFY_KEY;
+       cmd.sta_id = sta_id;
+
+       status = ADD_STA_SUCCESS;
+       if (cmd_flags == CMD_SYNC)
+               ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                                 &cmd, &status);
+       else
+               ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC,
+                                          sizeof(cmd), &cmd);
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_WEP(mvm, "MODIFY_STA: set dynamic key passed\n");
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "MODIFY_STA: set dynamic key failed\n");
+               break;
+       }
+
+       return ret;
+}
+
+static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
+                                struct ieee80211_key_conf *keyconf,
+                                u8 sta_id, bool remove_key)
+{
+       struct iwl_mvm_mgmt_mcast_key_cmd igtk_cmd = {};
+
+       /* verify the key details match the required command's expectations */
+       if (WARN_ON((keyconf->cipher != WLAN_CIPHER_SUITE_AES_CMAC) ||
+                   (keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) ||
+                   (keyconf->keyidx != 4 && keyconf->keyidx != 5)))
+               return -EINVAL;
+
+       igtk_cmd.key_id = cpu_to_le32(keyconf->keyidx);
+       igtk_cmd.sta_id = cpu_to_le32(sta_id);
+
+       if (remove_key) {
+               igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_NOT_VALID);
+       } else {
+               struct ieee80211_key_seq seq;
+               const u8 *pn;
+
+               memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
+               ieee80211_aes_cmac_calculate_k1_k2(keyconf,
+                                                  igtk_cmd.K1, igtk_cmd.K2);
+               ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+               pn = seq.aes_cmac.pn;
+               igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) |
+                                                      ((u64) pn[4] << 8) |
+                                                      ((u64) pn[3] << 16) |
+                                                      ((u64) pn[2] << 24) |
+                                                      ((u64) pn[1] << 32) |
+                                                      ((u64) pn[0] << 40));
+       }
+
+       IWL_DEBUG_INFO(mvm, "%s igtk for sta %u\n",
+                      remove_key ? "removing" : "installing",
+                      igtk_cmd.sta_id);
+
+       return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, CMD_SYNC,
+                                   sizeof(igtk_cmd), &igtk_cmd);
+}
+
+
+static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+
+       if (sta)
+               return sta->addr;
+
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+               u8 sta_id = mvmvif->ap_sta_id;
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                               lockdep_is_held(&mvm->mutex));
+               return sta->addr;
+       }
+
+
+       return NULL;
+}
+
+int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+                       struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta,
+                       struct ieee80211_key_conf *keyconf,
+                       bool have_key_offset)
+{
+       struct iwl_mvm_sta *mvm_sta;
+       int ret;
+       u8 *addr, sta_id;
+       struct ieee80211_key_seq seq;
+       u16 p1k[5];
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Get the station id from the mvm local station table */
+       sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+       if (sta_id == IWL_INVALID_STATION) {
+               IWL_ERR(mvm, "Failed to find station id\n");
+               return -EINVAL;
+       }
+
+       if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC) {
+               ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false);
+               goto end;
+       }
+
+       /*
+        * It is possible that the 'sta' parameter is NULL, and thus
+        * there is a need to retrieve  the sta from the local station table.
+        */
+       if (!sta) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                               lockdep_is_held(&mvm->mutex));
+               if (IS_ERR_OR_NULL(sta)) {
+                       IWL_ERR(mvm, "Invalid station id\n");
+                       return -EINVAL;
+               }
+       }
+
+       mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
+       if (WARN_ON_ONCE(mvm_sta->vif != vif))
+               return -EINVAL;
+
+       if (!have_key_offset) {
+               /*
+                * The D3 firmware hardcodes the PTK offset to 0, so we have to
+                * configure it there. As a result, this workaround exists to
+                * let the caller set the key offset (hw_key_idx), see d3.c.
+                */
+               keyconf->hw_key_idx = iwl_mvm_set_fw_key_idx(mvm);
+               if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID)
+                       return -ENOSPC;
+       }
+
+       switch (keyconf->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+               addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
+               /* get phase 1 key from mac80211 */
+               ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+               ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+                                          seq.tkip.iv32, p1k, CMD_SYNC);
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+                                          0, NULL, CMD_SYNC);
+               break;
+       default:
+               IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher);
+               ret = -EINVAL;
+       }
+
+       if (ret)
+               __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+
+end:
+       IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
+                     keyconf->cipher, keyconf->keylen, keyconf->keyidx,
+                     sta->addr, ret);
+       return ret;
+}
+
+int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
+                          struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta,
+                          struct ieee80211_key_conf *keyconf)
+{
+       struct iwl_mvm_sta *mvm_sta;
+       struct iwl_mvm_add_sta_cmd cmd = {};
+       __le16 key_flags;
+       int ret, status;
+       u8 sta_id;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Get the station id from the mvm local station table */
+       sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+
+       IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n",
+                     keyconf->keyidx, sta_id);
+
+       if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+               return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true);
+
+       ret = __test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+       if (!ret) {
+               IWL_ERR(mvm, "offset %d not used in fw key table.\n",
+                       keyconf->hw_key_idx);
+               return -ENOENT;
+       }
+
+       if (sta_id == IWL_INVALID_STATION) {
+               IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n");
+               return 0;
+       }
+
+       /*
+        * It is possible that the 'sta' parameter is NULL, and thus
+        * there is a need to retrieve the sta from the local station table,
+        * for example when a GTK is removed (where the sta_id will then be
+        * the AP ID, and no station was passed by mac80211.)
+        */
+       if (!sta) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+                                               lockdep_is_held(&mvm->mutex));
+               if (!sta) {
+                       IWL_ERR(mvm, "Invalid station id\n");
+                       return -EINVAL;
+               }
+       }
+
+       mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
+       if (WARN_ON_ONCE(mvm_sta->vif != vif))
+               return -EINVAL;
+
+       key_flags = cpu_to_le16(keyconf->keyidx & STA_KEY_FLG_KEYID_MSK);
+       key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
+       key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
+
+       if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+               key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+       cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color);
+       cmd.key.key_flags = key_flags;
+       cmd.key.key_offset = keyconf->hw_key_idx;
+       cmd.sta_id = sta_id;
+
+       cmd.modify_mask = STA_MODIFY_KEY;
+       cmd.add_modify = STA_MODE_MODIFY;
+
+       status = ADD_STA_SUCCESS;
+       ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd),
+                                         &cmd, &status);
+
+       switch (status) {
+       case ADD_STA_SUCCESS:
+               IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
+               break;
+       default:
+               ret = -EIO;
+               IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
+               break;
+       }
+
+       return ret;
+}
+
+void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_key_conf *keyconf,
+                            struct ieee80211_sta *sta, u32 iv32,
+                            u16 *phase1key)
+{
+       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+
+       if (sta_id == IWL_INVALID_STATION)
+               return;
+
+       iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+                            iv32, phase1key, CMD_ASYNC);
+}
+
+void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, int sta_id)
+{
+       struct iwl_mvm_add_sta_cmd cmd = {
+               .add_modify = STA_MODE_MODIFY,
+               .sta_id = sta_id,
+               .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
+               .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE),
+       };
+       int ret;
+
+       /*
+        * Same modify mask for sleep_tx_count and sleep_state_flags but this
+        * should be fine since if we set the STA as "awake", then
+        * sleep_tx_count is not relevant.
+        */
+       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
+}
+
+void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, int sta_id,
+                                      enum ieee80211_frame_release_type reason,
+                                      u16 cnt)
+{
+       u16 sleep_state_flags =
+               (reason == IEEE80211_FRAME_RELEASE_UAPSD) ?
+                       STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL;
+       struct iwl_mvm_add_sta_cmd cmd = {
+               .add_modify = STA_MODE_MODIFY,
+               .sta_id = sta_id,
+               .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,
+               .sleep_tx_count = cpu_to_le16(cnt),
+               /*
+                * Same modify mask for sleep_tx_count and sleep_state_flags so
+                * we must set the sleep_state_flags too.
+                */
+               .sleep_state_flags = cpu_to_le16(sleep_state_flags),
+       };
+       int ret;
+
+       /* TODO: somehow the fw doesn't seem to take PS_POLL into account */
+       ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
new file mode 100644 (file)
index 0000000..1bf3010
--- /dev/null
@@ -0,0 +1,368 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __sta_h__
+#define __sta_h__
+
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+#include <linux/wait.h>
+
+#include "iwl-trans.h" /* for IWL_MAX_TID_COUNT */
+#include "fw-api.h" /* IWL_MVM_STATION_COUNT */
+#include "rs.h"
+
+struct iwl_mvm;
+
+/**
+ * DOC: station table - introduction
+ *
+ * The station table is a list of data structure that reprensent the stations.
+ * In STA/P2P client mode, the driver will hold one station for the AP/ GO.
+ * In GO/AP mode, the driver will have as many stations as associated clients.
+ * All these stations are reflected in the fw's station table. The driver
+ * keeps the fw's station table up to date with the ADD_STA command. Stations
+ * can be removed by the REMOVE_STA command.
+ *
+ * All the data related to a station is held in the structure %iwl_mvm_sta
+ * which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area.
+ * This data includes the index of the station in the fw, per tid information
+ * (sequence numbers, Block-ack state machine, etc...). The stations are
+ * created and deleted by the %sta_state callback from %ieee80211_ops.
+ *
+ * The driver holds a map: %fw_id_to_mac_id that allows to fetch a
+ * %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw
+ * station index. That way, the driver is able to get the tid related data in
+ * O(1) in time sensitive paths (Tx / Tx response / BA notification). These
+ * paths are triggered by the fw, and the driver needs to get a pointer to the
+ * %ieee80211 structure. This map helps to get that pointer quickly.
+ */
+
+/**
+ * DOC: station table - locking
+ *
+ * As stated before, the station is created / deleted by mac80211's %sta_state
+ * callback from %ieee80211_ops which can sleep. The next paragraph explains
+ * the locking of a single stations, the next ones relates to the station
+ * table.
+ *
+ * The station holds the sequence number per tid. So this data needs to be
+ * accessed in the Tx path (which is softIRQ). It also holds the Block-Ack
+ * information (the state machine / and the logic that checks if the queues
+ * were drained), so it also needs to be accessible from the Tx response flow.
+ * In short, the station needs to be access from sleepable context as well as
+ * from tasklets, so the station itself needs a spinlock.
+ *
+ * The writers of %fw_id_to_mac_id map are serialized by the global mutex of
+ * the mvm op_mode. This is possible since %sta_state can sleep.
+ * The pointers in this map are RCU protected, hence we won't replace the
+ * station while we have Tx / Tx response / BA notification running.
+ *
+ * If a station is deleted while it still has packets in its A-MPDU queues,
+ * then the reclaim flow will notice that there is no station in the map for
+ * sta_id and it will dump the responses.
+ */
+
+/**
+ * DOC: station table - internal stations
+ *
+ * The FW needs a few internal stations that are not reflected in
+ * mac80211, such as broadcast station in AP / GO mode, or AUX sta for
+ * scanning and P2P device (during the GO negotiation).
+ * For these kind of stations we have %iwl_mvm_int_sta struct which holds the
+ * data relevant for them from both %iwl_mvm_sta and %ieee80211_sta.
+ * Usually the data for these stations is static, so no locking is required,
+ * and no TID data as this is also not needed.
+ * One thing to note, is that these stations have an ID in the fw, but not
+ * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id
+ * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of
+ * pointers from this mapping need to check that the value is not error
+ * or NULL.
+ *
+ * Currently there is only one auxiliary station for scanning, initialized
+ * on init.
+ */
+
+/**
+ * DOC: station table - AP Station in STA mode
+ *
+ * %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
+ * %ap_sta_id. To get the point to the coresponsding %ieee80211_sta,
+ * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
+ * the AP station from the fw before setting the MAC context as unassociated.
+ * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
+ * removed by mac80211, but the station won't be removed in the fw until the
+ * VIF is set as unassociated. Then, %ap_sta_id will be invalidated.
+ */
+
+/**
+ * DOC: station table - Drain vs. Flush
+ *
+ * Flush means that all the frames in the SCD queue are dumped regardless the
+ * station to which they were sent. We do that when we disassociate and before
+ * we remove the STA of the AP. The flush can be done synchronously against the
+ * fw.
+ * Drain means that the fw will drop all the frames sent to a specific station.
+ * This is useful when a client (if we are IBSS / GO or AP) disassociates. In
+ * that case, we need to drain all the frames for that client from the AC queues
+ * that are shared with the other clients. Only then, we can remove the STA in
+ * the fw. In order to do so, we track the non-AMPDU packets for each station.
+ * If mac80211 removes a STA and if it still has non-AMPDU packets pending in
+ * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all
+ * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped
+ * (we know about it with its Tx response), we remove the station in fw and set
+ * it as %NULL in %fw_id_to_mac_id: this is the purpose of
+ * %iwl_mvm_sta_drained_wk.
+ */
+
+/**
+ * DOC: station table - fw restart
+ *
+ * When the fw asserts, or we have any other issue that requires to reset the
+ * driver, we require mac80211 to reconfigure the driver. Since the private
+ * data of the stations is embed in mac80211's %ieee80211_sta, that data will
+ * not be zeroed and needs to be reinitialized manually.
+ * %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us
+ * that we must not allocate a new sta_id but reuse the previous one. This
+ * means that the stations being re-added after the reset will have the same
+ * place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id
+ * map, since the stations aren't in the fw any more. Internal stations that
+ * are not added by mac80211 will be re-added in the init flow that is called
+ * after the restart: mac80211 call's %iwl_mvm_mac_start which calls to
+ * %iwl_mvm_up.
+ */
+
+/**
+ * DOC: AP mode - PS
+ *
+ * When a station is asleep, the fw will set it as "asleep". All the
+ * non-aggregation frames to that station will be dropped by the fw
+ * (%TX_STATUS_FAIL_DEST_PS failure code).
+ * AMPDUs are in a separate queue that is stopped by the fw. We just need to
+ * let mac80211 know how many frames we have in these queues so that it can
+ * properly handle trigger frames.
+ * When the a trigger frame is received, mac80211 tells the driver to send
+ * frames from the AMPDU queues or AC queue depending on which queue are
+ * delivery-enabled and what TID has frames to transmit (Note that mac80211 has
+ * all the knowledege since all the non-agg frames are buffered / filtered, and
+ * the driver tells mac80211 about agg frames). The driver needs to tell the fw
+ * to let frames out even if the station is asleep. This is done by
+ * %iwl_mvm_sta_modify_sleep_tx_count.
+ * When we receive a frame from that station with PM bit unset, the
+ * driver needs to let the fw know that this station isn't alseep any more.
+ * This is done by %iwl_mvm_sta_modify_ps_wake.
+ *
+ * TODO - EOSP handling
+ */
+
+/**
+ * enum iwl_mvm_agg_state
+ *
+ * The state machine of the BA agreement establishment / tear down.
+ * These states relate to a specific RA / TID.
+ *
+ * @IWL_AGG_OFF: aggregation is not used
+ * @IWL_AGG_STARTING: aggregation are starting (between start and oper)
+ * @IWL_AGG_ON: aggregation session is up
+ * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the
+ *     HW queue to be empty from packets for this RA /TID.
+ * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the
+ *     HW queue to be empty from packets for this RA /TID.
+ */
+enum iwl_mvm_agg_state {
+       IWL_AGG_OFF = 0,
+       IWL_AGG_STARTING,
+       IWL_AGG_ON,
+       IWL_EMPTYING_HW_QUEUE_ADDBA,
+       IWL_EMPTYING_HW_QUEUE_DELBA,
+};
+
+/**
+ * struct iwl_mvm_tid_data - holds the states for each RA / TID
+ * @seq_number: the next WiFi sequence number to use
+ * @next_reclaimed: the WiFi sequence number of the next packet to be acked.
+ *     This is basically (last acked packet++).
+ * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the
+ *     Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA).
+ * @state: state of the BA agreement establishment / tear down.
+ * @txq_id: Tx queue used by the BA session
+ * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or
+ *     the first packet to be sent in legacy HW queue in Tx AGG stop flow.
+ *     Basically when next_reclaimed reaches ssn, we can tell mac80211 that
+ *     we are ready to finish the Tx AGG stop / start flow.
+ * @wait_for_ba: Expect block-ack before next Tx reply
+ */
+struct iwl_mvm_tid_data {
+       u16 seq_number;
+       u16 next_reclaimed;
+       /* The rest is Tx AGG related */
+       u32 rate_n_flags;
+       enum iwl_mvm_agg_state state;
+       u16 txq_id;
+       u16 ssn;
+       bool wait_for_ba;
+};
+
+/**
+ * struct iwl_mvm_sta - representation of a station in the driver
+ * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
+ * @tfd_queue_msk: the tfd queues used by the station
+ * @mac_id_n_color: the MAC context this station is linked to
+ * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
+ *     tid.
+ * @max_agg_bufsize: the maximal size of the AGG buffer for this station
+ * @lock: lock to protect the whole struct. Since %tid_data is access from Tx
+ * and from Tx response flow, it needs a spinlock.
+ * @pending_frames: number of frames for this STA on the shared Tx queues.
+ * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
+ *
+ * When mac80211 creates a station it reserves some space (hw->sta_data_size)
+ * in the structure for use by driver. This structure is placed in that
+ * space.
+ *
+ */
+struct iwl_mvm_sta {
+       u32 sta_id;
+       u32 tfd_queue_msk;
+       u32 mac_id_n_color;
+       u16 tid_disable_agg;
+       u8 max_agg_bufsize;
+       spinlock_t lock;
+       atomic_t pending_frames;
+       struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT];
+       struct iwl_lq_sta lq_sta;
+       struct ieee80211_vif *vif;
+
+#ifdef CONFIG_PM_SLEEP
+       u16 last_seq_ctl;
+#endif
+};
+
+/**
+ * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or
+ * broadcast)
+ * @sta_id: the index of the station in the fw (will be replaced by id_n_color)
+ * @tfd_queue_msk: the tfd queues used by the station
+ */
+struct iwl_mvm_int_sta {
+       u32 sta_id;
+       u32 tfd_queue_msk;
+};
+
+int iwl_mvm_sta_add_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta);
+int iwl_mvm_add_sta(struct iwl_mvm *mvm,
+                   struct ieee80211_vif *vif,
+                   struct ieee80211_sta *sta);
+int iwl_mvm_rm_sta(struct iwl_mvm *mvm,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta);
+int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,
+                     struct ieee80211_vif *vif,
+                     u8 sta_id);
+int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+                       struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta,
+                       struct ieee80211_key_conf *key,
+                       bool have_key_offset);
+int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,
+                          struct ieee80211_vif *vif,
+                          struct ieee80211_sta *sta,
+                          struct ieee80211_key_conf *keyconf);
+
+void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            struct ieee80211_key_conf *keyconf,
+                            struct ieee80211_sta *sta, u32 iv32,
+                            u16 *phase1key);
+
+/* AMPDU */
+int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+                      int tid, u16 ssn, bool start);
+int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                       struct ieee80211_sta *sta, u16 tid, u8 buf_size);
+int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid);
+
+int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm);
+int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,
+                            u32 qmask);
+void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm,
+                            struct iwl_mvm_int_sta *sta);
+int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                          struct iwl_mvm_int_sta *bsta);
+int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm,
+                             struct iwl_mvm_int_sta *bsta);
+int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         struct iwl_mvm_int_sta *bsta);
+int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *bsta);
+void iwl_mvm_sta_drained_wk(struct work_struct *wk);
+void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, int sta_id);
+void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, int sta_id,
+                                      enum ieee80211_frame_release_type reason,
+                                      u16 cnt);
+int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+                     bool drain);
+
+#endif /* __sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
new file mode 100644 (file)
index 0000000..b9f076f
--- /dev/null
@@ -0,0 +1,569 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/jiffies.h>
+#include <net/mac80211.h>
+
+#include "iwl-notif-wait.h"
+#include "iwl-trans.h"
+#include "fw-api.h"
+#include "time-event.h"
+#include "mvm.h"
+#include "iwl-io.h"
+#include "iwl-prph.h"
+
+/* A TimeUnit is 1024 microsecond */
+#define TU_TO_JIFFIES(_tu)     (usecs_to_jiffies((_tu) * 1024))
+#define MSEC_TO_TU(_msec)      (_msec*1000/1024)
+
+void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
+                          struct iwl_mvm_time_event_data *te_data)
+{
+       lockdep_assert_held(&mvm->time_event_lock);
+
+       if (te_data->id == TE_MAX)
+               return;
+
+       list_del(&te_data->list);
+       te_data->running = false;
+       te_data->uid = 0;
+       te_data->id = TE_MAX;
+       te_data->vif = NULL;
+}
+
+void iwl_mvm_roc_done_wk(struct work_struct *wk)
+{
+       struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
+
+       synchronize_net();
+
+       /*
+        * Flush the offchannel queue -- this is called when the time
+        * event finishes or is cancelled, so that frames queued for it
+        * won't get stuck on the queue and be transmitted in the next
+        * time event.
+        * We have to send the command asynchronously since this cannot
+        * be under the mutex for locking reasons, but that's not an
+        * issue as it will have to complete before the next command is
+        * executed, and a new time event means a new command.
+        */
+       iwl_mvm_flush_tx_path(mvm, BIT(IWL_OFFCHANNEL_QUEUE), false);
+}
+
+static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
+{
+       /*
+        * First, clear the ROC_RUNNING status bit. This will cause the TX
+        * path to drop offchannel transmissions. That would also be done
+        * by mac80211, but it is racy, in particular in the case that the
+        * time event actually completed in the firmware (which is handled
+        * in iwl_mvm_te_handle_notif).
+        */
+       clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+
+       /*
+        * Of course, our status bit is just as racy as mac80211, so in
+        * addition, fire off the work struct which will drop all frames
+        * from the hardware queues that made it through the race. First
+        * it will of course synchronize the TX path to make sure that
+        * any *new* TX will be rejected.
+        */
+       schedule_work(&mvm->roc_done_wk);
+}
+
+/*
+ * Handles a FW notification for an event that is known to the driver.
+ *
+ * @mvm: the mvm component
+ * @te_data: the time event data
+ * @notif: the notification data corresponding the time event data.
+ */
+static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
+                                   struct iwl_mvm_time_event_data *te_data,
+                                   struct iwl_time_event_notif *notif)
+{
+       lockdep_assert_held(&mvm->time_event_lock);
+
+       IWL_DEBUG_TE(mvm, "Handle time event notif - UID = 0x%x action %d\n",
+                    le32_to_cpu(notif->unique_id),
+                    le32_to_cpu(notif->action));
+
+       /*
+        * The FW sends the start/end time event notifications even for events
+        * that it fails to schedule. This is indicated in the status field of
+        * the notification. This happens in cases that the scheduler cannot
+        * find a schedule that can handle the event (for example requesting a
+        * P2P Device discoveribility, while there are other higher priority
+        * events in the system).
+        */
+       WARN_ONCE(!le32_to_cpu(notif->status),
+                 "Failed to schedule time event\n");
+
+       if (le32_to_cpu(notif->action) == TE_NOTIF_HOST_END) {
+               IWL_DEBUG_TE(mvm,
+                            "TE ended - current time %lu, estimated end %lu\n",
+                            jiffies, te_data->end_jiffies);
+
+               if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+                       ieee80211_remain_on_channel_expired(mvm->hw);
+                       iwl_mvm_roc_finished(mvm);
+               }
+
+               /*
+                * By now, we should have finished association
+                * and know the dtim period.
+                */
+               if (te_data->vif->type == NL80211_IFTYPE_STATION &&
+                   (!te_data->vif->bss_conf.assoc ||
+                    !te_data->vif->bss_conf.dtim_period))
+                       IWL_ERR(mvm,
+                               "No assocation and the time event is over already...\n");
+
+               iwl_mvm_te_clear_data(mvm, te_data);
+       } else if (le32_to_cpu(notif->action) == TE_NOTIF_HOST_START) {
+               te_data->running = true;
+               te_data->end_jiffies = jiffies +
+                       TU_TO_JIFFIES(te_data->duration);
+
+               if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+                       set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+                       ieee80211_ready_on_channel(mvm->hw);
+               }
+       } else {
+               IWL_WARN(mvm, "Got TE with unknown action\n");
+       }
+}
+
+/*
+ * The Rx handler for time event notifications
+ */
+int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
+                               struct iwl_rx_cmd_buffer *rxb,
+                               struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_time_event_notif *notif = (void *)pkt->data;
+       struct iwl_mvm_time_event_data *te_data, *tmp;
+
+       IWL_DEBUG_TE(mvm, "Time event notification - UID = 0x%x action %d\n",
+                    le32_to_cpu(notif->unique_id),
+                    le32_to_cpu(notif->action));
+
+       spin_lock_bh(&mvm->time_event_lock);
+       list_for_each_entry_safe(te_data, tmp, &mvm->time_event_list, list) {
+               if (le32_to_cpu(notif->unique_id) == te_data->uid)
+                       iwl_mvm_te_handle_notif(mvm, te_data, notif);
+       }
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       return 0;
+}
+
+static bool iwl_mvm_time_event_notif(struct iwl_notif_wait_data *notif_wait,
+                                    struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       struct iwl_mvm_time_event_data *te_data = data;
+       struct ieee80211_vif *vif = te_data->vif;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_time_event_notif *notif;
+       struct iwl_time_event_resp *resp;
+
+       u32 mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color);
+
+       /* until we do something else */
+       WARN_ON(te_data->id != TE_BSS_STA_AGGRESSIVE_ASSOC);
+
+       switch (pkt->hdr.cmd) {
+       case TIME_EVENT_CMD:
+               resp = (void *)pkt->data;
+               /* TODO: I can't check that since the fw is buggy - it doesn't
+                * put the right values when we remove a TE. We can be here
+                * when we remove a TE because the remove TE command is sent in
+                * ASYNC...
+                * WARN_ON(mac_id_n_color != le32_to_cpu(resp->id_and_color));
+                */
+               te_data->uid = le32_to_cpu(resp->unique_id);
+               IWL_DEBUG_TE(mvm, "Got response - UID = 0x%x\n", te_data->uid);
+               return false;
+
+       case TIME_EVENT_NOTIFICATION:
+               notif = (void *)pkt->data;
+               WARN_ON(le32_to_cpu(notif->status) != 1);
+               WARN_ON(mac_id_n_color != le32_to_cpu(notif->id_and_color));
+               /* check if this is our Time Event that is starting */
+               if (le32_to_cpu(notif->unique_id) != te_data->uid)
+                       return false;
+               IWL_DEBUG_TE(mvm, "Event %d is starting - time is %d\n",
+                            te_data->uid, le32_to_cpu(notif->timestamp));
+
+               WARN_ONCE(!le32_to_cpu(notif->status),
+                         "Failed to schedule protected session TE\n");
+
+               te_data->running = true;
+               te_data->end_jiffies = jiffies +
+                                      TU_TO_JIFFIES(te_data->duration);
+               return true;
+
+       default:
+               WARN_ON(1);
+               return false;
+       };
+}
+
+void iwl_mvm_protect_session(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            u32 duration, u32 min_duration)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+       static const u8 time_event_notif[] = { TIME_EVENT_CMD,
+                                              TIME_EVENT_NOTIFICATION };
+       struct iwl_notification_wait wait_time_event;
+       struct iwl_time_event_cmd time_cmd = {};
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (te_data->running &&
+           time_after(te_data->end_jiffies,
+                      jiffies + TU_TO_JIFFIES(min_duration))) {
+               IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
+                            jiffies_to_msecs(te_data->end_jiffies - jiffies));
+               return;
+       }
+
+       if (te_data->running) {
+               IWL_DEBUG_TE(mvm, "extend 0x%x: only %u ms left\n",
+                            te_data->uid,
+                            jiffies_to_msecs(te_data->end_jiffies - jiffies));
+               /*
+                * we don't have enough time
+                * cancel the current TE and issue a new one
+                * Of course it would be better to remove the old one only
+                * when the new one is added, but we don't care if we are off
+                * channel for a bit. All we need to do, is not to return
+                * before we actually begin to be on the channel.
+                */
+               iwl_mvm_stop_session_protection(mvm, vif);
+       }
+
+       iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
+                                  time_event_notif,
+                                  ARRAY_SIZE(time_event_notif),
+                                  iwl_mvm_time_event_notif,
+                                  &mvmvif->time_event_data);
+
+       time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+       time_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+       time_cmd.id = cpu_to_le32(TE_BSS_STA_AGGRESSIVE_ASSOC);
+
+       time_cmd.apply_time =
+               cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG));
+       time_cmd.dep_policy = TE_INDEPENDENT;
+       time_cmd.is_present = cpu_to_le32(1);
+       time_cmd.max_frags = cpu_to_le32(TE_FRAG_NONE);
+       time_cmd.max_delay = cpu_to_le32(500);
+       /* TODO: why do we need to interval = bi if it is not periodic? */
+       time_cmd.interval = cpu_to_le32(1);
+       time_cmd.interval_reciprocal = cpu_to_le32(iwl_mvm_reciprocal(1));
+       time_cmd.duration = cpu_to_le32(duration);
+       time_cmd.repeat = cpu_to_le32(1);
+       time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_START | TE_NOTIF_HOST_END);
+
+       te_data->vif = vif;
+       te_data->duration = duration;
+
+       spin_lock_bh(&mvm->time_event_lock);
+       te_data->id = le32_to_cpu(time_cmd.id);
+       list_add_tail(&te_data->list, &mvm->time_event_list);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
+                                  sizeof(time_cmd), &time_cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
+               goto out_remove_notif;
+       }
+
+       ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1 * HZ);
+       if (ret) {
+               IWL_ERR(mvm, "%s - failed on timeout\n", __func__);
+               spin_lock_bh(&mvm->time_event_lock);
+               iwl_mvm_te_clear_data(mvm, te_data);
+               spin_unlock_bh(&mvm->time_event_lock);
+       }
+
+       return;
+
+out_remove_notif:
+       iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
+}
+
+/*
+ * Explicit request to remove a time event. The removal of a time event needs to
+ * be synchronized with the flow of a time event's end notification, which also
+ * removes the time event from the op mode data structures.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+                              struct iwl_mvm_vif *mvmvif,
+                              struct iwl_mvm_time_event_data *te_data)
+{
+       struct iwl_time_event_cmd time_cmd = {};
+       u32 id, uid;
+       int ret;
+
+       /*
+        * It is possible that by the time we got to this point the time
+        * event was already removed.
+        */
+       spin_lock_bh(&mvm->time_event_lock);
+
+       /* Save time event uid before clearing its data */
+       uid = te_data->uid;
+       id = te_data->id;
+
+       /*
+        * The clear_data function handles time events that were already removed
+        */
+       iwl_mvm_te_clear_data(mvm, te_data);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       /*
+        * It is possible that by the time we try to remove it, the time event
+        * has already ended and removed. In such a case there is no need to
+        * send a removal command.
+        */
+       if (id == TE_MAX) {
+               IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", uid);
+               return;
+       }
+
+       /* When we remove a TE, the UID is to be set in the id field */
+       time_cmd.id = cpu_to_le32(uid);
+       time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+       time_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+
+       IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id));
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_ASYNC,
+                                  sizeof(time_cmd), &time_cmd);
+       if (WARN_ON(ret))
+               return;
+}
+
+void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+
+       lockdep_assert_held(&mvm->mutex);
+       iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+}
+
+static bool iwl_mvm_roc_te_notif(struct iwl_notif_wait_data *notif_wait,
+                                struct iwl_rx_packet *pkt, void *data)
+{
+       struct iwl_mvm *mvm =
+               container_of(notif_wait, struct iwl_mvm, notif_wait);
+       struct iwl_mvm_time_event_data *te_data = data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+       struct iwl_time_event_resp *resp;
+
+       u32 mac_id_n_color = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color);
+
+       /* until we do something else */
+       WARN_ON(te_data->id != TE_P2P_DEVICE_DISCOVERABLE);
+
+       switch (pkt->hdr.cmd) {
+       case TIME_EVENT_CMD:
+               resp = (void *)pkt->data;
+               WARN_ON(mac_id_n_color != le32_to_cpu(resp->id_and_color));
+               te_data->uid = le32_to_cpu(resp->unique_id);
+               IWL_DEBUG_TE(mvm, "Got response - UID = 0x%x\n", te_data->uid);
+               return true;
+
+       default:
+               WARN_ON(1);
+               return false;
+       };
+}
+
+int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         int duration)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+       static const u8 roc_te_notif[] = { TIME_EVENT_CMD };
+       struct iwl_notification_wait wait_time_event;
+       struct iwl_time_event_cmd time_cmd = {};
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+       if (te_data->running) {
+               IWL_WARN(mvm, "P2P_DEVICE remain on channel already running\n");
+               return -EBUSY;
+       }
+
+       /*
+        * Flush the done work, just in case it's still pending, so that
+        * the work it does can complete and we can accept new frames.
+        */
+       flush_work(&mvm->roc_done_wk);
+
+       iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
+                                  roc_te_notif,
+                                  ARRAY_SIZE(roc_te_notif),
+                                  iwl_mvm_roc_te_notif,
+                                  &mvmvif->time_event_data);
+
+       time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+       time_cmd.id_and_color =
+               cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+       time_cmd.id = cpu_to_le32(TE_P2P_DEVICE_DISCOVERABLE);
+
+       time_cmd.apply_time = cpu_to_le32(0);
+       time_cmd.dep_policy = cpu_to_le32(TE_INDEPENDENT);
+       time_cmd.is_present = cpu_to_le32(1);
+
+       time_cmd.interval = cpu_to_le32(1);
+
+       /*
+        * TE_P2P_DEVICE_DISCOVERABLE can have lower priority than other events
+        * that are being scheduled by the driver/fw, and thus it might not be
+        * scheduled. To improve the chances of it being scheduled, allow it to
+        * be fragmented.
+        * In addition, for the same reasons, allow to delay the scheduling of
+        * the time event.
+        */
+       time_cmd.max_frags = cpu_to_le32(MSEC_TO_TU(duration)/20);
+       time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
+       time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
+       time_cmd.repeat = cpu_to_le32(1);
+       time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_START | TE_NOTIF_HOST_END);
+
+       /* Push the te data to the tracked te list */
+       te_data->vif = vif;
+       te_data->duration = MSEC_TO_TU(duration);
+
+       spin_lock_bh(&mvm->time_event_lock);
+       te_data->id = le32_to_cpu(time_cmd.id);
+       list_add_tail(&te_data->list, &mvm->time_event_list);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
+                                  sizeof(time_cmd), &time_cmd);
+       if (ret) {
+               IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret);
+               goto out_remove_notif;
+       }
+
+       ret = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1 * HZ);
+       if (ret) {
+               IWL_ERR(mvm, "%s - failed on timeout\n", __func__);
+               iwl_mvm_te_clear_data(mvm, te_data);
+       }
+
+       return ret;
+
+out_remove_notif:
+       iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
+       return ret;
+}
+
+void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_vif *mvmvif;
+       struct iwl_mvm_time_event_data *te_data;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /*
+        * Iterate over the list of time events and find the time event that is
+        * associated with a P2P_DEVICE interface.
+        * This assumes that a P2P_DEVICE interface can have only a single time
+        * event at any given time and this time event coresponds to a ROC
+        * request
+        */
+       mvmvif = NULL;
+       spin_lock_bh(&mvm->time_event_lock);
+       list_for_each_entry(te_data, &mvm->time_event_list, list) {
+               if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+                       mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+                       break;
+               }
+       }
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       if (!mvmvif) {
+               IWL_WARN(mvm, "P2P_DEVICE no remain on channel event\n");
+               return;
+       }
+
+       iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+
+       iwl_mvm_roc_finished(mvm);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h
new file mode 100644 (file)
index 0000000..64fb57a
--- /dev/null
@@ -0,0 +1,214 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __time_event_h__
+#define __time_event_h__
+
+#include "fw-api.h"
+
+#include "mvm.h"
+
+/**
+ * DOC: Time Events - what is it?
+ *
+ * Time Events are a fw feature that allows the driver to control the presence
+ * of the device on the channel. Since the fw supports multiple channels
+ * concurrently, the fw may choose to jump to another channel at any time.
+ * In order to make sure that the fw is on a specific channel at a certain time
+ * and for a certain duration, the driver needs to issue a time event.
+ *
+ * The simplest example is for BSS association. The driver issues a time event,
+ * waits for it to start, and only then tells mac80211 that we can start the
+ * association. This way, we make sure that the association will be done
+ * smoothly and won't be interrupted by channel switch decided within the fw.
+ */
+
+ /**
+ * DOC: The flow against the fw
+ *
+ * When the driver needs to make sure we are in a certain channel, at a certain
+ * time and for a certain duration, it sends a Time Event. The flow against the
+ * fw goes like this:
+ *     1) Driver sends a TIME_EVENT_CMD to the fw
+ *     2) Driver gets the response for that command. This response contains the
+ *        Unique ID (UID) of the event.
+ *     3) The fw sends notification when the event starts.
+ *
+ * Of course the API provides various options that allow to cover parameters
+ * of the flow.
+ *     What is the duration of the event?
+ *     What is the start time of the event?
+ *     Is there an end-time for the event?
+ *     How much can the event be delayed?
+ *     Can the event be split?
+ *     If yes what is the maximal number of chunks?
+ *     etc...
+ */
+
+/**
+ * DOC: Abstraction to the driver
+ *
+ * In order to simplify the use of time events to the rest of the driver,
+ * we abstract the use of time events. This component provides the functions
+ * needed by the driver.
+ */
+
+#define IWL_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500
+#define IWL_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400
+
+/**
+ * iwl_mvm_protect_session - start / extend the session protection.
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the session is issued
+ * @duration: the duration of the session in TU.
+ * @min_duration: will start a new session if the current session will end
+ *     in less than min_duration.
+ *
+ * This function can be used to start a session protection which means that the
+ * fw will stay on the channel for %duration_ms milliseconds. This function
+ * will block (sleep) until the session starts. This function can also be used
+ * to extend a currently running session.
+ * This function is meant to be used for BSS association for example, where we
+ * want to make sure that the fw stays on the channel during the association.
+ */
+void iwl_mvm_protect_session(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            u32 duration, u32 min_duration);
+
+/**
+ * iwl_mvm_stop_session_protection - cancel the session protection.
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the session is issued
+ *
+ * This functions cancels the session protection which is an act of good
+ * citizenship. If it is not needed any more it should be cancelled because
+ * the other bindings wait for the medium during that time.
+ * This funtions doesn't sleep.
+ */
+void iwl_mvm_stop_session_protection(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif);
+
+/*
+ * iwl_mvm_rx_time_event_notif - handles %TIME_EVENT_NOTIFICATION.
+ */
+int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
+                               struct iwl_rx_cmd_buffer *rxb,
+                               struct iwl_device_cmd *cmd);
+
+/**
+ * iwl_mvm_start_p2p_roc - start remain on channel for p2p device functionlity
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the roc is requested. It is assumed
+ * that the vif type is NL80211_IFTYPE_P2P_DEVICE
+ * @duration: the requested duration in millisecond for the fw to be on the
+ * channel that is bound to the vif.
+ *
+ * This function can be used to issue a remain on channel session,
+ * which means that the fw will stay in the channel for the request %duration
+ * milliseconds. The function is async, meaning that it only issues the ROC
+ * request but does not wait for it to start. Once the FW is ready to serve the
+ * ROC request, it will issue a notification to the driver that it is on the
+ * requested channel. Once the FW completes the ROC request it will issue
+ * another notification to the driver.
+ */
+int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                         int duration);
+
+/**
+ * iwl_mvm_stop_p2p_roc - stop remain on channel for p2p device functionlity
+ * @mvm: the mvm component
+ *
+ * This function can be used to cancel an ongoing ROC session.
+ * The function is async, it will instruct the FW to stop serving the ROC
+ * session, but will not wait for the actual stopping of the session.
+ */
+void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm);
+
+/**
+ * iwl_mvm_remove_time_event - general function to clean up of time event
+ * @mvm: the mvm component
+ * @vif: the vif to which the time event belongs
+ * @te_data: the time event data that corresponds to that time event
+ *
+ * This function can be used to cancel a time event regardless its type.
+ * It is useful for cleaning up time events running before removing an
+ * interface.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+                              struct iwl_mvm_vif *mvmvif,
+                              struct iwl_mvm_time_event_data *te_data);
+
+/**
+ * iwl_mvm_te_clear_data - remove time event from list
+ * @mvm: the mvm component
+ * @te_data: the time event data to remove
+ *
+ * This function is mostly internal, it is made available here only
+ * for firmware restart purposes.
+ */
+void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
+                          struct iwl_mvm_time_event_data *te_data);
+
+void iwl_mvm_roc_done_wk(struct work_struct *wk);
+
+#endif /* __time_event_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
new file mode 100644 (file)
index 0000000..cada8ef
--- /dev/null
@@ -0,0 +1,916 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+
+#include "iwl-trans.h"
+#include "iwl-eeprom-parse.h"
+#include "mvm.h"
+#include "sta.h"
+
+/*
+ * Sets most of the Tx cmd's fields
+ */
+static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+                              struct iwl_tx_cmd *tx_cmd,
+                              struct ieee80211_tx_info *info, u8 sta_id)
+{
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       __le16 fc = hdr->frame_control;
+       u32 tx_flags = le32_to_cpu(tx_cmd->tx_flags);
+       u32 len = skb->len + FCS_LEN;
+
+       if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+               tx_flags |= TX_CMD_FLG_ACK;
+       else
+               tx_flags &= ~TX_CMD_FLG_ACK;
+
+       if (ieee80211_is_probe_resp(fc))
+               tx_flags |= TX_CMD_FLG_TSF;
+       else if (ieee80211_is_back_req(fc))
+               tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR;
+
+       /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */
+       if (info->band == IEEE80211_BAND_2GHZ        &&
+           (skb->protocol == cpu_to_be16(ETH_P_PAE)  ||
+            is_multicast_ether_addr(hdr->addr1)      ||
+            ieee80211_is_back_req(fc)                ||
+            ieee80211_is_mgmt(fc)))
+               tx_flags |= TX_CMD_FLG_BT_DIS;
+
+       if (ieee80211_has_morefrags(fc))
+               tx_flags |= TX_CMD_FLG_MORE_FRAG;
+
+       if (ieee80211_is_data_qos(fc)) {
+               u8 *qc = ieee80211_get_qos_ctl(hdr);
+               tx_cmd->tid_tspec = qc[0] & 0xf;
+               tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
+       } else {
+               tx_cmd->tid_tspec = IWL_TID_NON_QOS;
+               if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)
+                       tx_flags |= TX_CMD_FLG_SEQ_CTL;
+               else
+                       tx_flags &= ~TX_CMD_FLG_SEQ_CTL;
+       }
+
+       if (ieee80211_is_mgmt(fc)) {
+               if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc))
+                       tx_cmd->pm_frame_timeout = cpu_to_le16(3);
+               else
+                       tx_cmd->pm_frame_timeout = cpu_to_le16(2);
+
+               /* The spec allows Action frames in A-MPDU, we don't support
+                * it
+                */
+               WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
+       } else {
+               tx_cmd->pm_frame_timeout = 0;
+       }
+
+       if (info->flags & IEEE80211_TX_CTL_AMPDU)
+               tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
+
+       if (ieee80211_is_data(fc) && len > mvm->rts_threshold &&
+           !is_multicast_ether_addr(ieee80211_get_DA(hdr)))
+               tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
+
+       tx_cmd->driver_txop = 0;
+       tx_cmd->tx_flags = cpu_to_le32(tx_flags);
+       /* Total # bytes to be transmitted */
+       tx_cmd->len = cpu_to_le16((u16)skb->len);
+       tx_cmd->next_frame_len = 0;
+       tx_cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
+       tx_cmd->sta_id = sta_id;
+}
+
+/*
+ * Sets the fields in the Tx cmd that are rate related
+ */
+static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
+                                   struct iwl_tx_cmd *tx_cmd,
+                                   struct ieee80211_tx_info *info,
+                                   struct ieee80211_sta *sta,
+                                   __le16 fc)
+{
+       u32 rate_flags;
+       int rate_idx;
+       u8 rate_plcp;
+
+       /* Set retry limit on RTS packets */
+       tx_cmd->rts_retry_limit = IWL_RTS_DFAULT_RETRY_LIMIT;
+
+       /* Set retry limit on DATA packets and Probe Responses*/
+       if (ieee80211_is_probe_resp(fc)) {
+               tx_cmd->data_retry_limit = IWL_MGMT_DFAULT_RETRY_LIMIT;
+               tx_cmd->rts_retry_limit =
+                       min(tx_cmd->data_retry_limit, tx_cmd->rts_retry_limit);
+       } else if (ieee80211_is_back_req(fc)) {
+               tx_cmd->data_retry_limit = IWL_BAR_DFAULT_RETRY_LIMIT;
+       } else {
+               tx_cmd->data_retry_limit = IWL_DEFAULT_TX_RETRY;
+       }
+
+       /*
+        * for data packets, rate info comes from the table inside he fw. This
+        * table is controlled by LINK_QUALITY commands
+        */
+
+       if (ieee80211_is_data(fc)) {
+               tx_cmd->initial_rate_index = 0;
+               tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
+               return;
+       } else if (ieee80211_is_back_req(fc)) {
+               tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
+       }
+
+       /* HT rate doesn't make sense for a non data frame */
+       WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS,
+                 "Got an HT rate for a non data frame 0x%x\n",
+                 info->control.rates[0].flags);
+
+       rate_idx = info->control.rates[0].idx;
+       /* if the rate isn't a well known legacy rate, take the lowest one */
+       if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT_LEGACY)
+               rate_idx = rate_lowest_index(
+                               &mvm->nvm_data->bands[info->band], sta);
+
+       /* For 5 GHZ band, remap mac80211 rate indices into driver indices */
+       if (info->band == IEEE80211_BAND_5GHZ)
+               rate_idx += IWL_FIRST_OFDM_RATE;
+
+       /* For 2.4 GHZ band, check that there is no need to remap */
+       BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0);
+
+       /* Get PLCP rate for tx_cmd->rate_n_flags */
+       rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx);
+
+       mvm->mgmt_last_antenna_idx =
+               iwl_mvm_next_antenna(mvm, mvm->nvm_data->valid_tx_ant,
+                                    mvm->mgmt_last_antenna_idx);
+       rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
+
+       /* Set CCK flag as needed */
+       if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
+               rate_flags |= RATE_MCS_CCK_MSK;
+
+       /* Set the rate in the TX cmd */
+       tx_cmd->rate_n_flags = cpu_to_le32((u32)rate_plcp | rate_flags);
+}
+
+/*
+ * Sets the fields in the Tx cmd that are crypto related
+ */
+static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+                                     struct ieee80211_tx_info *info,
+                                     struct iwl_tx_cmd *tx_cmd,
+                                     struct sk_buff *skb_frag)
+{
+       struct ieee80211_key_conf *keyconf = info->control.hw_key;
+
+       switch (keyconf->cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+               tx_cmd->sec_ctl = TX_CMD_SEC_CCM;
+               memcpy(tx_cmd->key, keyconf->key, keyconf->keylen);
+               if (info->flags & IEEE80211_TX_CTL_AMPDU)
+                       tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_CCMP_AGG);
+               break;
+
+       case WLAN_CIPHER_SUITE_TKIP:
+               tx_cmd->sec_ctl = TX_CMD_SEC_TKIP;
+               ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key);
+               break;
+
+       case WLAN_CIPHER_SUITE_WEP104:
+               tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128;
+               /* fall through */
+       case WLAN_CIPHER_SUITE_WEP40:
+               tx_cmd->sec_ctl |= TX_CMD_SEC_WEP |
+                       ((keyconf->keyidx << TX_CMD_SEC_WEP_KEY_IDX_POS) &
+                         TX_CMD_SEC_WEP_KEY_IDX_MSK);
+
+               memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
+               break;
+       default:
+               IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher);
+               break;
+       }
+}
+
+/*
+ * Allocates and sets the Tx cmd the driver data pointers in the skb
+ */
+static struct iwl_device_cmd *
+iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb,
+                     struct ieee80211_sta *sta, u8 sta_id)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_device_cmd *dev_cmd;
+       struct iwl_tx_cmd *tx_cmd;
+
+       dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans);
+
+       if (unlikely(!dev_cmd))
+               return NULL;
+
+       memset(dev_cmd, 0, sizeof(*dev_cmd));
+       tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+
+       if (info->control.hw_key)
+               iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb);
+
+       iwl_mvm_set_tx_cmd(mvm, skb, tx_cmd, info, sta_id);
+
+       iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control);
+
+       memset(&info->status, 0, sizeof(info->status));
+
+       info->driver_data[0] = NULL;
+       info->driver_data[1] = dev_cmd;
+
+       return dev_cmd;
+}
+
+int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_device_cmd *dev_cmd;
+       struct iwl_tx_cmd *tx_cmd;
+       u8 sta_id;
+
+       if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU))
+               return -1;
+
+       if (WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM &&
+                        (!info->control.vif ||
+                         info->hw_queue != info->control.vif->cab_queue)))
+               return -1;
+
+       /*
+        * If the interface on which frame is sent is the P2P_DEVICE
+        * or an AP/GO interface use the broadcast station associated
+        * with it; otherwise use the AUX station.
+        */
+       if (info->control.vif &&
+           (info->control.vif->type == NL80211_IFTYPE_P2P_DEVICE ||
+            info->control.vif->type == NL80211_IFTYPE_AP)) {
+               struct iwl_mvm_vif *mvmvif =
+                       iwl_mvm_vif_from_mac80211(info->control.vif);
+               sta_id = mvmvif->bcast_sta.sta_id;
+       } else {
+               sta_id = mvm->aux_sta.sta_id;
+       }
+
+       IWL_DEBUG_TX(mvm, "station Id %d, queue=%d\n", sta_id, info->hw_queue);
+
+       dev_cmd = iwl_mvm_set_tx_params(mvm, skb, NULL, sta_id);
+       if (!dev_cmd)
+               return -1;
+
+       /* From now on, we cannot access info->control */
+       tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+
+       /* Copy MAC header from skb into command buffer */
+       memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control));
+
+       if (iwl_trans_tx(mvm->trans, skb, dev_cmd, info->hw_queue)) {
+               iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Sets the fields in the Tx cmd that are crypto related
+ */
+int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
+                  struct ieee80211_sta *sta)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct iwl_mvm_sta *mvmsta;
+       struct iwl_device_cmd *dev_cmd;
+       struct iwl_tx_cmd *tx_cmd;
+       __le16 fc;
+       u16 seq_number = 0;
+       u8 tid = IWL_MAX_TID_COUNT;
+       u8 txq_id = info->hw_queue;
+       bool is_data_qos = false, is_ampdu = false;
+
+       mvmsta = (void *)sta->drv_priv;
+       fc = hdr->frame_control;
+
+       if (WARN_ON_ONCE(!mvmsta))
+               return -1;
+
+       if (WARN_ON_ONCE(mvmsta->sta_id == IWL_INVALID_STATION))
+               return -1;
+
+       dev_cmd = iwl_mvm_set_tx_params(mvm, skb, sta, mvmsta->sta_id);
+       if (!dev_cmd)
+               goto drop;
+
+       tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload;
+       /* From now on, we cannot access info->control */
+
+       spin_lock(&mvmsta->lock);
+
+       if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) {
+               u8 *qc = NULL;
+               qc = ieee80211_get_qos_ctl(hdr);
+               tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+               if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
+                       goto drop_unlock_sta;
+
+               seq_number = mvmsta->tid_data[tid].seq_number;
+               seq_number &= IEEE80211_SCTL_SEQ;
+               hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+               hdr->seq_ctrl |= cpu_to_le16(seq_number);
+               seq_number += 0x10;
+               is_data_qos = true;
+               is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU;
+       }
+
+       /* Copy MAC header from skb into command buffer */
+       memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(fc));
+
+       WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
+
+       if (is_ampdu) {
+               if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON))
+                       goto drop_unlock_sta;
+               txq_id = mvmsta->tid_data[tid].txq_id;
+       }
+
+       IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id,
+                    tid, txq_id, seq_number);
+
+       /* NOTE: aggregation will need changes here (for txq id) */
+       if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id))
+               goto drop_unlock_sta;
+
+       if (is_data_qos && !ieee80211_has_morefrags(fc))
+               mvmsta->tid_data[tid].seq_number = seq_number;
+
+       spin_unlock(&mvmsta->lock);
+
+       if (mvmsta->vif->type == NL80211_IFTYPE_AP &&
+           txq_id < IWL_FIRST_AMPDU_QUEUE)
+               atomic_inc(&mvmsta->pending_frames);
+
+       return 0;
+
+drop_unlock_sta:
+       iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
+       spin_unlock(&mvmsta->lock);
+drop:
+       return -1;
+}
+
+static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm,
+                                     struct ieee80211_sta *sta, u8 tid)
+{
+       struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+       struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+       struct ieee80211_vif *vif = mvmsta->vif;
+
+       lockdep_assert_held(&mvmsta->lock);
+
+       if (tid_data->ssn != tid_data->next_reclaimed)
+               return;
+
+       switch (tid_data->state) {
+       case IWL_EMPTYING_HW_QUEUE_ADDBA:
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "Can continue addBA flow ssn = next_recl = %d\n",
+                                   tid_data->next_reclaimed);
+               tid_data->state = IWL_AGG_STARTING;
+               ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+
+       case IWL_EMPTYING_HW_QUEUE_DELBA:
+               IWL_DEBUG_TX_QUEUES(mvm,
+                                   "Can continue DELBA flow ssn = next_recl = %d\n",
+                                   tid_data->next_reclaimed);
+               iwl_trans_txq_disable(mvm->trans, tid_data->txq_id);
+               tid_data->state = IWL_AGG_OFF;
+               /*
+                * we can't hold the mutex - but since we are after a sequence
+                * point (call to iwl_trans_txq_disable), so we don't even need
+                * a memory barrier.
+                */
+               mvm->queue_to_mac80211[tid_data->txq_id] =
+                                       IWL_INVALID_MAC80211_QUEUE;
+               ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+               break;
+
+       default:
+               break;
+       }
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+const char *iwl_mvm_get_tx_fail_reason(u32 status)
+{
+#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x
+#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x
+
+       switch (status & TX_STATUS_MSK) {
+       case TX_STATUS_SUCCESS:
+               return "SUCCESS";
+       TX_STATUS_POSTPONE(DELAY);
+       TX_STATUS_POSTPONE(FEW_BYTES);
+       TX_STATUS_POSTPONE(BT_PRIO);
+       TX_STATUS_POSTPONE(QUIET_PERIOD);
+       TX_STATUS_POSTPONE(CALC_TTAK);
+       TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY);
+       TX_STATUS_FAIL(SHORT_LIMIT);
+       TX_STATUS_FAIL(LONG_LIMIT);
+       TX_STATUS_FAIL(UNDERRUN);
+       TX_STATUS_FAIL(DRAIN_FLOW);
+       TX_STATUS_FAIL(RFKILL_FLUSH);
+       TX_STATUS_FAIL(LIFE_EXPIRE);
+       TX_STATUS_FAIL(DEST_PS);
+       TX_STATUS_FAIL(HOST_ABORTED);
+       TX_STATUS_FAIL(BT_RETRY);
+       TX_STATUS_FAIL(STA_INVALID);
+       TX_STATUS_FAIL(FRAG_DROPPED);
+       TX_STATUS_FAIL(TID_DISABLE);
+       TX_STATUS_FAIL(FIFO_FLUSHED);
+       TX_STATUS_FAIL(SMALL_CF_POLL);
+       TX_STATUS_FAIL(FW_DROP);
+       TX_STATUS_FAIL(STA_COLOR_MISMATCH);
+       }
+
+       return "UNKNOWN";
+
+#undef TX_STATUS_FAIL
+#undef TX_STATUS_POSTPONE
+}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+/**
+ * translate ucode response to mac80211 tx status control values
+ */
+static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags,
+                                        struct ieee80211_tx_info *info)
+{
+       struct ieee80211_tx_rate *r = &info->status.rates[0];
+
+       info->status.antenna =
+               ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS);
+       if (rate_n_flags & RATE_HT_MCS_GF_MSK)
+               r->flags |= IEEE80211_TX_RC_GREEN_FIELD;
+       switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
+       case RATE_MCS_CHAN_WIDTH_20:
+               break;
+       case RATE_MCS_CHAN_WIDTH_40:
+               r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+               break;
+       case RATE_MCS_CHAN_WIDTH_80:
+               r->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
+               break;
+       case RATE_MCS_CHAN_WIDTH_160:
+               r->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH;
+               break;
+       }
+       if (rate_n_flags & RATE_MCS_SGI_MSK)
+               r->flags |= IEEE80211_TX_RC_SHORT_GI;
+       if (rate_n_flags & RATE_MCS_HT_MSK) {
+               r->flags |= IEEE80211_TX_RC_MCS;
+               r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+       } else if (rate_n_flags & RATE_MCS_VHT_MSK) {
+               ieee80211_rate_set_vht(
+                       r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK,
+                       ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
+                                               RATE_VHT_MCS_NSS_POS) + 1);
+               r->flags |= IEEE80211_TX_RC_VHT_MCS;
+       } else {
+               r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
+                                                            info->band);
+       }
+}
+
+static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
+                                    struct iwl_rx_packet *pkt)
+{
+       struct ieee80211_sta *sta;
+       u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+       int txq_id = SEQ_TO_QUEUE(sequence);
+       struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+       int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
+       int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
+       u32 status = le16_to_cpu(tx_resp->status.status);
+       u16 ssn = iwl_mvm_get_scd_ssn(tx_resp);
+       struct iwl_mvm_sta *mvmsta;
+       struct sk_buff_head skbs;
+       u8 skb_freed = 0;
+       u16 next_reclaimed, seq_ctl;
+
+       __skb_queue_head_init(&skbs);
+
+       seq_ctl = le16_to_cpu(tx_resp->seq_ctl);
+
+       /* we can free until ssn % q.n_bd not inclusive */
+       iwl_trans_reclaim(mvm->trans, txq_id, ssn, &skbs);
+
+       while (!skb_queue_empty(&skbs)) {
+               struct sk_buff *skb = __skb_dequeue(&skbs);
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+               skb_freed++;
+
+               iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+
+               memset(&info->status, 0, sizeof(info->status));
+
+               info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
+               /* inform mac80211 about what happened with the frame */
+               switch (status & TX_STATUS_MSK) {
+               case TX_STATUS_SUCCESS:
+               case TX_STATUS_DIRECT_DONE:
+                       info->flags |= IEEE80211_TX_STAT_ACK;
+                       break;
+               case TX_STATUS_FAIL_DEST_PS:
+                       info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
+                       break;
+               default:
+                       break;
+               }
+
+               info->status.rates[0].count = tx_resp->failure_frame + 1;
+               iwl_mvm_hwrate_to_tx_control(le32_to_cpu(tx_resp->initial_rate),
+                                            info);
+
+               /* Single frame failure in an AMPDU queue => send BAR */
+               if (txq_id >= IWL_FIRST_AMPDU_QUEUE &&
+                   !(info->flags & IEEE80211_TX_STAT_ACK)) {
+                       /* there must be only one skb in the skb_list */
+                       WARN_ON_ONCE(skb_freed > 1 ||
+                                    !skb_queue_empty(&skbs));
+                       info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+               }
+
+               /* W/A FW bug: seq_ctl is wrong when the queue is flushed */
+               if (status == TX_STATUS_FAIL_FIFO_FLUSHED) {
+                       struct ieee80211_hdr *hdr = (void *)skb->data;
+                       seq_ctl = le16_to_cpu(hdr->seq_ctrl);
+               }
+
+               ieee80211_tx_status(mvm->hw, skb);
+       }
+
+       if (txq_id >= IWL_FIRST_AMPDU_QUEUE) {
+               /* If this is an aggregation queue, we use the ssn since:
+                * ssn = wifi seq_num % 256.
+                * The seq_ctl is the sequence control of the packet to which
+                * this Tx response relates. But if there is a hole in the
+                * bitmap of the BA we received, this Tx response may allow to
+                * reclaim the hole and all the subsequent packets that were
+                * already acked. In that case, seq_ctl != ssn, and the next
+                * packet to be reclaimed will be ssn and not seq_ctl. In that
+                * case, several packets will be reclaimed even if
+                * frame_count = 1.
+                *
+                * The ssn is the index (% 256) of the latest packet that has
+                * treated (acked / dropped) + 1.
+                */
+               next_reclaimed = ssn;
+       } else {
+               /* The next packet to be reclaimed is the one after this one */
+               next_reclaimed = SEQ_TO_SN(seq_ctl + 0x10);
+       }
+
+       IWL_DEBUG_TX_REPLY(mvm,
+                          "TXQ %d status %s (0x%08x)\n\t\t\t\tinitial_rate 0x%x "
+                           "retries %d, idx=%d ssn=%d next_reclaimed=0x%x seq_ctl=0x%x\n",
+                          txq_id, iwl_mvm_get_tx_fail_reason(status),
+                          status, le32_to_cpu(tx_resp->initial_rate),
+                          tx_resp->failure_frame, SEQ_TO_INDEX(sequence),
+                          ssn, next_reclaimed, seq_ctl);
+
+       rcu_read_lock();
+
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+       if (!IS_ERR_OR_NULL(sta)) {
+               mvmsta = (void *)sta->drv_priv;
+
+               if (tid != IWL_TID_NON_QOS) {
+                       struct iwl_mvm_tid_data *tid_data =
+                               &mvmsta->tid_data[tid];
+
+                       spin_lock(&mvmsta->lock);
+                       tid_data->next_reclaimed = next_reclaimed;
+                       IWL_DEBUG_TX_REPLY(mvm, "Next reclaimed packet:%d\n",
+                                          next_reclaimed);
+                       iwl_mvm_check_ratid_empty(mvm, sta, tid);
+                       spin_unlock(&mvmsta->lock);
+               }
+
+#ifdef CONFIG_PM_SLEEP
+               mvmsta->last_seq_ctl = seq_ctl;
+#endif
+       } else {
+               sta = NULL;
+               mvmsta = NULL;
+       }
+
+       /*
+        * If the txq is not an AMPDU queue, there is no chance we freed
+        * several skbs. Check that out...
+        * If there are no pending frames for this STA, notify mac80211 that
+        * this station can go to sleep in its STA table.
+        */
+       if (txq_id < IWL_FIRST_AMPDU_QUEUE && mvmsta &&
+           !WARN_ON(skb_freed > 1) &&
+           mvmsta->vif->type == NL80211_IFTYPE_AP &&
+           atomic_sub_and_test(skb_freed, &mvmsta->pending_frames)) {
+               ieee80211_sta_block_awake(mvm->hw, sta, false);
+               set_bit(sta_id, mvm->sta_drained);
+               schedule_work(&mvm->sta_drained_wk);
+       }
+
+       rcu_read_unlock();
+}
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+#define AGG_TX_STATE_(x) case AGG_TX_STATE_ ## x: return #x
+static const char *iwl_get_agg_tx_status(u16 status)
+{
+       switch (status & AGG_TX_STATE_STATUS_MSK) {
+       AGG_TX_STATE_(TRANSMITTED);
+       AGG_TX_STATE_(UNDERRUN);
+       AGG_TX_STATE_(BT_PRIO);
+       AGG_TX_STATE_(FEW_BYTES);
+       AGG_TX_STATE_(ABORT);
+       AGG_TX_STATE_(LAST_SENT_TTL);
+       AGG_TX_STATE_(LAST_SENT_TRY_CNT);
+       AGG_TX_STATE_(LAST_SENT_BT_KILL);
+       AGG_TX_STATE_(SCD_QUERY);
+       AGG_TX_STATE_(TEST_BAD_CRC32);
+       AGG_TX_STATE_(RESPONSE);
+       AGG_TX_STATE_(DUMP_TX);
+       AGG_TX_STATE_(DELAY_TX);
+       }
+
+       return "UNKNOWN";
+}
+
+static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
+                                     struct iwl_rx_packet *pkt)
+{
+       struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+       struct agg_tx_status *frame_status = &tx_resp->status;
+       int i;
+
+       for (i = 0; i < tx_resp->frame_count; i++) {
+               u16 fstatus = le16_to_cpu(frame_status[i].status);
+
+               IWL_DEBUG_TX_REPLY(mvm,
+                                  "status %s (0x%04x), try-count (%d) seq (0x%x)\n",
+                                  iwl_get_agg_tx_status(fstatus),
+                                  fstatus & AGG_TX_STATE_STATUS_MSK,
+                                  (fstatus & AGG_TX_STATE_TRY_CNT_MSK) >>
+                                       AGG_TX_STATE_TRY_CNT_POS,
+                                  le16_to_cpu(frame_status[i].sequence));
+       }
+}
+#else
+static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm,
+                                     struct iwl_rx_packet *pkt)
+{}
+#endif /* CONFIG_IWLWIFI_DEBUG */
+
+static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm,
+                                 struct iwl_rx_packet *pkt)
+{
+       struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+       int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid);
+       int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid);
+       u16 sequence = le16_to_cpu(pkt->hdr.sequence);
+       struct ieee80211_sta *sta;
+
+       if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_FIRST_AMPDU_QUEUE))
+               return;
+
+       if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS))
+               return;
+
+       iwl_mvm_rx_tx_cmd_agg_dbg(mvm, pkt);
+
+       rcu_read_lock();
+
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+       if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
+               struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv;
+               mvmsta->tid_data[tid].rate_n_flags =
+                       le32_to_cpu(tx_resp->initial_rate);
+       }
+
+       rcu_read_unlock();
+}
+
+int iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                     struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data;
+
+       if (tx_resp->frame_count == 1)
+               iwl_mvm_rx_tx_cmd_single(mvm, pkt);
+       else
+               iwl_mvm_rx_tx_cmd_agg(mvm, pkt);
+
+       return 0;
+}
+
+int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                       struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data;
+       struct sk_buff_head reclaimed_skbs;
+       struct iwl_mvm_tid_data *tid_data;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb;
+       int sta_id, tid, freed;
+
+       /* "flow" corresponds to Tx queue */
+       u16 scd_flow = le16_to_cpu(ba_notif->scd_flow);
+
+       /* "ssn" is start of block-ack Tx window, corresponds to index
+        * (in Tx queue's circular buffer) of first TFD/frame in window */
+       u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn);
+
+       sta_id = ba_notif->sta_id;
+       tid = ba_notif->tid;
+
+       rcu_read_lock();
+
+       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+       /* Reclaiming frames for a station that has been deleted ? */
+       if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       mvmsta = (void *)sta->drv_priv;
+       tid_data = &mvmsta->tid_data[tid];
+
+       if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d",
+                     tid_data->txq_id, tid, scd_flow)) {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       spin_lock(&mvmsta->lock);
+
+       __skb_queue_head_init(&reclaimed_skbs);
+
+       /*
+        * Release all TFDs before the SSN, i.e. all TFDs in front of
+        * block-ack window (we assume that they've been successfully
+        * transmitted ... if not, it's too late anyway).
+        */
+       iwl_trans_reclaim(mvm->trans, scd_flow, ba_resp_scd_ssn,
+                         &reclaimed_skbs);
+
+       IWL_DEBUG_TX_REPLY(mvm,
+                          "BA_NOTIFICATION Received from %pM, sta_id = %d\n",
+                          (u8 *)&ba_notif->sta_addr_lo32,
+                          ba_notif->sta_id);
+       IWL_DEBUG_TX_REPLY(mvm,
+                          "TID = %d, SeqCtl = %d, bitmap = 0x%llx, scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n",
+                          ba_notif->tid, le16_to_cpu(ba_notif->seq_ctl),
+                          (unsigned long long)le64_to_cpu(ba_notif->bitmap),
+                          scd_flow, ba_resp_scd_ssn, ba_notif->txed,
+                          ba_notif->txed_2_done);
+
+       tid_data->next_reclaimed = ba_resp_scd_ssn;
+
+       iwl_mvm_check_ratid_empty(mvm, sta, tid);
+
+       freed = 0;
+
+       skb_queue_walk(&reclaimed_skbs, skb) {
+               hdr = (struct ieee80211_hdr *)skb->data;
+
+               if (ieee80211_is_data_qos(hdr->frame_control))
+                       freed++;
+               else
+                       WARN_ON_ONCE(1);
+
+               info = IEEE80211_SKB_CB(skb);
+               iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]);
+
+               if (freed == 1) {
+                       /* this is the first skb we deliver in this batch */
+                       /* put the rate scaling data there */
+                       info = IEEE80211_SKB_CB(skb);
+                       memset(&info->status, 0, sizeof(info->status));
+                       info->flags |= IEEE80211_TX_STAT_ACK;
+                       info->flags |= IEEE80211_TX_STAT_AMPDU;
+                       info->status.ampdu_ack_len = ba_notif->txed_2_done;
+                       info->status.ampdu_len = ba_notif->txed;
+                       iwl_mvm_hwrate_to_tx_control(tid_data->rate_n_flags,
+                                                    info);
+               }
+       }
+
+       spin_unlock(&mvmsta->lock);
+
+       rcu_read_unlock();
+
+       while (!skb_queue_empty(&reclaimed_skbs)) {
+               skb = __skb_dequeue(&reclaimed_skbs);
+               ieee80211_tx_status(mvm->hw, skb);
+       }
+
+       return 0;
+}
+
+int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync)
+{
+       int ret;
+       struct iwl_tx_path_flush_cmd flush_cmd = {
+               .queues_ctl = cpu_to_le32(tfd_msk),
+               .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH),
+       };
+
+       u32 flags = sync ? CMD_SYNC : CMD_ASYNC;
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags,
+                                  sizeof(flush_cmd), &flush_cmd);
+       if (ret)
+               IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret);
+       return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
new file mode 100644 (file)
index 0000000..000e842
--- /dev/null
@@ -0,0 +1,472 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include <net/mac80211.h>
+
+#include "iwl-debug.h"
+#include "iwl-io.h"
+
+#include "mvm.h"
+#include "fw-api-rs.h"
+
+/*
+ * Will return 0 even if the cmd failed when RFKILL is asserted unless
+ * CMD_WANT_SKB is set in cmd->flags.
+ */
+int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd)
+{
+       int ret;
+
+       /*
+        * Synchronous commands from this op-mode must hold
+        * the mutex, this ensures we don't try to send two
+        * (or more) synchronous commands at a time.
+        */
+       if (!(cmd->flags & CMD_ASYNC))
+               lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_trans_send_cmd(mvm->trans, cmd);
+
+       /*
+        * If the caller wants the SKB, then don't hide any problems, the
+        * caller might access the response buffer which will be NULL if
+        * the command failed.
+        */
+       if (cmd->flags & CMD_WANT_SKB)
+               return ret;
+
+       /* Silently ignore failures if RFKILL is asserted */
+       if (!ret || ret == -ERFKILL)
+               return 0;
+       return ret;
+}
+
+int iwl_mvm_send_cmd_pdu(struct iwl_mvm *mvm, u8 id,
+                        u32 flags, u16 len, const void *data)
+{
+       struct iwl_host_cmd cmd = {
+               .id = id,
+               .len = { len, },
+               .data = { data, },
+               .flags = flags,
+       };
+
+       return iwl_mvm_send_cmd(mvm, &cmd);
+}
+
+/*
+ * We assume that the caller set the status to the sucess value
+ */
+int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
+                           u32 *status)
+{
+       struct iwl_rx_packet *pkt;
+       struct iwl_cmd_response *resp;
+       int ret, resp_len;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /*
+        * Only synchronous commands can wait for status,
+        * we use WANT_SKB so the caller can't.
+        */
+       if (WARN_ONCE(cmd->flags & (CMD_ASYNC | CMD_WANT_SKB),
+                     "cmd flags %x", cmd->flags))
+               return -EINVAL;
+
+       cmd->flags |= CMD_SYNC | CMD_WANT_SKB;
+
+       ret = iwl_trans_send_cmd(mvm->trans, cmd);
+       if (ret == -ERFKILL) {
+               /*
+                * The command failed because of RFKILL, don't update
+                * the status, leave it as success and return 0.
+                */
+               return 0;
+       } else if (ret) {
+               return ret;
+       }
+
+       pkt = cmd->resp_pkt;
+       /* Can happen if RFKILL is asserted */
+       if (!pkt) {
+               ret = 0;
+               goto out_free_resp;
+       }
+
+       if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+               ret = -EIO;
+               goto out_free_resp;
+       }
+
+       resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+       if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) {
+               ret = -EIO;
+               goto out_free_resp;
+       }
+
+       resp = (void *)pkt->data;
+       *status = le32_to_cpu(resp->status);
+ out_free_resp:
+       iwl_free_resp(cmd);
+       return ret;
+}
+
+/*
+ * We assume that the caller set the status to the sucess value
+ */
+int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u8 id, u16 len,
+                               const void *data, u32 *status)
+{
+       struct iwl_host_cmd cmd = {
+               .id = id,
+               .len = { len, },
+               .data = { data, },
+       };
+
+       return iwl_mvm_send_cmd_status(mvm, &cmd, status);
+}
+
+#define IWL_DECLARE_RATE_INFO(r) \
+       [IWL_RATE_##r##M_INDEX] = IWL_RATE_##r##M_PLCP
+
+/*
+ * Translate from fw_rate_index (IWL_RATE_XXM_INDEX) to PLCP
+ */
+static const u8 fw_rate_idx_to_plcp[IWL_RATE_COUNT] = {
+       IWL_DECLARE_RATE_INFO(1),
+       IWL_DECLARE_RATE_INFO(2),
+       IWL_DECLARE_RATE_INFO(5),
+       IWL_DECLARE_RATE_INFO(11),
+       IWL_DECLARE_RATE_INFO(6),
+       IWL_DECLARE_RATE_INFO(9),
+       IWL_DECLARE_RATE_INFO(12),
+       IWL_DECLARE_RATE_INFO(18),
+       IWL_DECLARE_RATE_INFO(24),
+       IWL_DECLARE_RATE_INFO(36),
+       IWL_DECLARE_RATE_INFO(48),
+       IWL_DECLARE_RATE_INFO(54),
+};
+
+int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
+                                       enum ieee80211_band band)
+{
+       int rate = rate_n_flags & RATE_LEGACY_RATE_MSK;
+       int idx;
+       int band_offset = 0;
+
+       /* Legacy rate format, search for match in table */
+       if (band == IEEE80211_BAND_5GHZ)
+               band_offset = IWL_FIRST_OFDM_RATE;
+       for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++)
+               if (fw_rate_idx_to_plcp[idx] == rate)
+                       return idx - band_offset;
+
+       return -1;
+}
+
+u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx)
+{
+       /* Get PLCP rate for tx_cmd->rate_n_flags */
+       return fw_rate_idx_to_plcp[rate_idx];
+}
+
+int iwl_mvm_rx_fw_error(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+                         struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_error_resp *err_resp = (void *)pkt->data;
+
+       IWL_ERR(mvm, "FW Error notification: type 0x%08X cmd_id 0x%02X\n",
+               le32_to_cpu(err_resp->error_type), err_resp->cmd_id);
+       IWL_ERR(mvm, "FW Error notification: seq 0x%04X service 0x%08X\n",
+               le16_to_cpu(err_resp->bad_cmd_seq_num),
+               le32_to_cpu(err_resp->error_service));
+       IWL_ERR(mvm, "FW Error notification: timestamp 0x%16llX\n",
+               le64_to_cpu(err_resp->timestamp));
+       return 0;
+}
+
+/*
+ * Returns the first antenna as ANT_[ABC], as defined in iwl-config.h.
+ * The parameter should also be a combination of ANT_[ABC].
+ */
+u8 first_antenna(u8 mask)
+{
+       BUILD_BUG_ON(ANT_A != BIT(0)); /* using ffs is wrong if not */
+       WARN_ON_ONCE(!mask); /* ffs will return 0 if mask is zeroed */
+       return (u8)(BIT(ffs(mask)));
+}
+
+/*
+ * Toggles between TX antennas to send the probe request on.
+ * Receives the bitmask of valid TX antennas and the *index* used
+ * for the last TX, and returns the next valid *index* to use.
+ * In order to set it in the tx_cmd, must do BIT(idx).
+ */
+u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx)
+{
+       u8 ind = last_idx;
+       int i;
+
+       for (i = 0; i < RATE_MCS_ANT_NUM; i++) {
+               ind = (ind + 1) % RATE_MCS_ANT_NUM;
+               if (valid & BIT(ind))
+                       return ind;
+       }
+
+       WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid);
+       return last_idx;
+}
+
+static struct {
+       char *name;
+       u8 num;
+} advanced_lookup[] = {
+       { "NMI_INTERRUPT_WDG", 0x34 },
+       { "SYSASSERT", 0x35 },
+       { "UCODE_VERSION_MISMATCH", 0x37 },
+       { "BAD_COMMAND", 0x38 },
+       { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
+       { "FATAL_ERROR", 0x3D },
+       { "NMI_TRM_HW_ERR", 0x46 },
+       { "NMI_INTERRUPT_TRM", 0x4C },
+       { "NMI_INTERRUPT_BREAK_POINT", 0x54 },
+       { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
+       { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
+       { "NMI_INTERRUPT_HOST", 0x66 },
+       { "NMI_INTERRUPT_ACTION_PT", 0x7C },
+       { "NMI_INTERRUPT_UNKNOWN", 0x84 },
+       { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
+       { "ADVANCED_SYSASSERT", 0 },
+};
+
+static const char *desc_lookup(u32 num)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(advanced_lookup) - 1; i++)
+               if (advanced_lookup[i].num == num)
+                       return advanced_lookup[i].name;
+
+       /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */
+       return advanced_lookup[i].name;
+}
+
+/*
+ * Note: This structure is read from the device with IO accesses,
+ * and the reading already does the endian conversion. As it is
+ * read with u32-sized accesses, any members with a different size
+ * need to be ordered correctly though!
+ */
+struct iwl_error_event_table {
+       u32 valid;              /* (nonzero) valid, (0) log is empty */
+       u32 error_id;           /* type of error */
+       u32 pc;                 /* program counter */
+       u32 blink1;             /* branch link */
+       u32 blink2;             /* branch link */
+       u32 ilink1;             /* interrupt link */
+       u32 ilink2;             /* interrupt link */
+       u32 data1;              /* error-specific data */
+       u32 data2;              /* error-specific data */
+       u32 data3;              /* error-specific data */
+       u32 bcon_time;          /* beacon timer */
+       u32 tsf_low;            /* network timestamp function timer */
+       u32 tsf_hi;             /* network timestamp function timer */
+       u32 gp1;                /* GP1 timer register */
+       u32 gp2;                /* GP2 timer register */
+       u32 gp3;                /* GP3 timer register */
+       u32 ucode_ver;          /* uCode version */
+       u32 hw_ver;             /* HW Silicon version */
+       u32 brd_ver;            /* HW board version */
+       u32 log_pc;             /* log program counter */
+       u32 frame_ptr;          /* frame pointer */
+       u32 stack_ptr;          /* stack pointer */
+       u32 hcmd;               /* last host command header */
+       u32 isr0;               /* isr status register LMPM_NIC_ISR0:
+                                * rxtx_flag */
+       u32 isr1;               /* isr status register LMPM_NIC_ISR1:
+                                * host_flag */
+       u32 isr2;               /* isr status register LMPM_NIC_ISR2:
+                                * enc_flag */
+       u32 isr3;               /* isr status register LMPM_NIC_ISR3:
+                                * time_flag */
+       u32 isr4;               /* isr status register LMPM_NIC_ISR4:
+                                * wico interrupt */
+       u32 isr_pref;           /* isr status register LMPM_NIC_PREF_STAT */
+       u32 wait_event;         /* wait event() caller address */
+       u32 l2p_control;        /* L2pControlField */
+       u32 l2p_duration;       /* L2pDurationField */
+       u32 l2p_mhvalid;        /* L2pMhValidBits */
+       u32 l2p_addr_match;     /* L2pAddrMatchStat */
+       u32 lmpm_pmg_sel;       /* indicate which clocks are turned on
+                                * (LMPM_PMG_SEL) */
+       u32 u_timestamp;        /* indicate when the date and time of the
+                                * compilation */
+       u32 flow_handler;       /* FH read/write pointers, RX credit */
+} __packed;
+
+#define ERROR_START_OFFSET  (1 * sizeof(u32))
+#define ERROR_ELEM_SIZE     (7 * sizeof(u32))
+
+void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
+{
+       struct iwl_trans *trans = mvm->trans;
+       struct iwl_error_event_table table;
+       u32 base;
+
+       base = mvm->error_event_table;
+       if (mvm->cur_ucode == IWL_UCODE_INIT) {
+               if (!base)
+                       base = mvm->fw->init_errlog_ptr;
+       } else {
+               if (!base)
+                       base = mvm->fw->inst_errlog_ptr;
+       }
+
+       if (base < 0x800000 || base >= 0x80C000) {
+               IWL_ERR(mvm,
+                       "Not valid error log pointer 0x%08X for %s uCode\n",
+                       base,
+                       (mvm->cur_ucode == IWL_UCODE_INIT)
+                                       ? "Init" : "RT");
+               return;
+       }
+
+       iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
+
+       if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
+               IWL_ERR(trans, "Start IWL Error Log Dump:\n");
+               IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
+                       mvm->status, table.valid);
+       }
+
+       trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
+                                     table.data1, table.data2, table.data3,
+                                     table.blink1, table.blink2, table.ilink1,
+                                     table.ilink2, table.bcon_time, table.gp1,
+                                     table.gp2, table.gp3, table.ucode_ver,
+                                     table.hw_ver, table.brd_ver);
+       IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
+               desc_lookup(table.error_id));
+       IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
+       IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
+       IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
+       IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
+       IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
+       IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
+       IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
+       IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
+       IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
+       IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
+       IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
+       IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
+       IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
+       IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
+       IWL_ERR(mvm, "0x%08X | uCode version\n", table.ucode_ver);
+       IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
+       IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
+       IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
+       IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
+       IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
+       IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
+       IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
+       IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
+       IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
+       IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
+       IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
+       IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
+       IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
+       IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
+       IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
+       IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
+       IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
+}
+
+/**
+ * iwl_mvm_send_lq_cmd() - Send link quality command
+ * @init: This command is sent as part of station initialization right
+ *        after station has been added.
+ *
+ * The link quality command is sent as the last step of station creation.
+ * This is the special case in which init is set and we call a callback in
+ * this case to clear the state indicating that station creation is in
+ * progress.
+ */
+int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
+                       u8 flags, bool init)
+{
+       struct iwl_host_cmd cmd = {
+               .id = LQ_CMD,
+               .len = { sizeof(struct iwl_lq_cmd), },
+               .flags = flags,
+               .data = { lq, },
+       };
+
+       if (WARN_ON(lq->sta_id == IWL_INVALID_STATION))
+               return -EINVAL;
+
+       if (WARN_ON(init && (cmd.flags & CMD_ASYNC)))
+               return -EINVAL;
+
+       return iwl_mvm_send_cmd(mvm, &cmd);
+}
index f8620ec..ff33897 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 244019c..e7de331 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 83ca403..5096f7c 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index d4df976..801ff49 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
diff --git a/drivers/net/wireless/iwlwifi/pcie/7000.c b/drivers/net/wireless/iwlwifi/pcie/7000.c
new file mode 100644 (file)
index 0000000..6e35b2b
--- /dev/null
@@ -0,0 +1,111 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include "iwl-config.h"
+#include "iwl-agn-hw.h"
+#include "cfg.h"
+
+/* Highest firmware API version supported */
+#define IWL7260_UCODE_API_MAX  6
+#define IWL3160_UCODE_API_MAX  6
+
+/* Oldest version we won't warn about */
+#define IWL7260_UCODE_API_OK   6
+#define IWL3160_UCODE_API_OK   6
+
+/* Lowest firmware API version supported */
+#define IWL7260_UCODE_API_MIN  6
+#define IWL3160_UCODE_API_MIN  6
+
+/* NVM versions */
+#define IWL7260_NVM_VERSION            0x0a1d
+#define IWL7260_TX_POWER_VERSION       0xffff /* meaningless */
+#define IWL3160_NVM_VERSION            0x709
+#define IWL3160_TX_POWER_VERSION       0xffff /* meaningless */
+
+#define IWL7260_FW_PRE "iwlwifi-7260-"
+#define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
+
+#define IWL3160_FW_PRE "iwlwifi-3160-"
+#define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode"
+
+static const struct iwl_base_params iwl7000_base_params = {
+       .eeprom_size = OTP_LOW_IMAGE_SIZE,
+       .num_of_queues = IWLAGN_NUM_QUEUES,
+       .pll_cfg_val = 0,
+       .shadow_ram_support = true,
+       .led_compensation = 57,
+       .adv_thermal_throttle = true,
+       .support_ct_kill_exit = true,
+       .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+       .chain_noise_scale = 1000,
+       .wd_timeout = IWL_LONG_WD_TIMEOUT,
+       .max_event_log_size = 512,
+       .shadow_reg_enable = false, /* TODO: fix bugs using this feature */
+};
+
+static const struct iwl_ht_params iwl7000_ht_params = {
+       .ht_greenfield_support = true,
+       .use_rts_for_aggregation = true, /* use rts/cts protection */
+       .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
+};
+
+#define IWL_DEVICE_7000                                                \
+       .ucode_api_max = IWL7260_UCODE_API_MAX,                 \
+       .ucode_api_ok = IWL7260_UCODE_API_OK,                   \
+       .ucode_api_min = IWL7260_UCODE_API_MIN,                 \
+       .device_family = IWL_DEVICE_FAMILY_7000,                \
+       .max_inst_size = IWL60_RTC_INST_SIZE,                   \
+       .max_data_size = IWL60_RTC_DATA_SIZE,                   \
+       .base_params = &iwl7000_base_params,                    \
+       /* TODO: .bt_params? */                                 \
+       .need_temp_offset_calib = true,                         \
+       .led_mode = IWL_LED_RF_STATE,                           \
+       .adv_pm = true                                          \
+
+
+const struct iwl_cfg iwl7260_2ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC7260",
+       .fw_name_pre = IWL7260_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL7260_NVM_VERSION,
+       .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+};
+
+const struct iwl_cfg iwl3160_ac_cfg = {
+       .name = "Intel(R) Dual Band Wireless AC3160",
+       .fw_name_pre = IWL3160_FW_PRE,
+       IWL_DEVICE_7000,
+       .ht_params = &iwl7000_ht_params,
+       .nvm_ver = IWL3160_NVM_VERSION,
+       .nvm_calib_ver = IWL3160_TX_POWER_VERSION,
+};
+
+MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
index 8215231..c6f8e83 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -109,5 +109,7 @@ extern const struct iwl_cfg iwl6035_2agn_cfg;
 extern const struct iwl_cfg iwl105_bgn_cfg;
 extern const struct iwl_cfg iwl105_bgn_d_cfg;
 extern const struct iwl_cfg iwl135_bgn_cfg;
+extern const struct iwl_cfg iwl7260_2ac_cfg;
+extern const struct iwl_cfg iwl3160_ac_cfg;
 
 #endif /* __iwl_pci_h__ */
index c2e141a..7bc0fb9 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -255,6 +255,12 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
        {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)},
        {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)},
 
+/* 7000 Series */
+       {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)},
+       {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)},
+
        {0}
 };
 MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids);
index 20735a0..5f6bb4e 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -235,6 +235,7 @@ struct iwl_txq {
  * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
  * @rx_page_order: page order for receive buffer size
  * @wd_timeout: queue watchdog timeout (jiffies)
+ * @reg_lock: protect hw register access
  */
 struct iwl_trans_pcie {
        struct iwl_rxq rxq;
@@ -283,6 +284,9 @@ struct iwl_trans_pcie {
 
        /* queue watchdog */
        unsigned long wd_timeout;
+
+       /*protect hw register */
+       spinlock_t reg_lock;
 };
 
 /**
index 4e6591d..a9ca1d3 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -594,6 +594,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
                int index, cmd_index, err, len;
                struct iwl_rx_cmd_buffer rxcb = {
                        ._offset = offset,
+                       ._rx_page_order = trans_pcie->rx_page_order,
                        ._page = rxb->page,
                        ._page_stolen = false,
                        .truesize = max_len,
index c57641e..56d4f72 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "iwl-agn-hw.h"
 #include "internal.h"
 
+static void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
+                                                 u32 reg, u32 mask, u32 value)
+{
+       u32 v;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+       WARN_ON_ONCE(value & ~mask);
+#endif
+
+       v = iwl_read32(trans, reg);
+       v &= ~mask;
+       v |= value;
+       iwl_write32(trans, reg, v);
+}
+
+static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
+                                             u32 reg, u32 mask)
+{
+       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
+}
+
+static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
+                                           u32 reg, u32 mask)
+{
+       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
+}
+
 static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
 {
        if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
@@ -779,15 +806,16 @@ static int iwl_trans_pcie_resume(struct iwl_trans *trans)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent)
+static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
+                                               unsigned long *flags)
 {
        int ret;
-
-       lockdep_assert_held(&trans->reg_lock);
+       struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans);
+       spin_lock_irqsave(&pcie_trans->reg_lock, *flags);
 
        /* this bit wakes up the NIC */
-       __iwl_set_bit(trans, CSR_GP_CNTRL,
-                     CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+       __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+                                CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
 
        /*
         * These bits say the device is running, and should keep running for
@@ -819,18 +847,34 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent)
                        WARN_ONCE(1,
                                  "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n",
                                  val);
+                       spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags);
                        return false;
                }
        }
 
+       /*
+        * Fool sparse by faking we release the lock - sparse will
+        * track nic_access anyway.
+        */
+       __release(&pcie_trans->reg_lock);
        return true;
 }
 
-static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans)
+static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
+                                             unsigned long *flags)
 {
-       lockdep_assert_held(&trans->reg_lock);
-       __iwl_clear_bit(trans, CSR_GP_CNTRL,
-                       CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+       struct iwl_trans_pcie *pcie_trans = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       lockdep_assert_held(&pcie_trans->reg_lock);
+
+       /*
+        * Fool sparse by faking we acquiring the lock - sparse will
+        * track nic_access anyway.
+        */
+       __acquire(&pcie_trans->reg_lock);
+
+       __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+                                  CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
        /*
         * Above we read the CSR_GP_CNTRL register, which will flush
         * any previous writes, but we need the write that clears the
@@ -838,6 +882,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans)
         * scheduled on different CPUs (after we drop reg_lock).
         */
        mmiowb();
+       spin_unlock_irqrestore(&pcie_trans->reg_lock, *flags);
 }
 
 static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
@@ -847,16 +892,14 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
        int offs, ret = 0;
        u32 *vals = buf;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr);
                for (offs = 0; offs < dwords; offs++)
                        vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        } else {
                ret = -EBUSY;
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
        return ret;
 }
 
@@ -867,17 +910,15 @@ static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr,
        int offs, ret = 0;
        u32 *vals = buf;
 
-       spin_lock_irqsave(&trans->reg_lock, flags);
-       if (iwl_trans_grab_nic_access(trans, false)) {
+       if (iwl_trans_grab_nic_access(trans, false, &flags)) {
                iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
                for (offs = 0; offs < dwords; offs++)
                        iwl_write32(trans, HBUS_TARG_MEM_WDAT,
                                    vals ? vals[offs] : 0);
-               iwl_trans_release_nic_access(trans);
+               iwl_trans_release_nic_access(trans, &flags);
        } else {
                ret = -EBUSY;
        }
-       spin_unlock_irqrestore(&trans->reg_lock, flags);
        return ret;
 }
 
@@ -952,6 +993,17 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans)
        return ret;
 }
 
+static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg,
+                                        u32 mask, u32 value)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       unsigned long flags;
+
+       spin_lock_irqsave(&trans_pcie->reg_lock, flags);
+       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, value);
+       spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+}
+
 static const char *get_fh_string(int cmd)
 {
 #define IWL_CMD(x) case x: return #x
@@ -1405,7 +1457,8 @@ static const struct iwl_trans_ops trans_ops_pcie = {
        .configure = iwl_trans_pcie_configure,
        .set_pmi = iwl_trans_pcie_set_pmi,
        .grab_nic_access = iwl_trans_pcie_grab_nic_access,
-       .release_nic_access = iwl_trans_pcie_release_nic_access
+       .release_nic_access = iwl_trans_pcie_release_nic_access,
+       .set_bits_mask = iwl_trans_pcie_set_bits_mask,
 };
 
 struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
@@ -1429,6 +1482,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        trans->cfg = cfg;
        trans_pcie->trans = trans;
        spin_lock_init(&trans_pcie->irq_lock);
+       spin_lock_init(&trans_pcie->reg_lock);
        init_waitqueue_head(&trans_pcie->ucode_write_waitq);
 
        /* W/A - seems to solve weird behavior. We need to remove this if we
@@ -1495,7 +1549,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
 
        /* Initialize the wait queue for commands */
        init_waitqueue_head(&trans_pcie->wait_command_queue);
-       spin_lock_init(&trans->reg_lock);
 
        snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name),
                 "iwl_cmd_pool:%s", dev_name(trans->dev));
index a93f067..041127a 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index 7b0ae24..25596ab 100644 (file)
@@ -399,45 +399,6 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
        return ret_len;
 }
 
-/*
- * This function reconfigures the Tx buffer size in firmware.
- *
- * This function prepares a firmware command and issues it, if
- * the current Tx buffer size is different from the one requested.
- * Maximum configurable Tx buffer size is limited by the HT capability
- * field value.
- */
-void
-mwifiex_cfg_tx_buf(struct mwifiex_private *priv,
-                  struct mwifiex_bssdescriptor *bss_desc)
-{
-       u16 max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_2K;
-       u16 tx_buf, curr_tx_buf_size = 0;
-
-       if (bss_desc->bcn_ht_cap) {
-               if (le16_to_cpu(bss_desc->bcn_ht_cap->cap_info) &
-                               IEEE80211_HT_CAP_MAX_AMSDU)
-                       max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_8K;
-               else
-                       max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_4K;
-       }
-
-       tx_buf = min(priv->adapter->max_tx_buf_size, max_amsdu);
-
-       dev_dbg(priv->adapter->dev, "info: max_amsdu=%d, max_tx_buf=%d\n",
-               max_amsdu, priv->adapter->max_tx_buf_size);
-
-       if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_2K)
-               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
-       else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_4K)
-               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
-       else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_8K)
-               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_8K;
-       if (curr_tx_buf_size != tx_buf)
-               mwifiex_send_cmd_async(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
-                                      HostCmd_ACT_GEN_SET, 0, &tx_buf);
-}
-
 /*
  * This function checks if the given pointer is valid entry of
  * Tx BA Stream table.
index 46006a5..29a4c02 100644 (file)
@@ -34,8 +34,6 @@ int mwifiex_cmd_11n_cfg(struct host_cmd_ds_command *cmd, u16 cmd_action,
 int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
                               struct mwifiex_bssdescriptor *bss_desc,
                               u8 **buffer);
-void mwifiex_cfg_tx_buf(struct mwifiex_private *priv,
-                       struct mwifiex_bssdescriptor *bss_desc);
 void mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type,
                           struct mwifiex_ie_types_htcap *);
 int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv,
index b55bade..3d64613 100644 (file)
@@ -121,7 +121,6 @@ info
        wmm_ac_vi = <number of packets sent to device from WMM AcVi queue>
        wmm_ac_be = <number of packets sent to device from WMM AcBE queue>
        wmm_ac_bk = <number of packets sent to device from WMM AcBK queue>
-       max_tx_buf_size = <maximum Tx buffer size>
        tx_buf_size = <current Tx buffer size>
        curr_tx_buf_size = <current Tx buffer size>
        ps_mode = <0/1, CAM mode/PS mode>
index 46e34aa..753b568 100644 (file)
@@ -58,8 +58,6 @@ static struct mwifiex_debug_data items[] = {
         item_addr(packets_out[WMM_AC_BE]), 1},
        {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]),
         item_addr(packets_out[WMM_AC_BK]), 1},
-       {"max_tx_buf_size", item_size(max_tx_buf_size),
-        item_addr(max_tx_buf_size), 1},
        {"tx_buf_size", item_size(tx_buf_size),
         item_addr(tx_buf_size), 1},
        {"curr_tx_buf_size", item_size(curr_tx_buf_size),
index 84848c3..e38aa9b 100644 (file)
@@ -314,7 +314,6 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
 
        adapter->pm_wakeup_fw_try = false;
 
-       adapter->max_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
        adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
        adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
 
index 6095b3e..f3d9d04 100644 (file)
@@ -178,7 +178,6 @@ struct mwifiex_ds_tx_ba_stream_tbl {
 struct mwifiex_debug_info {
        u32 int_counter;
        u32 packets_out[MAX_NUM_TID];
-       u32 max_tx_buf_size;
        u32 tx_buf_size;
        u32 curr_tx_buf_size;
        u32 tx_tbl_num;
index 893d809..a537297 100644 (file)
@@ -157,8 +157,8 @@ static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1,
 
        memset(rate1, 0, rate1_size);
 
-       for (i = 0; rate2[i] && i < rate2_size; i++) {
-               for (j = 0; tmp[j] && j < rate1_size; j++) {
+       for (i = 0; i < rate2_size && rate2[i]; i++) {
+               for (j = 0; j < rate1_size && tmp[j]; j++) {
                        /* Check common rate, excluding the bit for
                           basic rate */
                        if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) {
@@ -398,8 +398,6 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv,
 
        pos = (u8 *) assoc;
 
-       mwifiex_cfg_tx_buf(priv, bss_desc);
-
        cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE);
 
        /* Save so we know which BSS Desc to use in the response handler */
index 51044e3..ac799a0 100644 (file)
@@ -631,7 +631,6 @@ struct mwifiex_adapter {
        /* spin lock for main process */
        spinlock_t main_proc_lock;
        u32 mwifiex_processing;
-       u16 max_tx_buf_size;
        u16 tx_buf_size;
        u16 curr_tx_buf_size;
        u32 ioport;
index 237949c..df88e65 100644 (file)
@@ -846,8 +846,8 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
 
                card->tx_buf_list[wrdoneidx] = NULL;
                card->txbd_ring[wrdoneidx]->paddr = 0;
-               card->rxbd_ring[wrdoneidx]->len = 0;
-               card->rxbd_ring[wrdoneidx]->flags = 0;
+               card->txbd_ring[wrdoneidx]->len = 0;
+               card->txbd_ring[wrdoneidx]->flags = 0;
                card->txbd_rdptr++;
 
                if ((card->txbd_rdptr & MWIFIEX_TXBD_MASK) == num_tx_buffs)
@@ -1985,6 +1985,7 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter)
        card->pci_mmap = pci_iomap(pdev, 0, 0);
        if (!card->pci_mmap) {
                dev_err(adapter->dev, "iomap(0) error\n");
+               ret = -EIO;
                goto err_iomap0;
        }
        ret = pci_request_region(pdev, 2, DRV_NAME);
@@ -1995,6 +1996,7 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter)
        card->pci_mmap1 = pci_iomap(pdev, 2, 0);
        if (!card->pci_mmap1) {
                dev_err(adapter->dev, "iomap(2) error\n");
+               ret = -EIO;
                goto err_iomap2;
        }
 
index 31d7b2b..d3fb9a1 100644 (file)
@@ -332,7 +332,7 @@ mwifiex_write_data_sync(struct mwifiex_adapter *adapter,
                        u8 *buffer, u32 pkt_len, u32 port)
 {
        struct sdio_mmc_card *card = adapter->card;
-       int ret = -1;
+       int ret;
        u8 blk_mode =
                (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE;
        u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1;
@@ -350,8 +350,7 @@ mwifiex_write_data_sync(struct mwifiex_adapter *adapter,
 
        sdio_claim_host(card->func);
 
-       if (!sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size))
-               ret = 0;
+       ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size);
 
        sdio_release_host(card->func);
 
@@ -365,7 +364,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer,
                                  u32 len, u32 port, u8 claim)
 {
        struct sdio_mmc_card *card = adapter->card;
-       int ret = -1;
+       int ret;
        u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE
                       : BLOCK_MODE;
        u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1;
@@ -376,8 +375,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer,
        if (claim)
                sdio_claim_host(card->func);
 
-       if (!sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size))
-               ret = 0;
+       ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size);
 
        if (claim)
                sdio_release_host(card->func);
index 65c12eb..8470564 100644 (file)
@@ -935,9 +935,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
                                        / MWIFIEX_SDIO_BLOCK_SIZE)
                                       * MWIFIEX_SDIO_BLOCK_SIZE;
                adapter->curr_tx_buf_size = adapter->tx_buf_size;
-               dev_dbg(adapter->dev,
-                       "cmd: max_tx_buf_size=%d, tx_buf_size=%d\n",
-                       adapter->max_tx_buf_size, adapter->tx_buf_size);
+               dev_dbg(adapter->dev, "cmd: curr_tx_buf_size=%d\n",
+                       adapter->curr_tx_buf_size);
 
                if (adapter->if_ops.update_mp_end_port)
                        adapter->if_ops.update_mp_end_port(adapter,
index 5d4a10a..f90fe21 100644 (file)
@@ -672,7 +672,7 @@ static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf,
                           *len, &actual_length, timeout);
        if (ret) {
                dev_err(adapter->dev, "usb_bulk_msg for tx failed: %d\n", ret);
-               ret = -1;
+               return ret;
        }
 
        *len = actual_length;
@@ -691,7 +691,7 @@ static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf,
                           *len, &actual_length, timeout);
        if (ret) {
                dev_err(adapter->dev, "usb_bulk_msg for rx failed: %d\n", ret);
-               ret = -1;
+               return ret;
        }
 
        *len = actual_length;
index 0982375..2155397 100644 (file)
@@ -91,7 +91,7 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv,
                memcpy(info->packets_out,
                       priv->wmm.packets_out,
                       sizeof(priv->wmm.packets_out));
-               info->max_tx_buf_size = (u32) adapter->max_tx_buf_size;
+               info->curr_tx_buf_size = (u32) adapter->curr_tx_buf_size;
                info->tx_buf_size = (u32) adapter->tx_buf_size;
                info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(priv,
                                                              info->rx_tbl);
index 224cf91..2031130 100644 (file)
@@ -285,6 +285,9 @@ struct mwl8k_priv {
        char *fw_pref;
        char *fw_alt;
        struct completion firmware_loading_complete;
+
+       /* bitmap of running BSSes */
+       u32 running_bsses;
 };
 
 #define MAX_WEP_KEY_LEN         13
@@ -2156,6 +2159,8 @@ static void mwl8k_fw_unlock(struct ieee80211_hw *hw)
        }
 }
 
+static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable,
+                              u32 bitmap);
 
 /*
  * Command processing.
@@ -2174,6 +2179,34 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
        int rc;
        unsigned long timeout = 0;
        u8 buf[32];
+       u32 bitmap = 0;
+
+       wiphy_dbg(hw->wiphy, "Posting %s [%d]\n",
+                 mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid);
+
+       /* Before posting firmware commands that could change the hardware
+        * characteristics, make sure that all BSSes are stopped temporary.
+        * Enable these stopped BSSes after completion of the commands
+        */
+
+       rc = mwl8k_fw_lock(hw);
+       if (rc)
+               return rc;
+
+       if (priv->ap_fw && priv->running_bsses) {
+               switch (le16_to_cpu(cmd->code)) {
+               case MWL8K_CMD_SET_RF_CHANNEL:
+               case MWL8K_CMD_RADIO_CONTROL:
+               case MWL8K_CMD_RF_TX_POWER:
+               case MWL8K_CMD_TX_POWER:
+               case MWL8K_CMD_RF_ANTENNA:
+               case MWL8K_CMD_RTS_THRESHOLD:
+               case MWL8K_CMD_MIMO_CONFIG:
+                       bitmap = priv->running_bsses;
+                       mwl8k_enable_bsses(hw, false, bitmap);
+                       break;
+               }
+       }
 
        cmd->result = (__force __le16) 0xffff;
        dma_size = le16_to_cpu(cmd->length);
@@ -2182,13 +2215,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
        if (pci_dma_mapping_error(priv->pdev, dma_addr))
                return -ENOMEM;
 
-       rc = mwl8k_fw_lock(hw);
-       if (rc) {
-               pci_unmap_single(priv->pdev, dma_addr, dma_size,
-                                               PCI_DMA_BIDIRECTIONAL);
-               return rc;
-       }
-
        priv->hostcmd_wait = &cmd_wait;
        iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);
        iowrite32(MWL8K_H2A_INT_DOORBELL,
@@ -2201,7 +2227,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
 
        priv->hostcmd_wait = NULL;
 
-       mwl8k_fw_unlock(hw);
 
        pci_unmap_single(priv->pdev, dma_addr, dma_size,
                                        PCI_DMA_BIDIRECTIONAL);
@@ -2228,6 +2253,11 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)
                                     ms);
        }
 
+       if (bitmap)
+               mwl8k_enable_bsses(hw, true, bitmap);
+
+       mwl8k_fw_unlock(hw);
+
        return rc;
 }
 
@@ -2489,7 +2519,7 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)
                priv->hw_rev = cmd->hw_rev;
                mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));
                priv->ap_macids_supported = 0x000000ff;
-               priv->sta_macids_supported = 0x00000000;
+               priv->sta_macids_supported = 0x00000100;
                priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues);
                if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) {
                        wiphy_warn(hw->wiphy, "fw reported %d ampdu queues"
@@ -3508,7 +3538,10 @@ static int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw,
        mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;
        if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {
                if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported))
-                       mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
+                       if (priv->ap_fw)
+                               mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
+                       else
+                               mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;
                else
                        mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;
        } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) {
@@ -3680,8 +3713,16 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif, int enable)
 {
        struct mwl8k_cmd_bss_start *cmd;
+       struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
+       struct mwl8k_priv *priv = hw->priv;
        int rc;
 
+       if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid)))
+               return 0;
+
+       if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid)))
+               return 0;
+
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
@@ -3693,9 +3734,31 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,
        rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
        kfree(cmd);
 
+       if (!rc) {
+               if (enable)
+                       priv->running_bsses |= (1 << mwl8k_vif->macid);
+               else
+                       priv->running_bsses &= ~(1 << mwl8k_vif->macid);
+       }
        return rc;
 }
 
+static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap)
+{
+       struct mwl8k_priv *priv = hw->priv;
+       struct mwl8k_vif *mwl8k_vif, *tmp_vif;
+       struct ieee80211_vif *vif;
+
+       list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) {
+               vif = mwl8k_vif->vif;
+
+               if (!(bitmap & (1 << mwl8k_vif->macid)))
+                       continue;
+
+               if (vif->type == NL80211_IFTYPE_AP)
+                       mwl8k_cmd_bss_start(hw, vif, enable);
+       }
+}
 /*
  * CMD_BASTREAM.
  */
@@ -4202,8 +4265,9 @@ static int mwl8k_set_key(struct ieee80211_hw *hw,
        u8 encr_type;
        u8 *addr;
        struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
+       struct mwl8k_priv *priv = hw->priv;
 
-       if (vif->type == NL80211_IFTYPE_STATION)
+       if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw)
                return -EOPNOTSUPP;
 
        if (sta == NULL)
@@ -4609,12 +4673,18 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
                break;
        case NL80211_IFTYPE_STATION:
                if (priv->ap_fw && di->fw_image_sta) {
-                       /* we must load the sta fw to meet this request */
-                       if (!list_empty(&priv->vif_list))
-                               return -EBUSY;
-                       rc = mwl8k_reload_firmware(hw, di->fw_image_sta);
-                       if (rc)
-                               return rc;
+                       if (!list_empty(&priv->vif_list)) {
+                               wiphy_warn(hw->wiphy, "AP interface is running.\n"
+                                          "Adding STA interface for WDS");
+                       } else {
+                               /* we must load the sta fw to
+                                * meet this request.
+                                */
+                               rc = mwl8k_reload_firmware(hw,
+                                                          di->fw_image_sta);
+                               if (rc)
+                                       return rc;
+                       }
                }
                macids_supported = priv->sta_macids_supported;
                break;
@@ -4638,7 +4708,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
        /* Set the mac address.  */
        mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
 
-       if (priv->ap_fw)
+       if (vif->type == NL80211_IFTYPE_AP)
                mwl8k_cmd_set_new_stn_add_self(hw, vif);
 
        priv->macids_used |= 1 << mwl8k_vif->macid;
@@ -4663,7 +4733,7 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw,
        struct mwl8k_priv *priv = hw->priv;
        struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
 
-       if (priv->ap_fw)
+       if (vif->type == NL80211_IFTYPE_AP)
                mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr);
 
        mwl8k_cmd_del_mac_addr(hw, vif, vif->addr);
@@ -4737,9 +4807,11 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
        if (rc)
                goto out;
 
-       rc = mwl8k_cmd_set_rf_channel(hw, conf);
-       if (rc)
-               goto out;
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               rc = mwl8k_cmd_set_rf_channel(hw, conf);
+               if (rc)
+                       goto out;
+       }
 
        if (conf->power_level > 18)
                conf->power_level = 18;
@@ -4752,12 +4824,6 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)
                                goto out;
                }
 
-               rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
-               if (rc)
-                       wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
-               rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
-               if (rc)
-                       wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
 
        } else {
                rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);
@@ -4815,7 +4881,8 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                rcu_read_unlock();
        }
 
-       if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) {
+       if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc &&
+           !priv->ap_fw) {
                rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates);
                if (rc)
                        goto out;
@@ -4823,6 +4890,25 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                rc = mwl8k_cmd_use_fixed_rate_sta(hw);
                if (rc)
                        goto out;
+       } else {
+               if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc &&
+                   priv->ap_fw) {
+                       int idx;
+                       int rate;
+
+                       /* Use AP firmware specific rate command.
+                        */
+                       idx = ffs(vif->bss_conf.basic_rates);
+                       if (idx)
+                               idx--;
+
+                       if (hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+                               rate = mwl8k_rates_24[idx].hw_value;
+                       else
+                               rate = mwl8k_rates_50[idx].hw_value;
+
+                       mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate);
+               }
        }
 
        if (changed & BSS_CHANGED_ERP_PREAMBLE) {
@@ -4832,13 +4918,13 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        goto out;
        }
 
-       if (changed & BSS_CHANGED_ERP_SLOT) {
+       if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw)  {
                rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);
                if (rc)
                        goto out;
        }
 
-       if (vif->bss_conf.assoc &&
+       if (vif->bss_conf.assoc && !priv->ap_fw &&
            (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT |
                        BSS_CHANGED_HT))) {
                rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates);
@@ -4918,11 +5004,9 @@ static void
 mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       struct ieee80211_bss_conf *info, u32 changed)
 {
-       struct mwl8k_priv *priv = hw->priv;
-
-       if (!priv->ap_fw)
+       if (vif->type == NL80211_IFTYPE_STATION)
                mwl8k_bss_info_changed_sta(hw, vif, info, changed);
-       else
+       if (vif->type == NL80211_IFTYPE_AP)
                mwl8k_bss_info_changed_ap(hw, vif, info, changed);
 }
 
@@ -5647,6 +5731,15 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)
                goto err_free_irq;
        }
 
+       /* Configure Antennas */
+       rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3);
+       if (rc)
+               wiphy_warn(hw->wiphy, "failed to set # of RX antennas");
+       rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7);
+       if (rc)
+               wiphy_warn(hw->wiphy, "failed to set # of TX antennas");
+
+
        /* Disable interrupts */
        iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
        free_irq(priv->pdev->irq, hw);
@@ -5734,6 +5827,7 @@ fail:
 
 static const struct ieee80211_iface_limit ap_if_limits[] = {
        { .max = 8,     .types = BIT(NL80211_IFTYPE_AP) },
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_STATION) },
 };
 
 static const struct ieee80211_iface_combination ap_if_comb = {
@@ -5826,6 +5920,7 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
 
        if (priv->ap_macids_supported || priv->device_info->fw_image_ap) {
                hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
+               hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
                hw->wiphy->iface_combinations = &ap_if_comb;
                hw->wiphy->n_iface_combinations = 1;
        }
@@ -5948,6 +6043,8 @@ static int mwl8k_probe(struct pci_dev *pdev,
 
        priv->hw_restart_in_progress = false;
 
+       priv->running_bsses = 0;
+
        return rc;
 
 err_stop_firmware:
index a5c694f..a658b4b 100644 (file)
@@ -80,7 +80,7 @@ static inline bool rt2800_is_305x_soc(struct rt2x00_dev *rt2x00dev)
            rt2x00_rf(rt2x00dev, RF3022))
                return true;
 
-       NOTICE(rt2x00dev, "Unknown RF chipset on rt305x\n");
+       WARNING(rt2x00dev, "Unknown RF chipset on rt305x\n");
        return false;
 }
 
index 0e8d170..48a01aa 100644 (file)
@@ -1152,6 +1152,7 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = {
        { 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) },
index 4721cad..42b5b65 100644 (file)
@@ -540,9 +540,9 @@ rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg)
        tx_pid  = rt2x00_get_field32(word, TXWI_W1_PACKETID);
 
        if (wcid != tx_wcid || ack != tx_ack || (!is_agg && pid != tx_pid)) {
-               WARNING(entry->queue->rt2x00dev,
-                       "TX status report missed for queue %d entry %d\n",
-                       entry->queue->qid, entry->entry_idx);
+               DEBUG(entry->queue->rt2x00dev,
+                     "TX status report missed for queue %d entry %d\n",
+                     entry->queue->qid, entry->entry_idx);
                return TXDONE_UNKNOWN;
        }
 
@@ -968,6 +968,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x07d1, 0x3c13) },
        { USB_DEVICE(0x07d1, 0x3c15) },
        { USB_DEVICE(0x07d1, 0x3c16) },
+       { USB_DEVICE(0x07d1, 0x3c17) },
        { USB_DEVICE(0x2001, 0x3c1b) },
        /* Draytek */
        { USB_DEVICE(0x07fa, 0x7712) },
@@ -1115,6 +1116,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* 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
@@ -1166,6 +1168,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
 #ifdef CONFIG_RT2800USB_RT53XX
        /* Arcadyan */
        { USB_DEVICE(0x043e, 0x7a12) },
+       { USB_DEVICE(0x043e, 0x7a32) },
        /* Azurewave */
        { USB_DEVICE(0x13d3, 0x3329) },
        { USB_DEVICE(0x13d3, 0x3365) },
@@ -1177,16 +1180,20 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x2001, 0x3c1e) },
        /* 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) },
-       /* Unknown */
-       { USB_DEVICE(0x04da, 0x23f6) },
 #endif
 #ifdef CONFIG_RT2800USB_UNKNOWN
        /*
@@ -1223,7 +1230,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x18c5, 0x0008) },
        /* D-Link */
        { USB_DEVICE(0x07d1, 0x3c0b) },
-       { USB_DEVICE(0x07d1, 0x3c17) },
        /* Encore */
        { USB_DEVICE(0x203d, 0x14a1) },
        /* Gemtek */
@@ -1261,8 +1267,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x083a, 0xc522) },
        { USB_DEVICE(0x083a, 0xd522) },
        { USB_DEVICE(0x083a, 0xf511) },
-       /* Zyxel */
-       { USB_DEVICE(0x0586, 0x341a) },
 #endif
        { 0, }
 };
index b52512b..9a3f31a 100644 (file)
 #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)
+       DEBUG_PRINTK_MSG(__dev, KERN_WARNING, "Warning", __msg, ##__args)
 #define INFO(__dev, __msg, __args...) \
-       DEBUG_PRINTK(__dev, KERN_INFO, "Info", __msg, ##__args)
+       DEBUG_PRINTK_MSG(__dev, KERN_INFO, "Info", __msg, ##__args)
 #define DEBUG(__dev, __msg, __args...) \
        DEBUG_PRINTK(__dev, KERN_DEBUG, "Debug", __msg, ##__args)
 #define EEPROM(__dev, __msg, __args...) \
index b40a538..1031db6 100644 (file)
@@ -1236,7 +1236,8 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev)
         */
        if_limit = &rt2x00dev->if_limits_ap;
        if_limit->max = rt2x00dev->ops->max_ap_intf;
-       if_limit->types = BIT(NL80211_IFTYPE_AP);
+       if_limit->types = BIT(NL80211_IFTYPE_AP) |
+                       BIT(NL80211_IFTYPE_MESH_POINT);
 
        /*
         * Build up AP interface combinations structure.
@@ -1446,7 +1447,7 @@ EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev);
 #ifdef CONFIG_PM
 int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state)
 {
-       NOTICE(rt2x00dev, "Going to sleep.\n");
+       DEBUG(rt2x00dev, "Going to sleep.\n");
 
        /*
         * Prevent mac80211 from accessing driver while suspended.
@@ -1486,7 +1487,7 @@ EXPORT_SYMBOL_GPL(rt2x00lib_suspend);
 
 int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev)
 {
-       NOTICE(rt2x00dev, "Waking up.\n");
+       DEBUG(rt2x00dev, "Waking up.\n");
 
        /*
         * Restore/enable extra components.
index ed7a1bb..20c6ecc 100644 (file)
@@ -731,9 +731,9 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw,
        queue->aifs = params->aifs;
        queue->txop = params->txop;
 
-       INFO(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);
+       DEBUG(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;
 }
index c1e065f..204f46c 100644 (file)
@@ -217,19 +217,6 @@ static void rtl_tx_status(void *ppriv,
        }
 }
 
-static void rtl_rate_init(void *ppriv,
-                         struct ieee80211_supported_band *sband,
-                         struct ieee80211_sta *sta, void *priv_sta)
-{
-}
-
-static void rtl_rate_update(void *ppriv,
-                           struct ieee80211_supported_band *sband,
-                           struct ieee80211_sta *sta, void *priv_sta,
-                           u32 changed)
-{
-}
-
 static void *rtl_rate_alloc(struct ieee80211_hw *hw,
                struct dentry *debugfsdir)
 {
@@ -274,8 +261,6 @@ static struct rate_control_ops rtl_rate_ops = {
        .free = rtl_rate_free,
        .alloc_sta = rtl_rate_alloc_sta,
        .free_sta = rtl_rate_free_sta,
-       .rate_init = rtl_rate_init,
-       .rate_update = rtl_rate_update,
        .tx_status = rtl_tx_status,
        .get_rate = rtl_get_rate,
 };
index 1cdf5a2..b793a65 100644 (file)
@@ -669,7 +669,8 @@ static void rtl92c_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw
        u8 thermalvalue, delta, delta_lck, delta_iqk;
        long ele_a, ele_d, temp_cck, val_x, value32;
        long val_y, ele_c = 0;
-       u8 ofdm_index[2], cck_index = 0, ofdm_index_old[2], cck_index_old = 0;
+       u8 ofdm_index[2], ofdm_index_old[2], cck_index_old = 0;
+       s8 cck_index = 0;
        int i;
        bool is2t = IS_92C_SERIAL(rtlhal->version);
        s8 txpwr_level[2] = {0, 0};
index b7e6607..d9e659f 100644 (file)
@@ -76,7 +76,7 @@ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw)
                                      GFP_KERNEL, hw, rtl_fw_cb);
 
 
-       return 0;
+       return err;
 }
 
 static void rtl92cu_deinit_sw_vars(struct ieee80211_hw *hw)
index fd8df23..5251fb8 100644 (file)
@@ -841,9 +841,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter(
        long ele_a = 0, ele_d, temp_cck, val_x, value32;
        long val_y, ele_c = 0;
        u8 ofdm_index[2];
-       u8 cck_index = 0;
+       s8 cck_index = 0;
        u8 ofdm_index_old[2];
-       u8 cck_index_old = 0;
+       s8 cck_index_old = 0;
        u8 index;
        int i;
        bool is2t = IS_92D_SINGLEPHY(rtlhal->version);
index f55b176..35cb8f8 100644 (file)
@@ -252,7 +252,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw,
        u16 box_reg = 0, box_extreg = 0;
        u8 u1tmp;
        bool isfw_rd = false;
-       bool bwrite_sucess = false;
+       bool bwrite_success = false;
        u8 wait_h2c_limmit = 100;
        u8 wait_writeh2c_limmit = 100;
        u8 boxcontent[4], boxextcontent[2];
@@ -291,7 +291,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw,
                }
        }
 
-       while (!bwrite_sucess) {
+       while (!bwrite_success) {
                wait_writeh2c_limmit--;
                if (wait_writeh2c_limmit == 0) {
                        RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -429,7 +429,7 @@ static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw,
                        break;
                }
 
-               bwrite_sucess = true;
+               bwrite_success = true;
 
                rtlhal->last_hmeboxnum = boxnum + 1;
                if (rtlhal->last_hmeboxnum == 4)
@@ -512,7 +512,6 @@ static bool _rtl8723ae_cmd_send_packet(struct ieee80211_hw *hw,
        struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
        struct rtl8192_tx_ring *ring;
        struct rtl_tx_desc *pdesc;
-       u8 own;
        unsigned long flags;
        struct sk_buff *pskb = NULL;
 
@@ -525,7 +524,6 @@ static bool _rtl8723ae_cmd_send_packet(struct ieee80211_hw *hw,
        spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
 
        pdesc = &ring->desc[0];
-       own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc, true, HW_DESC_OWN);
 
        rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *) pdesc, 1, 1, skb);
 
index 887d521..68c2834 100644 (file)
@@ -1433,7 +1433,6 @@ static void _rtl8723ae_dm_bt_coexist_2_ant(struct ieee80211_hw *hw)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
        struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
-       u8 bt_retry_cnt;
        u8 bt_info_original;
        RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG,
                 "[BTCoex] Get bt info by fw!!\n");
@@ -1445,7 +1444,6 @@ static void _rtl8723ae_dm_bt_coexist_2_ant(struct ieee80211_hw *hw)
                                 "[BTCoex] c2h for btInfo not rcvd yet!!\n");
        }
 
-       bt_retry_cnt = rtlhal->hal_coex_8723.bt_retry_cnt;
        bt_info_original = rtlhal->hal_coex_8723.c2h_bt_info_original;
 
        /* when bt inquiry or page scan, we have to set h2c 0x25
index 0a8c038..1498048 100644 (file)
@@ -703,11 +703,9 @@ static void _rtl8723ae_hw_configure(struct ieee80211_hw *hw)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
        u8 reg_bw_opmode;
-       u32 reg_ratr, reg_prsr;
+       u32 reg_prsr;
 
        reg_bw_opmode = BW_OPMODE_20MHZ;
-       reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG |
-           RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS;
        reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
 
        rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, 0x8);
@@ -2030,7 +2028,7 @@ bool rtl8723ae_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
        struct rtl_phy *rtlphy = &(rtlpriv->phy);
-       enum rf_pwrstate e_rfpowerstate_toset, cur_rfstate;
+       enum rf_pwrstate e_rfpowerstate_toset;
        u8 u1tmp;
        bool actuallyset = false;
 
@@ -2049,8 +2047,6 @@ bool rtl8723ae_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid)
                spin_unlock(&rtlpriv->locks.rf_ps_lock);
        }
 
-       cur_rfstate = ppsc->rfpwr_state;
-
        rtl_write_byte(rtlpriv, REG_GPIO_IO_SEL_2,
                       rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL_2)&~(BIT(1)));
 
index 3d8536b..eafbb18 100644 (file)
@@ -614,17 +614,11 @@ bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        int i;
-       bool rtstatus = true;
        u32 *radioa_array_table;
-       u32 *radiob_array_table;
-       u16 radioa_arraylen, radiob_arraylen;
+       u16 radioa_arraylen;
 
        radioa_arraylen = Rtl8723ERADIOA_1TARRAYLENGTH;
        radioa_array_table = RTL8723E_RADIOA_1TARRAY;
-       radiob_arraylen = RTL8723E_RADIOB_1TARRAYLENGTH;
-       radiob_array_table = RTL8723E_RADIOB_1TARRAY;
-
-       rtstatus = true;
 
        switch (rfpath) {
        case RF90_PATH_A:
@@ -1531,11 +1525,8 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw,
                0x522, 0x550, 0x551, 0x040
        };
        const u32 retrycount = 2;
-       u32 bbvalue;
 
        if (t == 0) {
-               bbvalue = rtl_get_bbreg(hw, 0x800, MASKDWORD);
-
                phy_save_adda_regs(hw, adda_reg, rtlphy->adda_backup, 16);
                phy_save_mac_regs(hw, iqk_mac_reg, rtlphy->iqk_mac_backup);
        }
@@ -1712,8 +1703,7 @@ void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
        long result[4][8];
        u8 i, final_candidate;
        bool patha_ok, pathb_ok;
-       long reg_e94, reg_e9c, reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4,
-           reg_ecc, reg_tmp = 0;
+       long reg_e94, reg_e9c, reg_ea4, reg_eb4, reg_ebc, reg_tmp = 0;
        bool is12simular, is13simular, is23simular;
        bool start_conttx = false, singletone = false;
        u32 iqk_bb_reg[10] = {
@@ -1780,21 +1770,15 @@ void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery)
                reg_e94 = result[i][0];
                reg_e9c = result[i][1];
                reg_ea4 = result[i][2];
-               reg_eac = result[i][3];
                reg_eb4 = result[i][4];
                reg_ebc = result[i][5];
-               reg_ec4 = result[i][6];
-               reg_ecc = result[i][7];
        }
        if (final_candidate != 0xff) {
                rtlphy->reg_e94 = reg_e94 = result[final_candidate][0];
                rtlphy->reg_e9c = reg_e9c = result[final_candidate][1];
                reg_ea4 = result[final_candidate][2];
-               reg_eac = result[final_candidate][3];
                rtlphy->reg_eb4 = reg_eb4 = result[final_candidate][4];
                rtlphy->reg_ebc = reg_ebc = result[final_candidate][5];
-               reg_ec4 = result[final_candidate][6];
-               reg_ecc = result[final_candidate][7];
                patha_ok = pathb_ok = true;
        } else {
                rtlphy->reg_e94 = rtlphy->reg_eb4 = 0x100;
index ce8ad12..b1fd2b3 100644 (file)
@@ -244,7 +244,6 @@ static void _rtl8723ae_translate_rx_signal_stuff(struct ieee80211_hw *hw,
        struct ieee80211_hdr *hdr;
        u8 *tmp_buf;
        u8 *praddr;
-       u8 *psaddr;
        __le16 fc;
        u16 type;
        bool packet_matchbssid, packet_toself, packet_beacon = false;
@@ -255,7 +254,6 @@ static void _rtl8723ae_translate_rx_signal_stuff(struct ieee80211_hw *hw,
        fc = hdr->frame_control;
        type = WLAN_FC_GET_TYPE(fc);
        praddr = hdr->addr1;
-       psaddr = ieee80211_get_SA(hdr);
 
        packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) &&
                            (!compare_ether_addr(mac->bssid,
index 1535efd..d42bbe2 100644 (file)
@@ -825,8 +825,6 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb,
        u32 ep_num;
        struct urb *_urb = NULL;
        struct sk_buff *_skb = NULL;
-       struct sk_buff_head *skb_list;
-       struct usb_anchor *urb_list;
 
        WARN_ON(NULL == rtlusb->usb_tx_aggregate_hdl);
        if (unlikely(IS_USB_STOP(rtlusb))) {
@@ -836,7 +834,6 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb,
                return;
        }
        ep_num = rtlusb->ep_map.ep_mapping[qnum];
-       skb_list = &rtlusb->tx_skb_queue[ep_num];
        _skb = skb;
        _urb = _rtl_usb_tx_urb_setup(hw, _skb, ep_num);
        if (unlikely(!_urb)) {
@@ -844,7 +841,6 @@ static void _rtl_usb_transmit(struct ieee80211_hw *hw, struct sk_buff *skb,
                         "Can't allocate urb. Drop skb!\n");
                return;
        }
-       urb_list = &rtlusb->tx_pending[ep_num];
        _rtl_submit_tx_urb(hw, _urb);
 }
 
index 21a5f4f..f13258a 100644 (file)
@@ -1702,7 +1702,7 @@ struct rtl_works {
 
 struct rtl_debug {
        u32 dbgp_type[DBGP_TYPE_MAX];
-       u32 global_debuglevel;
+       int global_debuglevel;
        u64 global_debugcomponents;
 
        /* add for proc debug */
index 1fb6584..8fec4ed 100644 (file)
@@ -1,6 +1,6 @@
 menuconfig WL1251
        tristate "TI wl1251 driver support"
-       depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS
+       depends on MAC80211 && GENERIC_HARDIRQS
        select FW_LOADER
        select CRC7
        ---help---
index da509aa..e6a2405 100644 (file)
@@ -1,3 +1,3 @@
-wl12xx-objs    = main.o cmd.o acx.o debugfs.o
+wl12xx-objs    = main.o cmd.o acx.o debugfs.o scan.o event.o
 
 obj-$(CONFIG_WL12XX)           += wl12xx.o
index 6222062..7dc9f96 100644 (file)
@@ -284,3 +284,40 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)
        kfree(radio_parms);
        return ret;
 }
+
+int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch)
+{
+       struct wl12xx_cmd_channel_switch *cmd;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "cmd channel switch");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+       cmd->channel = ch_switch->channel->hw_value;
+       cmd->switch_time = ch_switch->count;
+       cmd->stop_tx = ch_switch->block_tx;
+
+       /* FIXME: control from mac80211 in the future */
+       /* Enable TX on the target channel */
+       cmd->post_switch_tx_disable = 0;
+
+       ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send channel switch command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+
+out:
+       return ret;
+}
index 140a0e8..32cbad5 100644 (file)
@@ -103,10 +103,30 @@ struct wl1271_ext_radio_parms_cmd {
        u8 padding[3];
 } __packed;
 
+struct wl12xx_cmd_channel_switch {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+
+       /* The new serving channel */
+       u8 channel;
+       /* Relative time of the serving channel switch in TBTT units */
+       u8 switch_time;
+       /* Stop the role TX, should expect it after radar detection */
+       u8 stop_tx;
+       /* The target channel tx status 1-stopped 0-open*/
+       u8 post_switch_tx_disable;
+
+       u8 padding[3];
+} __packed;
+
 int wl1271_cmd_general_parms(struct wl1271 *wl);
 int wl128x_cmd_general_parms(struct wl1271 *wl);
 int wl1271_cmd_radio_parms(struct wl1271 *wl);
 int wl128x_cmd_radio_parms(struct wl1271 *wl);
 int wl1271_cmd_ext_radio_parms(struct wl1271 *wl);
+int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
 
 #endif /* __WL12XX_CMD_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/event.c b/drivers/net/wireless/ti/wl12xx/event.c
new file mode 100644 (file)
index 0000000..6ac0ed7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "event.h"
+#include "scan.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+
+int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout)
+{
+       u32 local_event;
+
+       switch (event) {
+       case WLCORE_EVENT_ROLE_STOP_COMPLETE:
+               local_event = ROLE_STOP_COMPLETE_EVENT_ID;
+               break;
+
+       case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
+               local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
+               break;
+
+       default:
+               /* event not implemented */
+               return 0;
+       }
+       return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
+}
+
+int wl12xx_process_mailbox_events(struct wl1271 *wl)
+{
+       struct wl12xx_event_mailbox *mbox = wl->mbox;
+       u32 vector;
+
+
+       vector = le32_to_cpu(mbox->events_vector);
+       vector &= ~(le32_to_cpu(mbox->events_mask));
+
+       wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
+
+       if (vector & SCAN_COMPLETE_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT, "status: 0x%x",
+                            mbox->scheduled_scan_status);
+
+               if (wl->scan_wlvif)
+                       wl12xx_scan_completed(wl, wl->scan_wlvif);
+       }
+
+       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT,
+                            "PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)",
+                            mbox->scheduled_scan_status);
+
+               wlcore_scan_sched_scan_results(wl);
+       }
+
+       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
+               wlcore_event_sched_scan_completed(wl,
+                                                 mbox->scheduled_scan_status);
+       if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
+               wlcore_event_soft_gemini_sense(wl,
+                                              mbox->soft_gemini_sense_info);
+
+       if (vector & BSS_LOSE_EVENT_ID)
+               wlcore_event_beacon_loss(wl, 0xff);
+
+       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
+               wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
+
+       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
+               wlcore_event_ba_rx_constraint(wl,
+                                             BIT(mbox->role_id),
+                                             mbox->rx_ba_allowed);
+
+       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
+               wlcore_event_channel_switch(wl, 0xff,
+                                           mbox->channel_switch_status);
+
+       if (vector & DUMMY_PACKET_EVENT_ID)
+               wlcore_event_dummy_packet(wl);
+
+       /*
+        * "TX retries exceeded" has a different meaning according to mode.
+        * In AP mode the offending station is disconnected.
+        */
+       if (vector & MAX_TX_RETRY_EVENT_ID)
+               wlcore_event_max_tx_failure(wl,
+                               le16_to_cpu(mbox->sta_tx_retry_exceeded));
+
+       if (vector & INACTIVE_STA_EVENT_ID)
+               wlcore_event_inactive_sta(wl,
+                                         le16_to_cpu(mbox->sta_aging_status));
+
+       if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
+               wlcore_event_roc_complete(wl);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/event.h b/drivers/net/wireless/ti/wl12xx/event.h
new file mode 100644 (file)
index 0000000..a5cc3fc
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_EVENT_H__
+#define __WL12XX_EVENT_H__
+
+#include "../wlcore/wlcore.h"
+
+enum {
+       MEASUREMENT_START_EVENT_ID               = BIT(8),
+       MEASUREMENT_COMPLETE_EVENT_ID            = BIT(9),
+       SCAN_COMPLETE_EVENT_ID                   = BIT(10),
+       WFD_DISCOVERY_COMPLETE_EVENT_ID          = BIT(11),
+       AP_DISCOVERY_COMPLETE_EVENT_ID           = BIT(12),
+       RESERVED1                                = BIT(13),
+       PSPOLL_DELIVERY_FAILURE_EVENT_ID         = BIT(14),
+       ROLE_STOP_COMPLETE_EVENT_ID              = BIT(15),
+       RADAR_DETECTED_EVENT_ID                  = BIT(16),
+       CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(17),
+       BSS_LOSE_EVENT_ID                        = BIT(18),
+       REGAINED_BSS_EVENT_ID                    = BIT(19),
+       MAX_TX_RETRY_EVENT_ID                    = BIT(20),
+       DUMMY_PACKET_EVENT_ID                    = BIT(21),
+       SOFT_GEMINI_SENSE_EVENT_ID               = BIT(22),
+       CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID        = BIT(23),
+       SOFT_GEMINI_AVALANCHE_EVENT_ID           = BIT(24),
+       PLT_RX_CALIBRATION_COMPLETE_EVENT_ID     = BIT(25),
+       INACTIVE_STA_EVENT_ID                    = BIT(26),
+       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(27),
+       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(28),
+       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(29),
+       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(30),
+       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(31),
+};
+
+struct wl12xx_event_mailbox {
+       __le32 events_vector;
+       __le32 events_mask;
+       __le32 reserved_1;
+       __le32 reserved_2;
+
+       u8 number_of_scan_results;
+       u8 scan_tag;
+       u8 completed_scan_status;
+       u8 reserved_3;
+
+       u8 soft_gemini_sense_info;
+       u8 soft_gemini_protective_info;
+       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
+       u8 change_auto_mode_timeout;
+       u8 scheduled_scan_status;
+       u8 reserved4;
+       /* tuned channel (roc) */
+       u8 roc_channel;
+
+       __le16 hlid_removed_bitmap;
+
+       /* bitmap of aged stations (by HLID) */
+       __le16 sta_aging_status;
+
+       /* bitmap of stations (by HLID) which exceeded max tx retries */
+       __le16 sta_tx_retry_exceeded;
+
+       /* discovery completed results */
+       u8 discovery_tag;
+       u8 number_of_preq_results;
+       u8 number_of_prsp_results;
+       u8 reserved_5;
+
+       /* rx ba constraint */
+       u8 role_id; /* 0xFF means any role. */
+       u8 rx_ba_allowed;
+       u8 reserved_6[2];
+
+       /* Channel switch results */
+
+       u8 channel_switch_role_id;
+       u8 channel_switch_status;
+       u8 reserved_7[2];
+
+       u8 ps_poll_delivery_failure_role_ids;
+       u8 stopped_role_ids;
+       u8 started_role_ids;
+
+       u8 reserved_8[9];
+} __packed;
+
+int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout);
+int wl12xx_process_mailbox_events(struct wl1271 *wl);
+
+#endif
+
index e5f5f8f..3254bfc 100644 (file)
@@ -38,6 +38,8 @@
 #include "reg.h"
 #include "cmd.h"
 #include "acx.h"
+#include "scan.h"
+#include "event.h"
 #include "debugfs.h"
 
 static char *fref_param;
@@ -208,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = {
                .tmpl_short_retry_limit      = 10,
                .tmpl_long_retry_limit       = 10,
                .tx_watchdog_timeout         = 5000,
+               .slow_link_thold             = 3,
+               .fast_link_thold             = 10,
        },
        .conn = {
                .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
@@ -265,8 +269,10 @@ static struct wlcore_conf wl12xx_conf = {
        .scan = {
                .min_dwell_time_active        = 7500,
                .max_dwell_time_active        = 30000,
-               .min_dwell_time_passive       = 100000,
-               .max_dwell_time_passive       = 100000,
+               .min_dwell_time_active_long   = 25000,
+               .max_dwell_time_active_long   = 50000,
+               .dwell_time_passive           = 100000,
+               .dwell_time_dfs               = 150000,
                .num_probe_reqs               = 2,
                .split_scan_timeout           = 50000,
        },
@@ -368,6 +374,10 @@ static struct wlcore_conf wl12xx_conf = {
                .increase_time              = 1,
                .window_size                = 16,
        },
+       .recovery = {
+               .bug_on_recovery            = 0,
+               .no_recovery                = 0,
+       },
 };
 
 static struct wl12xx_priv_conf wl12xx_default_priv_conf = {
@@ -601,9 +611,9 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
 {
        int ret;
 
-       if (wl->chip.id != CHIP_ID_1283_PG20) {
+       if (wl->chip.id != CHIP_ID_128X_PG20) {
                struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
-               struct wl127x_rx_mem_pool_addr rx_mem_addr;
+               struct wl12xx_priv *priv = wl->priv;
 
                /*
                 * Choose the block we want to read
@@ -612,13 +622,13 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
                 */
                u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK;
 
-               rx_mem_addr.addr = (mem_block << 8) +
+               priv->rx_mem_addr->addr = (mem_block << 8) +
                        le32_to_cpu(wl_mem_map->packet_memory_pool_start);
 
-               rx_mem_addr.addr_extra = rx_mem_addr.addr + 4;
+               priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4;
 
-               ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr,
-                                  sizeof(rx_mem_addr), false);
+               ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr,
+                                  sizeof(*priv->rx_mem_addr), false);
                if (ret < 0)
                        return ret;
        }
@@ -631,13 +641,15 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
        int ret = 0;
 
        switch (wl->chip.id) {
-       case CHIP_ID_1271_PG10:
+       case CHIP_ID_127X_PG10:
                wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
                               wl->chip.id);
 
                wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
                memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@@ -646,18 +658,22 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* read data preparation is only needed by wl127x */
                wl->ops->prepare_read = wl127x_prepare_read;
 
-               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
-                                     WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
-                                     WL127X_MINOR_VER);
+               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
+                             WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
+                             WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
+                             WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
+                             WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
                break;
 
-       case CHIP_ID_1271_PG20:
+       case CHIP_ID_127X_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
                             wl->chip.id);
 
                wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
                wl->plt_fw_name = WL127X_PLT_FW_NAME;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@@ -667,12 +683,14 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* read data preparation is only needed by wl127x */
                wl->ops->prepare_read = wl127x_prepare_read;
 
-               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
-                                     WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
-                                     WL127X_MINOR_VER);
+               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
+                             WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
+                             WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
+                             WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
+                             WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
                break;
 
-       case CHIP_ID_1283_PG20:
+       case CHIP_ID_128X_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
                             wl->chip.id);
                wl->plt_fw_name = WL128X_PLT_FW_NAME;
@@ -682,19 +700,29 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* wl128x requires TX blocksize alignment */
                wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
-
-               wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER,
-                                     WL128X_MAJOR_VER, WL128X_SUBTYPE_VER,
-                                     WL128X_MINOR_VER);
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
+
+               wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER,
+                             WL128X_IFTYPE_SR_VER,  WL128X_MAJOR_SR_VER,
+                             WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER,
+                             WL128X_IFTYPE_MR_VER,  WL128X_MAJOR_MR_VER,
+                             WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER);
                break;
-       case CHIP_ID_1283_PG10:
+       case CHIP_ID_128X_PG10:
        default:
                wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
                ret = -ENODEV;
                goto out;
        }
 
+       /* common settings */
+       wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
+       wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
+       wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+       wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;
 out:
        return ret;
 }
@@ -1067,7 +1095,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
        u32 clk;
        int selected_clock = -1;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                ret = wl128x_boot_clk(wl, &selected_clock);
                if (ret < 0)
                        goto out;
@@ -1098,7 +1126,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
 
        wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
 
-       if (wl->chip.id == CHIP_ID_1283_PG20)
+       if (wl->chip.id == CHIP_ID_128X_PG20)
                clk |= ((selected_clock & 0x3) << 1) << 4;
        else
                clk |= (priv->ref_clock << 1) << 4;
@@ -1152,7 +1180,7 @@ static int wl12xx_pre_upload(struct wl1271 *wl)
        /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
         * to upload_fw) */
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
                if (ret < 0)
                        goto out;
@@ -1219,6 +1247,23 @@ static int wl12xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
+       wl->event_mask = BSS_LOSE_EVENT_ID |
+               REGAINED_BSS_EVENT_ID |
+               SCAN_COMPLETE_EVENT_ID |
+               ROLE_STOP_COMPLETE_EVENT_ID |
+               RSSI_SNR_TRIGGER_0_EVENT_ID |
+               PSPOLL_DELIVERY_FAILURE_EVENT_ID |
+               SOFT_GEMINI_SENSE_EVENT_ID |
+               PERIODIC_SCAN_REPORT_EVENT_ID |
+               PERIODIC_SCAN_COMPLETE_EVENT_ID |
+               DUMMY_PACKET_EVENT_ID |
+               PEER_REMOVE_COMPLETE_EVENT_ID |
+               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+               INACTIVE_STA_EVENT_ID |
+               MAX_TX_RETRY_EVENT_ID |
+               CHANNEL_SWITCH_COMPLETE_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1261,7 +1306,7 @@ static void
 wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
                          u32 blks, u32 spare_blks)
 {
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                desc->wl128x_mem.total_mem_blocks = blks;
        } else {
                desc->wl127x_mem.extra_blocks = spare_blks;
@@ -1275,7 +1320,7 @@ wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
 {
        u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len);
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                desc->wl128x_mem.extra_bytes = aligned_len - skb->len;
                desc->length = cpu_to_le16(aligned_len >> 2);
 
@@ -1339,7 +1384,7 @@ static int wl12xx_hw_init(struct wl1271 *wl)
 {
        int ret;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
 
                ret = wl128x_cmd_general_parms(wl);
@@ -1394,22 +1439,6 @@ static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
        return wlvif->rate_set;
 }
 
-static int wl12xx_identify_fw(struct wl1271 *wl)
-{
-       unsigned int *fw_ver = wl->chip.fw_ver;
-
-       /* Only new station firmwares support routing fw logs to the host */
-       if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
-           (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN))
-               wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED;
-
-       /* This feature is not yet supported for AP mode */
-       if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP)
-               wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED;
-
-       return 0;
-}
-
 static void wl12xx_conf_init(struct wl1271 *wl)
 {
        struct wl12xx_priv *priv = wl->priv;
@@ -1426,7 +1455,7 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
        bool supported = false;
        u8 major, minor;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
                minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
 
@@ -1482,7 +1511,7 @@ static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
        u16 die_info;
        int ret;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20)
+       if (wl->chip.id == CHIP_ID_128X_PG20)
                ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,
                                          &die_info);
        else
@@ -1589,16 +1618,46 @@ static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
        return wlcore_set_key(wl, cmd, vif, sta, key_conf);
 }
 
+static int wl12xx_set_peer_cap(struct wl1271 *wl,
+                              struct ieee80211_sta_ht_cap *ht_cap,
+                              bool allow_ht_operation,
+                              u32 rate_set, u8 hlid)
+{
+       return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation,
+                                             hlid);
+}
+
+static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                                struct wl1271_link *lnk)
+{
+       u8 thold;
+
+       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
+               thold = wl->conf.tx.fast_link_thold;
+       else
+               thold = wl->conf.tx.slow_link_thold;
+
+       return lnk->allocated_pkts < thold;
+}
+
+static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                               struct wl1271_link *lnk)
+{
+       /* any link is good for low priority */
+       return true;
+}
+
 static int wl12xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl12xx_ops = {
        .setup                  = wl12xx_setup,
        .identify_chip          = wl12xx_identify_chip,
-       .identify_fw            = wl12xx_identify_fw,
        .boot                   = wl12xx_boot,
        .plt_init               = wl12xx_plt_init,
        .trigger_cmd            = wl12xx_trigger_cmd,
        .ack_event              = wl12xx_ack_event,
+       .wait_for_event         = wl12xx_wait_for_event,
+       .process_mailbox_events = wl12xx_process_mailbox_events,
        .calc_tx_blocks         = wl12xx_calc_tx_blocks,
        .set_tx_desc_blocks     = wl12xx_set_tx_desc_blocks,
        .set_tx_desc_data_len   = wl12xx_set_tx_desc_data_len,
@@ -1615,9 +1674,17 @@ static struct wlcore_ops wl12xx_ops = {
        .set_rx_csum            = NULL,
        .ap_get_mimo_wide_rate_mask = NULL,
        .debugfs_init           = wl12xx_debugfs_add_files,
+       .scan_start             = wl12xx_scan_start,
+       .scan_stop              = wl12xx_scan_stop,
+       .sched_scan_start       = wl12xx_sched_scan_start,
+       .sched_scan_stop        = wl12xx_scan_sched_scan_stop,
        .get_spare_blocks       = wl12xx_get_spare_blocks,
        .set_key                = wl12xx_set_key,
+       .channel_switch         = wl12xx_cmd_channel_switch,
        .pre_pkt_send           = NULL,
+       .set_peer_cap           = wl12xx_set_peer_cap,
+       .lnk_high_prio          = wl12xx_lnk_high_prio,
+       .lnk_low_prio           = wl12xx_lnk_low_prio,
 };
 
 static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@@ -1641,6 +1708,7 @@ static int wl12xx_setup(struct wl1271 *wl)
        wl->rtable = wl12xx_rtable;
        wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
        wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
+       wl->num_channels = 1;
        wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
@@ -1693,6 +1761,10 @@ static int wl12xx_setup(struct wl1271 *wl)
                        wl1271_error("Invalid tcxo parameter %s", tcxo_param);
        }
 
+       priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL);
+       if (!priv->rx_mem_addr)
+               return -ENOMEM;
+
        return 0;
 }
 
@@ -1703,7 +1775,8 @@ static int wl12xx_probe(struct platform_device *pdev)
        int ret;
 
        hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv),
-                            WL12XX_AGGR_BUFFER_SIZE);
+                            WL12XX_AGGR_BUFFER_SIZE,
+                            sizeof(struct wl12xx_event_mailbox));
        if (IS_ERR(hw)) {
                wl1271_error("can't allocate hw");
                ret = PTR_ERR(hw);
@@ -1725,6 +1798,21 @@ out:
        return ret;
 }
 
+static int wl12xx_remove(struct platform_device *pdev)
+{
+       struct wl1271 *wl = platform_get_drvdata(pdev);
+       struct wl12xx_priv *priv;
+
+       if (!wl)
+               goto out;
+       priv = wl->priv;
+
+       kfree(priv->rx_mem_addr);
+
+out:
+       return wlcore_remove(pdev);
+}
+
 static const struct platform_device_id wl12xx_id_table[] = {
        { "wl12xx", 0 },
        {  } /* Terminating Entry */
@@ -1733,7 +1821,7 @@ MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
 
 static struct platform_driver wl12xx_driver = {
        .probe          = wl12xx_probe,
-       .remove         = wlcore_remove,
+       .remove         = wl12xx_remove,
        .id_table       = wl12xx_id_table,
        .driver = {
                .name   = "wl12xx_driver",
diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c
new file mode 100644 (file)
index 0000000..affdb3e
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/ieee80211.h>
+#include "scan.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/tx.h"
+
+static int wl1271_get_scan_channels(struct wl1271 *wl,
+                                   struct cfg80211_scan_request *req,
+                                   struct basic_scan_channel_params *channels,
+                                   enum ieee80211_band band, bool passive)
+{
+       struct conf_scan_settings *c = &wl->conf.scan;
+       int i, j;
+       u32 flags;
+
+       for (i = 0, j = 0;
+            i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
+            i++) {
+               flags = req->channels[i]->flags;
+
+               if (!test_bit(i, wl->scan.scanned_ch) &&
+                   !(flags & IEEE80211_CHAN_DISABLED) &&
+                   (req->channels[i]->band == band) &&
+                   /*
+                    * In passive scans, we scan all remaining
+                    * channels, even if not marked as such.
+                    * In active scans, we only scan channels not
+                    * marked as passive.
+                    */
+                   (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
+                       wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
+                                    req->channels[i]->band,
+                                    req->channels[i]->center_freq);
+                       wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
+                                    req->channels[i]->hw_value,
+                                    req->channels[i]->flags);
+                       wl1271_debug(DEBUG_SCAN,
+                                    "max_antenna_gain %d, max_power %d",
+                                    req->channels[i]->max_antenna_gain,
+                                    req->channels[i]->max_power);
+                       wl1271_debug(DEBUG_SCAN, "beacon_found %d",
+                                    req->channels[i]->beacon_found);
+
+                       if (!passive) {
+                               channels[j].min_duration =
+                                       cpu_to_le32(c->min_dwell_time_active);
+                               channels[j].max_duration =
+                                       cpu_to_le32(c->max_dwell_time_active);
+                       } else {
+                               channels[j].min_duration =
+                                       cpu_to_le32(c->dwell_time_passive);
+                               channels[j].max_duration =
+                                       cpu_to_le32(c->dwell_time_passive);
+                       }
+                       channels[j].early_termination = 0;
+                       channels[j].tx_power_att = req->channels[i]->max_power;
+                       channels[j].channel = req->channels[i]->hw_value;
+
+                       memset(&channels[j].bssid_lsb, 0xff, 4);
+                       memset(&channels[j].bssid_msb, 0xff, 2);
+
+                       /* Mark the channels we already used */
+                       set_bit(i, wl->scan.scanned_ch);
+
+                       j++;
+               }
+       }
+
+       return j;
+}
+
+#define WL1271_NOTHING_TO_SCAN 1
+
+static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           enum ieee80211_band band,
+                           bool passive, u32 basic_rate)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wl1271_cmd_scan *cmd;
+       struct wl1271_cmd_trigger_scan_to *trigger;
+       int ret;
+       u16 scan_options = 0;
+
+       /* skip active scans if we don't have SSIDs */
+       if (!passive && wl->scan.req->n_ssids == 0)
+               return WL1271_NOTHING_TO_SCAN;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
+       if (!cmd || !trigger) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (wl->conf.scan.split_scan_timeout)
+               scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
+
+       if (passive)
+               scan_options |= WL1271_SCAN_OPT_PASSIVE;
+
+       cmd->params.role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->params.scan_options = cpu_to_le16(scan_options);
+
+       cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
+                                                   cmd->channels,
+                                                   band, passive);
+       if (cmd->params.n_ch == 0) {
+               ret = WL1271_NOTHING_TO_SCAN;
+               goto out;
+       }
+
+       cmd->params.tx_rate = cpu_to_le32(basic_rate);
+       cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
+       cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
+       cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
+
+       if (band == IEEE80211_BAND_2GHZ)
+               cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
+       else
+               cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
+
+       if (wl->scan.ssid_len && wl->scan.ssid) {
+               cmd->params.ssid_len = wl->scan.ssid_len;
+               memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
+       }
+
+       memcpy(cmd->addr, vif->addr, ETH_ALEN);
+
+       ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                        cmd->params.role_id, band,
+                                        wl->scan.ssid, wl->scan.ssid_len,
+                                        wl->scan.req->ie,
+                                        wl->scan.req->ie_len, false);
+       if (ret < 0) {
+               wl1271_error("PROBE request template failed");
+               goto out;
+       }
+
+       trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
+       ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
+                             sizeof(*trigger), 0);
+       if (ret < 0) {
+               wl1271_error("trigger scan to failed for hw scan");
+               goto out;
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd);
+       kfree(trigger);
+       return ret;
+}
+
+int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_header *cmd = NULL;
+       int ret = 0;
+
+       if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
+               return -EINVAL;
+
+       wl1271_debug(DEBUG_CMD, "cmd scan stop");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
+                             sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("cmd stop_scan failed");
+               goto out;
+       }
+out:
+       kfree(cmd);
+       return ret;
+}
+
+void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret = 0;
+       enum ieee80211_band band;
+       u32 rate, mask;
+
+       switch (wl->scan.state) {
+       case WL1271_SCAN_STATE_IDLE:
+               break;
+
+       case WL1271_SCAN_STATE_2GHZ_ACTIVE:
+               band = IEEE80211_BAND_2GHZ;
+               mask = wlvif->bitrate_masks[band];
+               if (wl->scan.req->no_cck) {
+                       mask &= ~CONF_TX_CCK_RATES;
+                       if (!mask)
+                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
+               }
+               rate = wl1271_tx_min_rate_get(wl, mask);
+               ret = wl1271_scan_send(wl, wlvif, band, false, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_2GHZ_PASSIVE:
+               band = IEEE80211_BAND_2GHZ;
+               mask = wlvif->bitrate_masks[band];
+               if (wl->scan.req->no_cck) {
+                       mask &= ~CONF_TX_CCK_RATES;
+                       if (!mask)
+                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
+               }
+               rate = wl1271_tx_min_rate_get(wl, mask);
+               ret = wl1271_scan_send(wl, wlvif, band, true, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       if (wl->enable_11a)
+                               wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
+                       else
+                               wl->scan.state = WL1271_SCAN_STATE_DONE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_5GHZ_ACTIVE:
+               band = IEEE80211_BAND_5GHZ;
+               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+               ret = wl1271_scan_send(wl, wlvif, band, false, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_5GHZ_PASSIVE:
+               band = IEEE80211_BAND_5GHZ;
+               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+               ret = wl1271_scan_send(wl, wlvif, band, true, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_DONE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_DONE:
+               wl->scan.failed = false;
+               cancel_delayed_work(&wl->scan_complete_work);
+               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                            msecs_to_jiffies(0));
+               break;
+
+       default:
+               wl1271_error("invalid scan state");
+               break;
+       }
+
+       if (ret < 0) {
+               cancel_delayed_work(&wl->scan_complete_work);
+               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                            msecs_to_jiffies(0));
+       }
+}
+
+static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
+                                  struct wlcore_scan_channels *cmd_channels)
+{
+       memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
+       memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
+       cmd->dfs = cmd_channels->dfs;
+       cmd->n_pactive_ch = cmd_channels->passive_active;
+
+       memcpy(cmd->channels_2, cmd_channels->channels_2,
+              sizeof(cmd->channels_2));
+       memcpy(cmd->channels_5, cmd_channels->channels_5,
+              sizeof(cmd->channels_2));
+       /* channels_4 are not supported, so no need to copy them */
+}
+
+int wl1271_scan_sched_scan_config(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif,
+                                 struct cfg80211_sched_scan_request *req,
+                                 struct ieee80211_sched_scan_ies *ies)
+{
+       struct wl1271_cmd_sched_scan_config *cfg = NULL;
+       struct wlcore_scan_channels *cfg_channels = NULL;
+       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+       int i, ret;
+       bool force_passive = !req->n_ssids;
+
+       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->role_id = wlvif->role_id;
+       cfg->rssi_threshold = c->rssi_threshold;
+       cfg->snr_threshold  = c->snr_threshold;
+       cfg->n_probe_reqs = c->num_probe_reqs;
+       /* cycles set to 0 it means infinite (until manually stopped) */
+       cfg->cycles = 0;
+       /* report APs when at least 1 is found */
+       cfg->report_after = 1;
+       /* don't stop scanning automatically when something is found */
+       cfg->terminate = 0;
+       cfg->tag = WL1271_SCAN_DEFAULT_TAG;
+       /* don't filter on BSS type */
+       cfg->bss_type = SCAN_BSS_TYPE_ANY;
+       /* currently NL80211 supports only a single interval */
+       for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
+               cfg->intervals[i] = cpu_to_le32(req->interval);
+
+       cfg->ssid_len = 0;
+       ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
+       if (ret < 0)
+               goto out;
+
+       cfg->filter_type = ret;
+
+       wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
+
+       cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL);
+       if (!cfg_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels,
+                                        req->n_channels, req->n_ssids,
+                                        SCAN_TYPE_PERIODIC)) {
+               wl1271_error("scan channel list is empty");
+               ret = -EINVAL;
+               goto out;
+       }
+       wl12xx_adjust_channels(cfg, cfg_channels);
+
+       if (!force_passive && cfg->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                                wlvif->role_id, band,
+                                                req->ssids[0].ssid,
+                                                req->ssids[0].ssid_len,
+                                                ies->ie[band],
+                                                ies->len[band], true);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (!force_passive && cfg->active[1]) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                                wlvif->role_id, band,
+                                                req->ssids[0].ssid,
+                                                req->ssids[0].ssid_len,
+                                                ies->ie[band],
+                                                ies->len[band], true);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
+
+       ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
+                             sizeof(*cfg), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN configuration failed");
+               goto out;
+       }
+out:
+       kfree(cfg_channels);
+       kfree(cfg);
+       return ret;
+}
+
+int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_sched_scan_start *start;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
+
+       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+               return -EOPNOTSUPP;
+
+       if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
+           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
+               return -EBUSY;
+
+       start = kzalloc(sizeof(*start), GFP_KERNEL);
+       if (!start)
+               return -ENOMEM;
+
+       start->role_id = wlvif->role_id;
+       start->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
+                             sizeof(*start), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send scan start command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(start);
+       return ret;
+}
+
+int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies)
+{
+       int ret;
+
+       ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
+       if (ret < 0)
+               return ret;
+
+       return wl1271_scan_sched_scan_start(wl, wlvif);
+}
+
+void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_sched_scan_stop *stop;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+       /* FIXME: what to do if alloc'ing to stop fails? */
+       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+       if (!stop) {
+               wl1271_error("failed to alloc memory to send sched scan stop");
+               return;
+       }
+
+       stop->role_id = wlvif->role_id;
+       stop->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
+                             sizeof(*stop), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send sched scan stop command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(stop);
+}
+
+int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req)
+{
+       wl1271_scan_stm(wl, wlvif);
+       return 0;
+}
+
+void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       wl1271_scan_stm(wl, wlvif);
+}
diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h
new file mode 100644 (file)
index 0000000..264af7a
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_SCAN_H__
+#define __WL12XX_SCAN_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/scan.h"
+
+#define WL12XX_MAX_CHANNELS_5GHZ 23
+
+struct basic_scan_params {
+       /* Scan option flags (WL1271_SCAN_OPT_*) */
+       __le16 scan_options;
+       u8 role_id;
+       /* Number of scan channels in the list (maximum 30) */
+       u8 n_ch;
+       /* This field indicates the number of probe requests to send
+          per channel for an active scan */
+       u8 n_probe_reqs;
+       u8 tid_trigger;
+       u8 ssid_len;
+       u8 use_ssid_list;
+
+       /* Rate bit field for sending the probes */
+       __le32 tx_rate;
+
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       /* Band to scan */
+       u8 band;
+
+       u8 scan_tag;
+       u8 padding2[2];
+} __packed;
+
+struct basic_scan_channel_params {
+       /* Duration in TU to wait for frames on a channel for active scan */
+       __le32 min_duration;
+       __le32 max_duration;
+       __le32 bssid_lsb;
+       __le16 bssid_msb;
+       u8 early_termination;
+       u8 tx_power_att;
+       u8 channel;
+       /* FW internal use only! */
+       u8 dfs_candidate;
+       u8 activity_detected;
+       u8 pad;
+} __packed;
+
+struct wl1271_cmd_scan {
+       struct wl1271_cmd_header header;
+
+       struct basic_scan_params params;
+       struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
+
+       /* src mac address */
+       u8 addr[ETH_ALEN];
+       u8 padding[2];
+} __packed;
+
+struct wl1271_cmd_sched_scan_config {
+       struct wl1271_cmd_header header;
+
+       __le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
+
+       s8 rssi_threshold; /* for filtering (in dBm) */
+       s8 snr_threshold;  /* for filtering (in dB) */
+
+       u8 cycles;       /* maximum number of scan cycles */
+       u8 report_after; /* report when this number of results are received */
+       u8 terminate;    /* stop scanning after reporting */
+
+       u8 tag;
+       u8 bss_type; /* for filtering */
+       u8 filter_type;
+
+       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+       u8 n_probe_reqs; /* Number of probes requests per channel */
+
+       u8 passive[SCAN_MAX_BANDS];
+       u8 active[SCAN_MAX_BANDS];
+
+       u8 dfs;
+
+       u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
+                           channels in BG band */
+       u8 role_id;
+       u8 padding[1];
+       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+       struct conn_scan_ch_params channels_5[WL12XX_MAX_CHANNELS_5GHZ];
+       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+} __packed;
+
+struct wl1271_cmd_sched_scan_start {
+       struct wl1271_cmd_header header;
+
+       u8 tag;
+       u8 role_id;
+       u8 padding[2];
+} __packed;
+
+struct wl1271_cmd_sched_scan_stop {
+       struct wl1271_cmd_header header;
+
+       u8 tag;
+       u8 role_id;
+       u8 padding[2];
+} __packed;
+
+int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req);
+int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies);
+void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
+#endif
index 7182bbf..d455285 100644 (file)
 
 #include "conf.h"
 
-/* minimum FW required for driver for wl127x */
+/* WiLink 6/7 chip IDs */
+#define CHIP_ID_127X_PG10              (0x04030101)
+#define CHIP_ID_127X_PG20              (0x04030111)
+#define CHIP_ID_128X_PG10              (0x05030101)
+#define CHIP_ID_128X_PG20              (0x05030111)
+
+/* FW chip version for wl127x */
 #define WL127X_CHIP_VER                6
-#define WL127X_IFTYPE_VER      3
-#define WL127X_MAJOR_VER       10
-#define WL127X_SUBTYPE_VER     2
-#define WL127X_MINOR_VER       115
+/* minimum single-role FW version for wl127x */
+#define WL127X_IFTYPE_SR_VER   3
+#define WL127X_MAJOR_SR_VER    10
+#define WL127X_SUBTYPE_SR_VER  WLCORE_FW_VER_IGNORE
+#define WL127X_MINOR_SR_VER    115
+/* minimum multi-role FW version for wl127x */
+#define WL127X_IFTYPE_MR_VER   5
+#define WL127X_MAJOR_MR_VER    7
+#define WL127X_SUBTYPE_MR_VER  WLCORE_FW_VER_IGNORE
+#define WL127X_MINOR_MR_VER    115
 
-/* minimum FW required for driver for wl128x */
+/* FW chip version for wl128x */
 #define WL128X_CHIP_VER                7
-#define WL128X_IFTYPE_VER      3
-#define WL128X_MAJOR_VER       10
-#define WL128X_SUBTYPE_VER     2
-#define WL128X_MINOR_VER       115
+/* minimum single-role FW version for wl128x */
+#define WL128X_IFTYPE_SR_VER   3
+#define WL128X_MAJOR_SR_VER    10
+#define WL128X_SUBTYPE_SR_VER  WLCORE_FW_VER_IGNORE
+#define WL128X_MINOR_SR_VER    115
+/* minimum multi-role FW version for wl128x */
+#define WL128X_IFTYPE_MR_VER   5
+#define WL128X_MAJOR_MR_VER    7
+#define WL128X_SUBTYPE_MR_VER  WLCORE_FW_VER_IGNORE
+#define WL128X_MINOR_MR_VER    42
 
 #define WL12XX_AGGR_BUFFER_SIZE        (4 * PAGE_SIZE)
 
@@ -55,6 +73,8 @@ struct wl12xx_priv {
 
        int ref_clock;
        int tcxo_clock;
+
+       struct wl127x_rx_mem_pool_addr *rx_mem_addr;
 };
 
 #endif /* __WL12XX_PRIV_H__ */
index 67c0987..ae2b817 100644 (file)
@@ -1,3 +1,3 @@
-wl18xx-objs    = main.o acx.o tx.o io.o debugfs.o
+wl18xx-objs    = main.o acx.o tx.o io.o debugfs.o scan.o cmd.o event.o
 
 obj-$(CONFIG_WL18XX)           += wl18xx.o
index 72840e2..a169bb5 100644 (file)
@@ -75,7 +75,7 @@ int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
 
        acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
 
-       ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx));
+       ret = wl1271_cmd_configure(wl, ACX_CSUM_CONFIG, acx, sizeof(*acx));
        if (ret < 0) {
                wl1271_warning("failed to set Tx checksum state: %d", ret);
                goto out;
@@ -109,3 +109,88 @@ out:
        kfree(acx);
        return ret;
 }
+
+int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide)
+{
+       struct wlcore_peer_ht_operation_mode *acx;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d",
+                    hlid, wide);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->hlid = hlid;
+       acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ;
+
+       ret = wl1271_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx,
+                                  sizeof(*acx));
+
+       if (ret < 0) {
+               wl1271_warning("acx peer ht operation mode failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+
+}
+
+/*
+ * this command is basically the same as wl1271_acx_ht_capabilities,
+ * with the addition of supported rates. they should be unified in
+ * the next fw api change
+ */
+int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid)
+{
+       struct wlcore_acx_peer_cap *acx;
+       int ret = 0;
+       u32 ht_capabilites = 0;
+
+       wl1271_debug(DEBUG_ACX,
+                    "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x",
+                    ht_cap->ht_supported, ht_cap->cap, rate_set);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (allow_ht_operation && ht_cap->ht_supported) {
+               /* no need to translate capabilities - use the spec values */
+               ht_capabilites = ht_cap->cap;
+
+               /*
+                * this bit is not employed by the spec but only by FW to
+                * indicate peer HT support
+                */
+               ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION;
+
+               /* get data from A-MPDU parameters field */
+               acx->ampdu_max_length = ht_cap->ampdu_factor;
+               acx->ampdu_min_spacing = ht_cap->ampdu_density;
+       }
+
+       acx->hlid = hlid;
+       acx->ht_capabilites = cpu_to_le32(ht_capabilites);
+       acx->supported_rates = cpu_to_le32(rate_set);
+
+       ret = wl1271_cmd_configure(wl, ACX_PEER_CAP, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("acx ht capabilities setting failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
index e2609a6..0e636de 100644 (file)
 #include "../wlcore/acx.h"
 
 enum {
-       ACX_CLEAR_STATISTICS             = 0x0047,
+       ACX_NS_IPV6_FILTER               = 0x0050,
+       ACX_PEER_HT_OPERATION_MODE_CFG   = 0x0051,
+       ACX_CSUM_CONFIG                  = 0x0052,
+       ACX_SIM_CONFIG                   = 0x0053,
+       ACX_CLEAR_STATISTICS             = 0x0054,
+       ACX_AUTO_RX_STREAMING            = 0x0055,
+       ACX_PEER_CAP                     = 0x0056
 };
 
 /* numbers of bits the length field takes (add 1 for the actual number) */
@@ -278,10 +284,57 @@ struct wl18xx_acx_clear_statistics {
        struct acx_header header;
 };
 
+enum wlcore_bandwidth {
+       WLCORE_BANDWIDTH_20MHZ,
+       WLCORE_BANDWIDTH_40MHZ,
+};
+
+struct wlcore_peer_ht_operation_mode {
+       struct acx_header header;
+
+       u8 hlid;
+       u8 bandwidth; /* enum wlcore_bandwidth */
+       u8 padding[2];
+};
+
+/*
+ * ACX_PEER_CAP
+ * this struct is very similar to wl1271_acx_ht_capabilities, with the
+ * addition of supported rates
+ */
+struct wlcore_acx_peer_cap {
+       struct acx_header header;
+
+       /* bitmask of capability bits supported by the peer */
+       __le32 ht_capabilites;
+
+       /* rates supported by the remote peer */
+       __le32 supported_rates;
+
+       /* Indicates to which link these capabilities apply. */
+       u8 hlid;
+
+       /*
+        * This the maximum A-MPDU length supported by the AP. The FW may not
+        * exceed this length when sending A-MPDUs
+        */
+       u8 ampdu_max_length;
+
+       /* This is the minimal spacing required when sending A-MPDUs to the AP*/
+       u8 ampdu_min_spacing;
+
+       u8 padding;
+} __packed;
+
 int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
                                  u32 sdio_blk_size, u32 extra_mem_blks,
                                  u32 len_field_size);
 int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
 int wl18xx_acx_clear_statistics(struct wl1271 *wl);
+int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide);
+int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid);
 
 #endif /* __WL18XX_ACX_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c
new file mode 100644 (file)
index 0000000..1d1f6cc
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/hw_ops.h"
+
+#include "cmd.h"
+
+int wl18xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch)
+{
+       struct wl18xx_cmd_channel_switch *cmd;
+       u32 supported_rates;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "cmd channel switch");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+       cmd->channel = ch_switch->channel->hw_value;
+       cmd->switch_time = ch_switch->count;
+       cmd->stop_tx = ch_switch->block_tx;
+
+       switch (ch_switch->channel->band) {
+       case IEEE80211_BAND_2GHZ:
+               cmd->band = WLCORE_BAND_2_4GHZ;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               cmd->band = WLCORE_BAND_5GHZ;
+               break;
+       default:
+               wl1271_error("invalid channel switch band: %d",
+                            ch_switch->channel->band);
+               ret = -EINVAL;
+               goto out_free;
+       }
+
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
+                         wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
+       cmd->local_supported_rates = cpu_to_le32(supported_rates);
+       cmd->channel_type = wlvif->channel_type;
+
+       ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send channel switch command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h
new file mode 100644 (file)
index 0000000..6687d10
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_CMD_H__
+#define __WL18XX_CMD_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/acx.h"
+
+struct wl18xx_cmd_channel_switch {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+
+       /* The new serving channel */
+       u8 channel;
+       /* Relative time of the serving channel switch in TBTT units */
+       u8 switch_time;
+       /* Stop the role TX, should expect it after radar detection */
+       u8 stop_tx;
+
+       __le32 local_supported_rates;
+
+       u8 channel_type;
+       u8 band;
+
+       u8 padding[2];
+} __packed;
+
+int wl18xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
+
+#endif
index 4d426cc..b5f1148 100644 (file)
 #define __WL18XX_CONF_H__
 
 #define WL18XX_CONF_MAGIC      0x10e100ca
-#define WL18XX_CONF_VERSION    (WLCORE_CONF_VERSION | 0x0003)
+#define WL18XX_CONF_VERSION    (WLCORE_CONF_VERSION | 0x0005)
 #define WL18XX_CONF_MASK       0x0000ffff
 #define WL18XX_CONF_SIZE       (WLCORE_CONF_SIZE + \
                                 sizeof(struct wl18xx_priv_conf))
 
 #define NUM_OF_CHANNELS_11_ABG 150
 #define NUM_OF_CHANNELS_11_P 7
-#define WL18XX_NUM_OF_SUB_BANDS 9
 #define SRF_TABLE_LEN 16
 #define PIN_MUXING_SIZE 2
+#define WL18XX_TRACE_LOSS_GAPS_TX 10
+#define WL18XX_TRACE_LOSS_GAPS_RX 18
 
 struct wl18xx_mac_and_phy_params {
        u8 phy_standalone;
-       u8 rdl;
+       u8 spare0;
        u8 enable_clpc;
        u8 enable_tx_low_pwr_on_siso_rdl;
        u8 auto_detect;
@@ -69,18 +70,26 @@ struct wl18xx_mac_and_phy_params {
        u8 pwr_limit_reference_11_abg;
        u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
        u8 pwr_limit_reference_11p;
-       u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
-       u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
+       u8 spare1[9];
+       u8 spare2[9];
        u8 primary_clock_setting_time;
        u8 clock_valid_on_wake_up;
        u8 secondary_clock_setting_time;
        u8 board_type;
        /* enable point saturation */
        u8 psat;
-       /* low/medium/high Tx power in dBm */
+       /* low/medium/high Tx power in dBm for STA-HP BG */
        s8 low_power_val;
        s8 med_power_val;
        s8 high_power_val;
+       s8 per_sub_band_tx_trace_loss[WL18XX_TRACE_LOSS_GAPS_TX];
+       s8 per_sub_band_rx_trace_loss[WL18XX_TRACE_LOSS_GAPS_RX];
+       u8 tx_rf_margin;
+       /* low/medium/high Tx power in dBm for other role */
+       s8 low_power_val_2nd;
+       s8 med_power_val_2nd;
+       s8 high_power_val_2nd;
+
        u8 padding[1];
 } __packed;
 
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
new file mode 100644 (file)
index 0000000..c9199d7
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "event.h"
+#include "scan.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+
+int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout)
+{
+       u32 local_event;
+
+       switch (event) {
+       case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
+               local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
+               break;
+
+       case WLCORE_EVENT_DFS_CONFIG_COMPLETE:
+               local_event = DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+               break;
+
+       default:
+               /* event not implemented */
+               return 0;
+       }
+       return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
+}
+
+int wl18xx_process_mailbox_events(struct wl1271 *wl)
+{
+       struct wl18xx_event_mailbox *mbox = wl->mbox;
+       u32 vector;
+
+       vector = le32_to_cpu(mbox->events_vector);
+       wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
+
+       if (vector & SCAN_COMPLETE_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT, "scan results: %d",
+                            mbox->number_of_scan_results);
+
+               if (wl->scan_wlvif)
+                       wl18xx_scan_completed(wl, wl->scan_wlvif);
+       }
+
+       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT,
+                            "PERIODIC_SCAN_REPORT_EVENT (results %d)",
+                            mbox->number_of_sched_scan_results);
+
+               wlcore_scan_sched_scan_results(wl);
+       }
+
+       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
+               wlcore_event_sched_scan_completed(wl, 1);
+
+       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
+               wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
+
+       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
+               wlcore_event_ba_rx_constraint(wl,
+                               le16_to_cpu(mbox->rx_ba_role_id_bitmap),
+                               le16_to_cpu(mbox->rx_ba_allowed_bitmap));
+
+       if (vector & BSS_LOSS_EVENT_ID)
+               wlcore_event_beacon_loss(wl,
+                                        le16_to_cpu(mbox->bss_loss_bitmap));
+
+       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
+               wlcore_event_channel_switch(wl,
+                       le16_to_cpu(mbox->channel_switch_role_id_bitmap),
+                       true);
+
+       if (vector & DUMMY_PACKET_EVENT_ID)
+               wlcore_event_dummy_packet(wl);
+
+       /*
+        * "TX retries exceeded" has a different meaning according to mode.
+        * In AP mode the offending station is disconnected.
+        */
+       if (vector & MAX_TX_FAILURE_EVENT_ID)
+               wlcore_event_max_tx_failure(wl,
+                               le32_to_cpu(mbox->tx_retry_exceeded_bitmap));
+
+       if (vector & INACTIVE_STA_EVENT_ID)
+               wlcore_event_inactive_sta(wl,
+                               le32_to_cpu(mbox->inactive_sta_bitmap));
+
+       if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
+               wlcore_event_roc_complete(wl);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h
new file mode 100644 (file)
index 0000000..398f3d2
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_EVENT_H__
+#define __WL18XX_EVENT_H__
+
+#include "../wlcore/wlcore.h"
+
+enum {
+       SCAN_COMPLETE_EVENT_ID                   = BIT(8),
+       RADAR_DETECTED_EVENT_ID                  = BIT(9),
+       CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(10),
+       BSS_LOSS_EVENT_ID                        = BIT(11),
+       MAX_TX_FAILURE_EVENT_ID                  = BIT(12),
+       DUMMY_PACKET_EVENT_ID                    = BIT(13),
+       INACTIVE_STA_EVENT_ID                    = BIT(14),
+       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(15),
+       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(16),
+       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(17),
+       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(18),
+       DFS_CHANNELS_CONFIG_COMPLETE_EVENT       = BIT(19),
+       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(20),
+};
+
+struct wl18xx_event_mailbox {
+       __le32 events_vector;
+
+       u8 number_of_scan_results;
+       u8 number_of_sched_scan_results;
+
+       __le16 channel_switch_role_id_bitmap;
+
+       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
+
+       /* bitmap of removed links */
+       __le32 hlid_removed_bitmap;
+
+       /* rx ba constraint */
+       __le16 rx_ba_role_id_bitmap; /* 0xfff means any role. */
+       __le16 rx_ba_allowed_bitmap;
+
+       /* bitmap of roc completed (by role id) */
+       __le16 roc_completed_bitmap;
+
+       /* bitmap of stations (by role id) with bss loss */
+       __le16 bss_loss_bitmap;
+
+       /* bitmap of stations (by HLID) which exceeded max tx retries */
+       __le32 tx_retry_exceeded_bitmap;
+
+       /* bitmap of inactive stations (by HLID) */
+       __le32 inactive_sta_bitmap;
+} __packed;
+
+int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout);
+int wl18xx_process_mailbox_events(struct wl1271 *wl);
+
+#endif
index 8d8c1f8..0be1cfc 100644 (file)
 
 #include "reg.h"
 #include "conf.h"
+#include "cmd.h"
 #include "acx.h"
 #include "tx.h"
 #include "wl18xx.h"
 #include "io.h"
+#include "scan.h"
+#include "event.h"
 #include "debugfs.h"
 
 #define WL18XX_RX_CHECKSUM_MASK      0x40
@@ -334,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = {
                .tmpl_short_retry_limit      = 10,
                .tmpl_long_retry_limit       = 10,
                .tx_watchdog_timeout         = 5000,
+               .slow_link_thold             = 3,
+               .fast_link_thold             = 30,
        },
        .conn = {
                .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
@@ -391,8 +396,10 @@ static struct wlcore_conf wl18xx_conf = {
        .scan = {
                .min_dwell_time_active        = 7500,
                .max_dwell_time_active        = 30000,
-               .min_dwell_time_passive       = 100000,
-               .max_dwell_time_passive       = 100000,
+               .min_dwell_time_active_long   = 25000,
+               .max_dwell_time_active_long   = 50000,
+               .dwell_time_passive           = 100000,
+               .dwell_time_dfs               = 150000,
                .num_probe_reqs               = 2,
                .split_scan_timeout           = 50000,
        },
@@ -489,6 +496,10 @@ static struct wlcore_conf wl18xx_conf = {
                .increase_time              = 1,
                .window_size                = 16,
        },
+       .recovery = {
+               .bug_on_recovery            = 0,
+               .no_recovery                = 0,
+       },
 };
 
 static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
@@ -501,7 +512,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .clock_valid_on_wake_up         = 0x00,
                .secondary_clock_setting_time   = 0x05,
                .board_type                     = BOARD_TYPE_HDK_18XX,
-               .rdl                            = 0x01,
                .auto_detect                    = 0x00,
                .dedicated_fem                  = FEM_NONE,
                .low_band_component             = COMPONENT_3_WAY_SWITCH,
@@ -517,14 +527,39 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .enable_clpc                    = 0x00,
                .enable_tx_low_pwr_on_siso_rdl  = 0x00,
                .rx_profile                     = 0x00,
-               .pwr_limit_reference_11_abg     = 0xc8,
+               .pwr_limit_reference_11_abg     = 0x64,
+               .per_chan_pwr_limit_arr_11abg   = {
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+               .pwr_limit_reference_11p        = 0x64,
+               .per_chan_pwr_limit_arr_11p     = { 0xff, 0xff, 0xff, 0xff,
+                                                   0xff, 0xff, 0xff },
                .psat                           = 0,
-               .low_power_val                  = 0x00,
-               .med_power_val                  = 0x0a,
-               .high_power_val                 = 0x1e,
+               .low_power_val                  = 0x08,
+               .med_power_val                  = 0x12,
+               .high_power_val                 = 0x18,
+               .low_power_val_2nd              = 0x05,
+               .med_power_val_2nd              = 0x0a,
+               .high_power_val_2nd             = 0x14,
                .external_pa_dc2dc              = 0,
-               .number_of_assembled_ant2_4     = 1,
+               .number_of_assembled_ant2_4     = 2,
                .number_of_assembled_ant5       = 1,
+               .tx_rf_margin                   = 1,
        },
 };
 
@@ -595,7 +630,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin"
 
 static int wl18xx_identify_chip(struct wl1271 *wl)
 {
@@ -608,15 +643,18 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                wl->sr_fw_name = WL18XX_FW_NAME;
                /* wl18xx uses the same firmware for PLT */
                wl->plt_fw_name = WL18XX_FW_NAME;
-               wl->quirks |= WLCORE_QUIRK_NO_ELP |
-                             WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+               wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
-                             WLCORE_QUIRK_TX_PAD_LAST_FRAME;
-
-               wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER,
-                                     WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER,
-                                     WL18XX_MINOR_VER);
+                             WLCORE_QUIRK_TX_PAD_LAST_FRAME |
+                             WLCORE_QUIRK_REGDOMAIN_CONF |
+                             WLCORE_QUIRK_DUAL_PROBE_TMPL;
+
+               wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER,
+                                     WL18XX_IFTYPE_VER,  WL18XX_MAJOR_VER,
+                                     WL18XX_SUBTYPE_VER, WL18XX_MINOR_VER,
+                                     /* there's no separate multi-role FW */
+                                     0, 0, 0, 0);
                break;
        case CHIP_ID_185x_PG10:
                wl1271_warning("chip id 0x%x (185x PG10) is deprecated",
@@ -630,6 +668,11 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+       wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
+       wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC;
+       wl->max_channels_5 = WL18XX_MAX_CHANNELS_5GHZ;
 out:
        return ret;
 }
@@ -843,6 +886,20 @@ static int wl18xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
+       wl->event_mask = BSS_LOSS_EVENT_ID |
+               SCAN_COMPLETE_EVENT_ID |
+               RSSI_SNR_TRIGGER_0_EVENT_ID |
+               PERIODIC_SCAN_COMPLETE_EVENT_ID |
+               PERIODIC_SCAN_REPORT_EVENT_ID |
+               DUMMY_PACKET_EVENT_ID |
+               PEER_REMOVE_COMPLETE_EVENT_ID |
+               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+               INACTIVE_STA_EVENT_ID |
+               MAX_TX_FAILURE_EVENT_ID |
+               CHANNEL_SWITCH_COMPLETE_EVENT_ID |
+               DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -964,7 +1021,7 @@ static int wl18xx_hw_init(struct wl1271 *wl)
 
        /* (re)init private structures. Relevant on recovery as well. */
        priv->last_fw_rls_idx = 0;
-       priv->extra_spare_vif_count = 0;
+       priv->extra_spare_key_count = 0;
 
        /* set the default amount of spare blocks in the bitmap */
        ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE);
@@ -1022,7 +1079,12 @@ static bool wl18xx_is_mimo_supported(struct wl1271 *wl)
 {
        struct wl18xx_priv *priv = wl->priv;
 
-       return priv->conf.phy.number_of_assembled_ant2_4 >= 2;
+       /* only support MIMO with multiple antennas, and when SISO
+        * is not forced through config
+        */
+       return (priv->conf.phy.number_of_assembled_ant2_4 >= 2) &&
+              (priv->conf.ht.mode != HT_MODE_WIDE) &&
+              (priv->conf.ht.mode != HT_MODE_SISO20);
 }
 
 /*
@@ -1223,8 +1285,8 @@ static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
 {
        struct wl18xx_priv *priv = wl->priv;
 
-       /* If we have VIFs requiring extra spare, indulge them */
-       if (priv->extra_spare_vif_count)
+       /* If we have keys requiring extra spare, indulge them */
+       if (priv->extra_spare_key_count)
                return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
 
        return WL18XX_TX_HW_BLOCK_SPARE;
@@ -1236,42 +1298,48 @@ static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                          struct ieee80211_key_conf *key_conf)
 {
        struct wl18xx_priv *priv = wl->priv;
-       bool change_spare = false;
+       bool change_spare = false, special_enc;
        int ret;
 
+       wl1271_debug(DEBUG_CRYPT, "extra spare keys before: %d",
+                    priv->extra_spare_key_count);
+
+       special_enc = key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
+                     key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
+
+       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       if (ret < 0)
+               goto out;
+
        /*
-        * when adding the first or removing the last GEM/TKIP interface,
+        * when adding the first or removing the last GEM/TKIP key,
         * we have to adjust the number of spare blocks.
         */
-       change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
-               key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) &&
-               ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) ||
-                (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY));
+       if (special_enc) {
+               if (cmd == SET_KEY) {
+                       /* first key */
+                       change_spare = (priv->extra_spare_key_count == 0);
+                       priv->extra_spare_key_count++;
+               } else if (cmd == DISABLE_KEY) {
+                       /* last key */
+                       change_spare = (priv->extra_spare_key_count == 1);
+                       priv->extra_spare_key_count--;
+               }
+       }
 
-       /* no need to change spare - just regular set_key */
-       if (!change_spare)
-               return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       wl1271_debug(DEBUG_CRYPT, "extra spare keys after: %d",
+                    priv->extra_spare_key_count);
 
-       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
-       if (ret < 0)
+       if (!change_spare)
                goto out;
 
        /* key is now set, change the spare blocks */
-       if (cmd == SET_KEY) {
+       if (priv->extra_spare_key_count)
                ret = wl18xx_set_host_cfg_bitmap(wl,
                                        WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
-               if (ret < 0)
-                       goto out;
-
-               priv->extra_spare_vif_count++;
-       } else {
+       else
                ret = wl18xx_set_host_cfg_bitmap(wl,
                                        WL18XX_TX_HW_BLOCK_SPARE);
-               if (ret < 0)
-                       goto out;
-
-               priv->extra_spare_vif_count--;
-       }
 
 out:
        return ret;
@@ -1296,6 +1364,92 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
        return buf_offset;
 }
 
+static void wl18xx_sta_rc_update(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
+                                struct ieee80211_sta *sta,
+                                u32 changed)
+{
+       bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide);
+
+       if (!(changed & IEEE80211_RC_BW_CHANGED))
+               return;
+
+       mutex_lock(&wl->mutex);
+
+       /* sanity */
+       if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
+               goto out;
+
+       /* ignore the change before association */
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               goto out;
+
+       /*
+        * If we started out as wide, we can change the operation mode. If we
+        * thought this was a 20mhz AP, we have to reconnect
+        */
+       if (wlvif->sta.role_chan_type == NL80211_CHAN_HT40MINUS ||
+           wlvif->sta.role_chan_type == NL80211_CHAN_HT40PLUS)
+               wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide);
+       else
+               ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif));
+
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static int wl18xx_set_peer_cap(struct wl1271 *wl,
+                              struct ieee80211_sta_ht_cap *ht_cap,
+                              bool allow_ht_operation,
+                              u32 rate_set, u8 hlid)
+{
+       return wl18xx_acx_set_peer_cap(wl, ht_cap, allow_ht_operation,
+                                      rate_set, hlid);
+}
+
+static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                                struct wl1271_link *lnk)
+{
+       u8 thold;
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+
+       /* suspended links are never high priority */
+       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+               return false;
+
+       /* the priority thresholds are taken from FW */
+       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
+           !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+               thold = status_priv->tx_fast_link_prio_threshold;
+       else
+               thold = status_priv->tx_slow_link_prio_threshold;
+
+       return lnk->allocated_pkts < thold;
+}
+
+static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                               struct wl1271_link *lnk)
+{
+       u8 thold;
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+
+       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+               thold = status_priv->tx_suspend_threshold;
+       else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
+                !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+               thold = status_priv->tx_fast_stop_threshold;
+       else
+               thold = status_priv->tx_slow_stop_threshold;
+
+       return lnk->allocated_pkts < thold;
+}
+
 static int wl18xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl18xx_ops = {
@@ -1305,6 +1459,8 @@ static struct wlcore_ops wl18xx_ops = {
        .plt_init       = wl18xx_plt_init,
        .trigger_cmd    = wl18xx_trigger_cmd,
        .ack_event      = wl18xx_ack_event,
+       .wait_for_event = wl18xx_wait_for_event,
+       .process_mailbox_events = wl18xx_process_mailbox_events,
        .calc_tx_blocks = wl18xx_calc_tx_blocks,
        .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks,
        .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len,
@@ -1320,16 +1476,26 @@ static struct wlcore_ops wl18xx_ops = {
        .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask,
        .get_mac        = wl18xx_get_mac,
        .debugfs_init   = wl18xx_debugfs_add_files,
+       .scan_start     = wl18xx_scan_start,
+       .scan_stop      = wl18xx_scan_stop,
+       .sched_scan_start       = wl18xx_sched_scan_start,
+       .sched_scan_stop        = wl18xx_scan_sched_scan_stop,
        .handle_static_data     = wl18xx_handle_static_data,
        .get_spare_blocks = wl18xx_get_spare_blocks,
        .set_key        = wl18xx_set_key,
+       .channel_switch = wl18xx_cmd_channel_switch,
        .pre_pkt_send   = wl18xx_pre_pkt_send,
+       .sta_rc_update  = wl18xx_sta_rc_update,
+       .set_peer_cap   = wl18xx_set_peer_cap,
+       .lnk_high_prio  = wl18xx_lnk_high_prio,
+       .lnk_low_prio   = wl18xx_lnk_low_prio,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
 static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
        .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1343,7 +1509,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
 /* HT cap appropriate for wide channels in 5Ghz */
 static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
        .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-              IEEE80211_HT_CAP_SUP_WIDTH_20_40,
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1356,7 +1523,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
 
 /* HT cap appropriate for SISO 20 */
 static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
-       .cap = IEEE80211_HT_CAP_SGI_20,
+       .cap = IEEE80211_HT_CAP_SGI_20 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1369,7 +1537,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
 
 /* HT cap appropriate for MIMO rates in 20mhz channel */
 static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
-       .cap = IEEE80211_HT_CAP_SGI_20,
+       .cap = IEEE80211_HT_CAP_SGI_20 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1387,7 +1556,8 @@ static int wl18xx_setup(struct wl1271 *wl)
 
        wl->rtable = wl18xx_rtable;
        wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
-       wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS;
+       wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS;
+       wl->num_channels = 2;
        wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
@@ -1506,7 +1676,8 @@ static int wl18xx_probe(struct platform_device *pdev)
        int ret;
 
        hw = wlcore_alloc_hw(sizeof(struct wl18xx_priv),
-                            WL18XX_AGGR_BUFFER_SIZE);
+                            WL18XX_AGGR_BUFFER_SIZE,
+                            sizeof(struct wl18xx_event_mailbox));
        if (IS_ERR(hw)) {
                wl1271_error("can't allocate hw");
                ret = PTR_ERR(hw);
diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c
new file mode 100644 (file)
index 0000000..09d9445
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/ieee80211.h>
+#include "scan.h"
+#include "../wlcore/debug.h"
+
+static void wl18xx_adjust_channels(struct wl18xx_cmd_scan_params *cmd,
+                                  struct wlcore_scan_channels *cmd_channels)
+{
+       memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
+       memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
+       cmd->dfs = cmd_channels->dfs;
+       cmd->passive_active = cmd_channels->passive_active;
+
+       memcpy(cmd->channels_2, cmd_channels->channels_2,
+              sizeof(cmd->channels_2));
+       memcpy(cmd->channels_5, cmd_channels->channels_5,
+              sizeof(cmd->channels_2));
+       /* channels_4 are not supported, so no need to copy them */
+}
+
+static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_scan_request *req)
+{
+       struct wl18xx_cmd_scan_params *cmd;
+       struct wlcore_scan_channels *cmd_channels = NULL;
+       int ret;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->scan_type = SCAN_TYPE_SEARCH;
+       cmd->rssi_threshold = -127;
+       cmd->snr_threshold = 0;
+
+       cmd->bss_type = SCAN_BSS_TYPE_ANY;
+
+       cmd->ssid_from_list = 0;
+       cmd->filter = 0;
+       cmd->add_broadcast = 0;
+
+       cmd->urgency = 0;
+       cmd->protect = 0;
+
+       cmd->n_probe_reqs = wl->conf.scan.num_probe_reqs;
+       cmd->terminate_after = 0;
+
+       /* configure channels */
+       WARN_ON(req->n_ssids > 1);
+
+       cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+       if (!cmd_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
+                                   req->n_channels, req->n_ssids,
+                                   SCAN_TYPE_SEARCH);
+       wl18xx_adjust_channels(cmd, cmd_channels);
+
+       /*
+        * all the cycles params (except total cycles) should
+        * remain 0 for normal scan
+        */
+       cmd->total_cycles = 1;
+
+       if (req->no_cck)
+               cmd->rate = WL18XX_SCAN_RATE_6;
+
+       cmd->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       if (req->n_ssids) {
+               cmd->ssid_len = req->ssids[0].ssid_len;
+               memcpy(cmd->ssid, req->ssids[0].ssid, cmd->ssid_len);
+       }
+
+       /* TODO: per-band ies? */
+       if (cmd->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                req->ie,
+                                req->ie_len,
+                                false);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (cmd->active[1] || cmd->dfs) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                req->ie,
+                                req->ie_len,
+                                false);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd_channels);
+       kfree(cmd);
+       return ret;
+}
+
+void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       wl->scan.failed = false;
+       cancel_delayed_work(&wl->scan_complete_work);
+       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                    msecs_to_jiffies(0));
+}
+
+static
+int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif,
+                                 struct cfg80211_sched_scan_request *req,
+                                 struct ieee80211_sched_scan_ies *ies)
+{
+       struct wl18xx_cmd_scan_params *cmd;
+       struct wlcore_scan_channels *cmd_channels = NULL;
+       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+       int ret;
+       int filter_type;
+
+       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+       filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
+       if (filter_type < 0)
+               return filter_type;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->scan_type = SCAN_TYPE_PERIODIC;
+       cmd->rssi_threshold = c->rssi_threshold;
+       cmd->snr_threshold = c->snr_threshold;
+
+       /* don't filter on BSS type */
+       cmd->bss_type = SCAN_BSS_TYPE_ANY;
+
+       cmd->ssid_from_list = 1;
+       if (filter_type == SCAN_SSID_FILTER_LIST)
+               cmd->filter = 1;
+       cmd->add_broadcast = 0;
+
+       cmd->urgency = 0;
+       cmd->protect = 0;
+
+       cmd->n_probe_reqs = c->num_probe_reqs;
+       /* don't stop scanning automatically when something is found */
+       cmd->terminate_after = 0;
+
+       cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+       if (!cmd_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* configure channels */
+       wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
+                                   req->n_channels, req->n_ssids,
+                                   SCAN_TYPE_PERIODIC);
+       wl18xx_adjust_channels(cmd, cmd_channels);
+
+       cmd->short_cycles_sec = 0;
+       cmd->long_cycles_sec = cpu_to_le16(req->interval);
+       cmd->short_cycles_count = 0;
+
+       cmd->total_cycles = 0;
+
+       cmd->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */
+       cmd->report_threshold = 1;
+       cmd->terminate_on_report = 0;
+
+       if (cmd->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                ies->ie[band],
+                                ies->len[band],
+                                true);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (cmd->active[1] || cmd->dfs) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                ies->ie[band],
+                                ies->len[band],
+                                true);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd_channels);
+       kfree(cmd);
+       return ret;
+}
+
+int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies)
+{
+       return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
+}
+
+static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                              u8 scan_type)
+{
+       struct wl18xx_cmd_scan_stop *stop;
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+       if (!stop) {
+               wl1271_error("failed to alloc memory to send sched scan stop");
+               return -ENOMEM;
+       }
+
+       stop->role_id = wlvif->role_id;
+       stop->scan_type = scan_type;
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send sched scan stop command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(stop);
+       return ret;
+}
+
+void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC);
+}
+int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req)
+{
+       return wl18xx_scan_send(wl, wlvif, req);
+}
+
+int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH);
+}
diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h
new file mode 100644 (file)
index 0000000..eadee42
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_SCAN_H__
+#define __WL18XX_SCAN_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/scan.h"
+
+struct tracking_ch_params {
+       struct conn_scan_ch_params channel;
+
+       __le32 bssid_lsb;
+       __le16 bssid_msb;
+
+       u8 padding[2];
+} __packed;
+
+/* probe request rate */
+enum
+{
+       WL18XX_SCAN_RATE_1      = 0,
+       WL18XX_SCAN_RATE_5_5    = 1,
+       WL18XX_SCAN_RATE_6      = 2,
+};
+
+#define WL18XX_MAX_CHANNELS_5GHZ 32
+
+struct wl18xx_cmd_scan_params {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+       u8 scan_type;
+
+       s8 rssi_threshold; /* for filtering (in dBm) */
+       s8 snr_threshold;  /* for filtering (in dB) */
+
+       u8 bss_type;       /* for filtering */
+       u8 ssid_from_list; /* use ssid from configured ssid list */
+       u8 filter;         /* forward only results with matching ssids */
+
+       /*
+        * add broadcast ssid in addition to the configured ssids.
+        * the driver should add dummy entry for it (?).
+        */
+       u8 add_broadcast;
+
+       u8 urgency;
+       u8 protect;      /* ??? */
+       u8 n_probe_reqs;    /* Number of probes requests per channel */
+       u8 terminate_after; /* early terminate scan operation */
+
+       u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
+       u8 active[SCAN_MAX_BANDS];  /* number of active scan channels */
+       u8 dfs;            /* number of dfs channels in 5ghz */
+       u8 passive_active; /* number of passive before active channels 2.4ghz */
+
+       __le16 short_cycles_sec;
+       __le16 long_cycles_sec;
+       u8 short_cycles_count;
+       u8 total_cycles; /* 0 - infinite */
+       u8 padding[2];
+
+       union {
+               struct {
+                       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+                       struct conn_scan_ch_params channels_5[WL18XX_MAX_CHANNELS_5GHZ];
+                       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+               };
+               struct tracking_ch_params channels_tracking[WL1271_SCAN_MAX_CHANNELS];
+       } ;
+
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
+       u8 tag;
+       u8 rate;
+
+       /* send SCAN_REPORT_EVENT in periodic scans after each cycle
+       * if number of results >= report_threshold. Must be 0 for
+       * non periodic scans
+       */
+       u8 report_threshold;
+
+       /* Should periodic scan stop after a report event was created.
+       * Must be 0 for non periodic scans.
+       */
+       u8 terminate_on_report;
+
+       u8 padding1[3];
+} __packed;
+
+struct wl18xx_cmd_scan_stop {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+       u8 scan_type;
+       u8 padding[2];
+} __packed;
+
+int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req);
+int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies);
+void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+#endif
index 5b1fb10..57c6943 100644 (file)
 #include "wl18xx.h"
 #include "tx.h"
 
+static
+void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
+                            struct ieee80211_tx_rate *rate)
+{
+       u8 fw_rate = wl->fw_status_2->counters.tx_last_rate;
+
+       if (fw_rate > CONF_HW_RATE_INDEX_MAX) {
+               wl1271_error("last Tx rate invalid: %d", fw_rate);
+               rate->idx = 0;
+               rate->flags = 0;
+               return;
+       }
+
+       if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) {
+               rate->idx = fw_rate;
+               rate->flags = 0;
+       } else {
+               rate->flags = IEEE80211_TX_RC_MCS;
+               rate->idx = fw_rate - CONF_HW_RATE_INDEX_MCS0;
+
+               /* SGI modifier is counted as a separate rate */
+               if (fw_rate >= CONF_HW_RATE_INDEX_MCS7_SGI)
+                       (rate->idx)--;
+               if (fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
+                       (rate->idx)--;
+
+               /* this also covers the 40Mhz SGI case (= MCS15) */
+               if (fw_rate == CONF_HW_RATE_INDEX_MCS7_SGI ||
+                   fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
+                       rate->flags |= IEEE80211_TX_RC_SHORT_GI;
+
+               if (fw_rate > CONF_HW_RATE_INDEX_MCS7_SGI && vif) {
+                       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+                       if (wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
+                           wlvif->channel_type == NL80211_CHAN_HT40PLUS) {
+                               /* adjustment needed for range 0-7 */
+                               rate->idx -= 8;
+                               rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+                       }
+               }
+       }
+}
+
 static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
 {
        struct ieee80211_tx_info *info;
@@ -44,7 +87,6 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
        /* a zero bit indicates Tx success */
        tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));
 
-
        skb = wl->tx_frames[id];
        info = IEEE80211_SKB_CB(skb);
 
@@ -56,11 +98,13 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
        /* update the TX status info */
        if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
                info->flags |= IEEE80211_TX_STAT_ACK;
+       /*
+        * first pass info->control.vif while it's valid, and then fill out
+        * the info->status structures
+        */
+       wl18xx_get_last_tx_rate(wl, info->control.vif, &info->status.rates[0]);
 
-       /* no real data about Tx completion */
-       info->status.rates[0].idx = -1;
-       info->status.rates[0].count = 0;
-       info->status.rates[0].flags = 0;
+       info->status.rates[0].count = 1; /* no data about retries */
        info->status.ack_signal = -1;
 
        if (!tx_success)
index 96a1e43..b6739e7 100644 (file)
 
 /* minimum FW required for driver */
 #define WL18XX_CHIP_VER                8
-#define WL18XX_IFTYPE_VER      2
-#define WL18XX_MAJOR_VER       0
-#define WL18XX_SUBTYPE_VER     0
-#define WL18XX_MINOR_VER       100
+#define WL18XX_IFTYPE_VER      5
+#define WL18XX_MAJOR_VER       WLCORE_FW_VER_IGNORE
+#define WL18XX_SUBTYPE_VER     WLCORE_FW_VER_IGNORE
+#define WL18XX_MINOR_VER       28
 
 #define WL18XX_CMD_MAX_SIZE          740
 
@@ -49,8 +49,8 @@ struct wl18xx_priv {
        /* Index of last released Tx desc in FW */
        u8 last_fw_rls_idx;
 
-       /* number of VIFs requiring extra spare mem-blocks */
-       int extra_spare_vif_count;
+       /* number of keys requiring extra spare mem-blocks */
+       int extra_spare_key_count;
 };
 
 #define WL18XX_FW_MAX_TX_STATUS_DESC 33
@@ -68,7 +68,43 @@ struct wl18xx_fw_status_priv {
         */
        u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC];
 
-       u8 padding[2];
+       /* A bitmap representing the currently suspended links. The suspend
+        * is short lived, for multi-channel Tx requirements.
+        */
+       __le32 link_suspend_bitmap;
+
+       /* packet threshold for an "almost empty" AC,
+        * for Tx schedulng purposes
+        */
+       u8 tx_ac_threshold;
+
+       /* number of packets to queue up for a link in PS */
+       u8 tx_ps_threshold;
+
+       /* number of packet to queue up for a suspended link */
+       u8 tx_suspend_threshold;
+
+       /* Should have less than this number of packets in queue of a slow
+        * link to qualify as high priority link
+        */
+       u8 tx_slow_link_prio_threshold;
+
+       /* Should have less than this number of packets in queue of a fast
+        * link to qualify as high priority link
+        */
+       u8 tx_fast_link_prio_threshold;
+
+       /* Should have less than this number of packets in queue of a slow
+        * link before we stop queuing up packets for it.
+        */
+       u8 tx_slow_stop_threshold;
+
+       /* Should have less than this number of packets in queue of a fast
+        * link before we stop queuing up packets for it.
+        */
+       u8 tx_fast_stop_threshold;
+
+       u8 padding[3];
 };
 
 #define WL18XX_PHY_VERSION_MAX_LEN 20
index ce108a7..c796543 100644 (file)
@@ -1340,6 +1340,8 @@ out:
        kfree(acx);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_set_ht_capabilities);
+
 
 int wl1271_acx_set_ht_information(struct wl1271 *wl,
                                   struct wl12xx_vif *wlvif,
@@ -1433,13 +1435,22 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
        acx->win_size = wl->conf.ht.rx_ba_win_size;
        acx->ssn = ssn;
 
-       ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx,
-                                  sizeof(*acx));
+       ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx,
+                                           sizeof(*acx),
+                                           BIT(CMD_STATUS_NO_RX_BA_SESSION));
        if (ret < 0) {
                wl1271_warning("acx ba receiver session failed: %d", ret);
                goto out;
        }
 
+       /* sometimes we can't start the session */
+       if (ret == CMD_STATUS_NO_RX_BA_SESSION) {
+               wl1271_warning("no fw rx ba on tid %d", tid_index);
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = 0;
 out:
        kfree(acx);
        return ret;
index d03215d..126536c 100644 (file)
@@ -1025,7 +1025,6 @@ enum {
        ACX_CONFIG_HANGOVER              = 0x0042,
        ACX_FEATURE_CFG                  = 0x0043,
        ACX_PROTECTION_CFG               = 0x0044,
-       ACX_CHECKSUM_CONFIG              = 0x0045,
 };
 
 
index 375ea57..b58ae5f 100644 (file)
@@ -84,47 +84,57 @@ out:
 static int wlcore_validate_fw_ver(struct wl1271 *wl)
 {
        unsigned int *fw_ver = wl->chip.fw_ver;
-       unsigned int *min_ver = wl->min_fw_ver;
+       unsigned int *min_ver = (wl->fw_type == WL12XX_FW_TYPE_NORMAL) ?
+               wl->min_sr_fw_ver : wl->min_mr_fw_ver;
+       char min_fw_str[32] = "";
+       int i;
 
        /* the chip must be exactly equal */
-       if (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP])
+       if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]))
                goto fail;
 
-       /* always check the next digit if all previous ones are equal */
-
-       if (min_ver[FW_VER_IF_TYPE] < fw_ver[FW_VER_IF_TYPE])
-               goto out;
-       else if (min_ver[FW_VER_IF_TYPE] > fw_ver[FW_VER_IF_TYPE])
+       /* the firmware type must be equal */
+       if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE]))
                goto fail;
 
-       if (min_ver[FW_VER_MAJOR] < fw_ver[FW_VER_MAJOR])
-               goto out;
-       else if (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR])
+       /* the project number must be equal */
+       if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE]))
                goto fail;
 
-       if (min_ver[FW_VER_SUBTYPE] < fw_ver[FW_VER_SUBTYPE])
-               goto out;
-       else if (min_ver[FW_VER_SUBTYPE] > fw_ver[FW_VER_SUBTYPE])
+       /* the API version must be greater or equal */
+       if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) &&
+                (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]))
                goto fail;
 
-       if (min_ver[FW_VER_MINOR] < fw_ver[FW_VER_MINOR])
-               goto out;
-       else if (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])
+       /* if the API version is equal... */
+       if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) ||
+            (min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) &&
+           /* ...the minor must be greater or equal */
+           ((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) &&
+            (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])))
                goto fail;
 
-out:
        return 0;
 
 fail:
-       wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is outdated.\n"
-                    "Please use at least FW %u.%u.%u.%u.%u.\n"
-                    "You can get more information at:\n"
-                    "http://wireless.kernel.org/en/users/Drivers/wl12xx",
+       for (i = 0; i < NUM_FW_VER; i++)
+               if (min_ver[i] == WLCORE_FW_VER_IGNORE)
+                       snprintf(min_fw_str, sizeof(min_fw_str),
+                                 "%s*.", min_fw_str);
+               else
+                       snprintf(min_fw_str, sizeof(min_fw_str),
+                                 "%s%u.", min_fw_str, min_ver[i]);
+
+       wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n"
+                    "Please use at least FW %s\n"
+                    "You can get the latest firmwares at:\n"
+                    "git://github.com/TI-OpenLink/firmwares.git",
                     fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE],
                     fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE],
-                    fw_ver[FW_VER_MINOR], min_ver[FW_VER_CHIP],
-                    min_ver[FW_VER_IF_TYPE], min_ver[FW_VER_MAJOR],
-                    min_ver[FW_VER_SUBTYPE], min_ver[FW_VER_MINOR]);
+                    fw_ver[FW_VER_MINOR], min_fw_str);
        return -EINVAL;
 }
 
@@ -491,7 +501,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
-       wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
+       wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size;
 
        wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
                     wl->mbox_ptr[0], wl->mbox_ptr[1]);
@@ -508,23 +518,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
         */
 
        /* unmask required mbox events  */
-       wl->event_mask = BSS_LOSE_EVENT_ID |
-               REGAINED_BSS_EVENT_ID |
-               SCAN_COMPLETE_EVENT_ID |
-               ROLE_STOP_COMPLETE_EVENT_ID |
-               RSSI_SNR_TRIGGER_0_EVENT_ID |
-               PSPOLL_DELIVERY_FAILURE_EVENT_ID |
-               SOFT_GEMINI_SENSE_EVENT_ID |
-               PERIODIC_SCAN_REPORT_EVENT_ID |
-               PERIODIC_SCAN_COMPLETE_EVENT_ID |
-               DUMMY_PACKET_EVENT_ID |
-               PEER_REMOVE_COMPLETE_EVENT_ID |
-               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
-               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
-               INACTIVE_STA_EVENT_ID |
-               MAX_TX_RETRY_EVENT_ID |
-               CHANNEL_SWITCH_COMPLETE_EVENT_ID;
-
        ret = wl1271_event_unmask(wl);
        if (ret < 0) {
                wl1271_error("EVENT mask setting failed");
index 27f83f7..1201aca 100644 (file)
  * @id: command id
  * @buf: buffer containing the command, must work with dma
  * @len: length of the buffer
+ * return the cmd status code on success.
  */
-int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
-                   size_t res_len)
+static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
+                            size_t len, size_t res_len)
 {
        struct wl1271_cmd_header *cmd;
        unsigned long timeout;
        u32 intr;
-       int ret = 0;
+       int ret;
        u16 status;
        u16 poll_count = 0;
 
@@ -71,7 +72,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
        ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        /*
         * TODO: we just need this because one bit is in a different
@@ -79,19 +80,18 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
         */
        ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT);
 
        ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) {
                if (time_after(jiffies, timeout)) {
                        wl1271_error("command complete timeout");
-                       ret = -ETIMEDOUT;
-                       goto fail;
+                       return -ETIMEDOUT;
                }
 
                poll_count++;
@@ -102,7 +102,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
                ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
                if (ret < 0)
-                       goto fail;
+                       return ret;
        }
 
        /* read back the status code of the command */
@@ -111,33 +111,66 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
        ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        status = le16_to_cpu(cmd->status);
-       if (status != CMD_STATUS_SUCCESS) {
-               wl1271_error("command execute failure %d", status);
-               ret = -EIO;
-               goto fail;
-       }
 
        ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK,
                               WL1271_ACX_INTR_CMD_COMPLETE);
+       if (ret < 0)
+               return ret;
+
+       return status;
+}
+
+/*
+ * send command to fw and return cmd status on success
+ * valid_rets contains a bitmap of allowed error codes
+ */
+int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                            size_t res_len, unsigned long valid_rets)
+{
+       int ret = __wlcore_cmd_send(wl, id, buf, len, res_len);
+
        if (ret < 0)
                goto fail;
 
-       return 0;
+       /* success is always a valid status */
+       valid_rets |= BIT(CMD_STATUS_SUCCESS);
 
+       if (ret >= MAX_COMMAND_STATUS ||
+           !test_bit(ret, &valid_rets)) {
+               wl1271_error("command execute failure %d", ret);
+               ret = -EIO;
+               goto fail;
+       }
+       return ret;
 fail:
        wl12xx_queue_recovery_work(wl);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_cmd_send);
+
+/*
+ * wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS
+ * return 0 on success.
+ */
+int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                   size_t res_len)
+{
+       int ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0);
+
+       if (ret < 0)
+               return ret;
+       return 0;
+}
 
 /*
  * Poll the mailbox event field until any of the bits in the mask is set or a
  * timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
  */
-static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
-                                               u32 mask, bool *timeout)
+int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
+                                        u32 mask, bool *timeout)
 {
        u32 *events_vector;
        u32 event;
@@ -187,20 +220,7 @@ out:
        kfree(events_vector);
        return ret;
 }
-
-static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
-{
-       int ret;
-       bool timeout = false;
-
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask, &timeout);
-       if (ret != 0 || timeout) {
-               wl12xx_queue_recovery_work(wl);
-               return ret;
-       }
-
-       return 0;
-}
+EXPORT_SYMBOL_GPL(wlcore_cmd_wait_for_event_or_timeout);
 
 int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
                           u8 *role_id)
@@ -278,6 +298,16 @@ out:
        return ret;
 }
 
+static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid)
+{
+       if (wl->session_ids[hlid] >= SESSION_COUNTER_MAX)
+               wl->session_ids[hlid] = 0;
+
+       wl->session_ids[hlid]++;
+
+       return wl->session_ids[hlid];
+}
+
 int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 {
        unsigned long flags;
@@ -285,12 +315,21 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        if (link >= WL12XX_MAX_LINKS)
                return -EBUSY;
 
+       wl->session_ids[link] = wlcore_get_new_session_id(wl, link);
+
        /* these bits are used by op_tx */
        spin_lock_irqsave(&wl->wl_lock, flags);
        __set_bit(link, wl->links_map);
        __set_bit(link, wlvif->links_map);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       /* take the last "freed packets" value from the current FW status */
+       wl->links[link].prev_freed_pkts =
+                       wl->fw_status_2->counters.tx_lnk_free_pkts[link];
+       wl->links[link].wlvif = wlvif;
        *hlid = link;
+
+       wl->active_link_count++;
        return 0;
 }
 
@@ -307,24 +346,21 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        __clear_bit(*hlid, wlvif->links_map);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
+       wl->links[*hlid].allocated_pkts = 0;
+       wl->links[*hlid].prev_freed_pkts = 0;
+       wl->links[*hlid].ba_bitmap = 0;
+       memset(wl->links[*hlid].addr, 0, ETH_ALEN);
+
        /*
         * At this point op_tx() will not add more packets to the queues. We
         * can purge them.
         */
        wl1271_tx_reset_link_queues(wl, *hlid);
+       wl->links[*hlid].wlvif = NULL;
 
        *hlid = WL12XX_INVALID_LINK_ID;
-}
-
-static int wl12xx_get_new_session_id(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif)
-{
-       if (wlvif->session_counter >= SESSION_COUNTER_MAX)
-               wlvif->session_counter = 0;
-
-       wlvif->session_counter++;
-
-       return wlvif->session_counter;
+       wl->active_link_count--;
+       WARN_ON_ONCE(wl->active_link_count < 0);
 }
 
 static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
@@ -345,7 +381,9 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
 }
 
 static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif)
+                                    struct wl12xx_vif *wlvif,
+                                    enum ieee80211_band band,
+                                    int channel)
 {
        struct wl12xx_cmd_role_start *cmd;
        int ret;
@@ -359,9 +397,9 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
        wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);
 
        cmd->role_id = wlvif->dev_role_id;
-       if (wlvif->band == IEEE80211_BAND_5GHZ)
+       if (band == IEEE80211_BAND_5GHZ)
                cmd->band = WLCORE_BAND_5GHZ;
-       cmd->channel = wlvif->channel;
+       cmd->channel = channel;
 
        if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) {
                ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid);
@@ -369,7 +407,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
                        goto out_free;
        }
        cmd->device.hlid = wlvif->dev_hlid;
-       cmd->device.session = wl12xx_get_new_session_id(wl, wlvif);
+       cmd->device.session = wl->session_ids[wlvif->dev_hlid];
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",
                     cmd->role_id, cmd->device.hlid, cmd->device.session);
@@ -420,12 +458,6 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl,
                goto out_free;
        }
 
-       ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID);
-       if (ret < 0) {
-               wl1271_error("cmd role stop dev event completion error");
-               goto out_free;
-       }
-
        wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid);
 
 out_free:
@@ -439,6 +471,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct wl12xx_cmd_role_start *cmd;
+       u32 supported_rates;
        int ret;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -459,7 +492,14 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        cmd->sta.ssid_len = wlvif->ssid_len;
        memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
        memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
-       cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
+
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
+                         wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
+
+       cmd->sta.local_rates = cpu_to_le32(supported_rates);
+
        cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
        if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
@@ -468,7 +508,11 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                        goto out_free;
        }
        cmd->sta.hlid = wlvif->sta.hlid;
-       cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif);
+       cmd->sta.session = wl->session_ids[wlvif->sta.hlid];
+       /*
+        * We don't have the correct remote rates in this stage. the rates
+        * will be reconfigured later, after authorization.
+        */
        cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set);
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
@@ -482,6 +526,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                goto err_hlid;
        }
 
+       wlvif->sta.role_chan_type = wlvif->channel_type;
        goto out_free;
 
 err_hlid:
@@ -500,7 +545,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_role_stop *cmd;
        int ret;
-       bool timeout = false;
 
        if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID))
                return -EINVAL;
@@ -523,17 +567,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                goto out_free;
        }
 
-       /*
-        * Sometimes the firmware doesn't send this event, so we just
-        * time out without failing.  Queue recovery for other
-        * failures.
-        */
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl,
-                                                  ROLE_STOP_COMPLETE_EVENT_ID,
-                                                  &timeout);
-       if (ret)
-               wl12xx_queue_recovery_work(wl);
-
        wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
 
 out_free:
@@ -579,12 +612,15 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        cmd->ap.bss_index = WL1271_AP_BSS_INDEX;
        cmd->ap.global_hlid = wlvif->ap.global_hlid;
        cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid;
+       cmd->ap.global_session_id = wl->session_ids[wlvif->ap.global_hlid];
+       cmd->ap.bcast_session_id = wl->session_ids[wlvif->ap.bcast_hlid];
        cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
        cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);
        cmd->ap.dtim_interval = bss_conf->dtim_period;
        cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
        /* FIXME: Change when adding DFS */
        cmd->ap.reset_tsf = 1;  /* By default reset AP TSF */
+       cmd->ap.wmm = wlvif->wmm_enabled;
        cmd->channel = wlvif->channel;
        cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
@@ -599,8 +635,10 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len);
        }
 
-       supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES |
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
                wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
 
        wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
                     supported_rates);
@@ -799,8 +837,11 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
  * @id: acx id
  * @buf: buffer containing acx, including all headers, must work with dma
  * @len: length of buf
+ * @valid_rets: bitmap of valid cmd status codes (i.e. return values).
+ * return the cmd status on success.
  */
-int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
+int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
+                                 size_t len, unsigned long valid_rets)
 {
        struct acx_header *acx = buf;
        int ret;
@@ -812,12 +853,26 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
        /* payload length, does not include any headers */
        acx->len = cpu_to_le16(len - sizeof(*acx));
 
-       ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0);
+       ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0,
+                                      valid_rets);
        if (ret < 0) {
                wl1271_warning("CONFIGURE command NOK");
                return ret;
        }
 
+       return ret;
+}
+
+/*
+ * wrapper for wlcore_cmd_configure that accepts only success status.
+ * return 0 on success
+ */
+int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
+{
+       int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0);
+
+       if (ret < 0)
+               return ret;
        return 0;
 }
 EXPORT_SYMBOL_GPL(wl1271_cmd_configure);
@@ -1034,8 +1089,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        struct sk_buff *skb;
        int ret;
        u32 rate;
-       u16 template_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
-       u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       u16 template_id_2_4 = wl->scan_templ_id_2_4;
+       u16 template_id_5 = wl->scan_templ_id_5;
 
        skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
                                     ie_len);
@@ -1048,10 +1103,10 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
 
-       if (!sched_scan &&
+       if (sched_scan &&
            (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
-               template_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4;
-               template_id_5 = CMD_TEMPL_APP_PROBE_REQ_5;
+               template_id_2_4 = wl->sched_scan_templ_id_2_4;
+               template_id_5 = wl->sched_scan_templ_id_5;
        }
 
        rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
@@ -1068,6 +1123,7 @@ out:
        dev_kfree_skb(skb);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl12xx_cmd_build_probe_req);
 
 struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
                                              struct wl12xx_vif *wlvif,
@@ -1379,7 +1435,8 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid)
+int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 hlid)
 {
        struct wl12xx_cmd_set_peer_state *cmd;
        int ret = 0;
@@ -1395,6 +1452,10 @@ int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid)
        cmd->hlid = hlid;
        cmd->state = WL1271_CMD_STA_STATE_CONNECTED;
 
+       /* wmm param is valid only for station role */
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS)
+               cmd->wmm = wlvif->wmm_enabled;
+
        ret = wl1271_cmd_send(wl, CMD_SET_PEER_STATE, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("failed to send set peer state command");
@@ -1429,6 +1490,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        cmd->hlid = hlid;
        cmd->sp_len = sta->max_sp;
        cmd->wmm = sta->wme ? 1 : 0;
+       cmd->session_id = wl->session_ids[hlid];
 
        for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
                if (sta->wme && (sta->uapsd_queues & BIT(i)))
@@ -1490,9 +1552,10 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
                goto out_free;
        }
 
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl,
-                                          PEER_REMOVE_COMPLETE_EVENT_ID,
-                                          &timeout);
+       ret = wl->ops->wait_for_event(wl,
+                                     WLCORE_EVENT_PEER_REMOVE_COMPLETE,
+                                     &timeout);
+
        /*
         * We are ok with a timeout here. The event is sometimes not sent
         * due to a firmware bug. In case of another error (like SDIO timeout)
@@ -1508,6 +1571,131 @@ out:
        return ret;
 }
 
+static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch)
+{
+       int idx = -1;
+
+       switch (band) {
+       case IEEE80211_BAND_5GHZ:
+               if (ch >= 8 && ch <= 16)
+                       idx = ((ch-8)/4 + 18);
+               else if (ch >= 34 && ch <= 64)
+                       idx = ((ch-34)/2 + 3 + 18);
+               else if (ch >= 100 && ch <= 140)
+                       idx = ((ch-100)/4 + 15 + 18);
+               else if (ch >= 149 && ch <= 165)
+                       idx = ((ch-149)/4 + 26 + 18);
+               else
+                       idx = -1;
+               break;
+       case IEEE80211_BAND_2GHZ:
+               if (ch >= 1 && ch <= 14)
+                       idx = ch - 1;
+               else
+                       idx = -1;
+               break;
+       default:
+               wl1271_error("get reg conf ch idx - unknown band: %d",
+                            (int)band);
+       }
+
+       return idx;
+}
+
+void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
+                                    enum ieee80211_band band)
+{
+       int ch_bit_idx = 0;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return;
+
+       ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel);
+
+       if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS)
+               set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending);
+}
+
+int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl)
+{
+       struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL;
+       int ret = 0, i, b, ch_bit_idx;
+       struct ieee80211_channel *channel;
+       u32 tmp_ch_bitmap[2];
+       u16 ch;
+       struct wiphy *wiphy = wl->hw->wiphy;
+       struct ieee80211_supported_band *band;
+       bool timeout = false;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd reg domain config");
+
+       memset(tmp_ch_bitmap, 0, sizeof(tmp_ch_bitmap));
+
+       for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) {
+               band = wiphy->bands[b];
+               for (i = 0; i < band->n_channels; i++) {
+                       channel = &band->channels[i];
+                       ch = channel->hw_value;
+
+                       if (channel->flags & (IEEE80211_CHAN_DISABLED |
+                                             IEEE80211_CHAN_RADAR |
+                                             IEEE80211_CHAN_PASSIVE_SCAN))
+                               continue;
+
+                       ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch);
+                       if (ch_bit_idx < 0)
+                               continue;
+
+                       set_bit(ch_bit_idx, (long *)tmp_ch_bitmap);
+               }
+       }
+
+       tmp_ch_bitmap[0] |= wl->reg_ch_conf_pending[0];
+       tmp_ch_bitmap[1] |= wl->reg_ch_conf_pending[1];
+
+       if (!memcmp(tmp_ch_bitmap, wl->reg_ch_conf_last, sizeof(tmp_ch_bitmap)))
+               goto out;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]);
+       cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]);
+
+       wl1271_debug(DEBUG_CMD,
+                    "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x",
+                    cmd->ch_bit_map1, cmd->ch_bit_map2);
+
+       ret = wl1271_cmd_send(wl, CMD_DFS_CHANNEL_CONFIG, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send reg domain dfs config");
+               goto out;
+       }
+
+       ret = wl->ops->wait_for_event(wl,
+                                     WLCORE_EVENT_DFS_CONFIG_COMPLETE,
+                                     &timeout);
+       if (ret < 0 || timeout) {
+               wl1271_error("reg domain conf %serror",
+                            timeout ? "completion " : "");
+               ret = timeout ? -ETIMEDOUT : ret;
+               goto out;
+       }
+
+       memcpy(wl->reg_ch_conf_last, tmp_ch_bitmap, sizeof(tmp_ch_bitmap));
+       memset(wl->reg_ch_conf_pending, 0, sizeof(wl->reg_ch_conf_pending));
+
+out:
+       kfree(cmd);
+       return ret;
+}
+
 int wl12xx_cmd_config_fwlog(struct wl1271 *wl)
 {
        struct wl12xx_cmd_config_fwlog *cmd;
@@ -1593,12 +1781,12 @@ out:
 }
 
 static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                         u8 role_id)
+                         u8 role_id, enum ieee80211_band band, u8 channel)
 {
        struct wl12xx_cmd_roc *cmd;
        int ret = 0;
 
-       wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id);
+       wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id);
 
        if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID))
                return -EINVAL;
@@ -1610,8 +1798,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        }
 
        cmd->role_id = role_id;
-       cmd->channel = wlvif->channel;
-       switch (wlvif->band) {
+       cmd->channel = channel;
+       switch (band) {
        case IEEE80211_BAND_2GHZ:
                cmd->band = WLCORE_BAND_2_4GHZ;
                break;
@@ -1666,30 +1854,18 @@ out:
        return ret;
 }
 
-int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
+              enum ieee80211_band band, u8 channel)
 {
        int ret = 0;
-       bool is_first_roc;
 
        if (WARN_ON(test_bit(role_id, wl->roc_map)))
                return 0;
 
-       is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >=
-                       WL12XX_MAX_ROLES);
-
-       ret = wl12xx_cmd_roc(wl, wlvif, role_id);
+       ret = wl12xx_cmd_roc(wl, wlvif, role_id, band, channel);
        if (ret < 0)
                goto out;
 
-       if (is_first_roc) {
-               ret = wl1271_cmd_wait_for_event(wl,
-                                          REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
-               if (ret < 0) {
-                       wl1271_error("cmd roc event completion error");
-                       goto out;
-               }
-       }
-
        __set_bit(role_id, wl->roc_map);
 out:
        return ret;
@@ -1719,43 +1895,7 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_channel_switch(struct wl1271 *wl,
-                             struct wl12xx_vif *wlvif,
-                             struct ieee80211_channel_switch *ch_switch)
-{
-       struct wl12xx_cmd_channel_switch *cmd;
-       int ret;
-
-       wl1271_debug(DEBUG_ACX, "cmd channel switch");
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       if (!cmd) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       cmd->role_id = wlvif->role_id;
-       cmd->channel = ch_switch->channel->hw_value;
-       cmd->switch_time = ch_switch->count;
-       cmd->stop_tx = ch_switch->block_tx;
-
-       /* FIXME: control from mac80211 in the future */
-       cmd->post_switch_tx_disable = 0;  /* Enable TX on the target channel */
-
-       ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
-       if (ret < 0) {
-               wl1271_error("failed to send channel switch command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(cmd);
-
-out:
-       return ret;
-}
-
-int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
+int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_stop_channel_switch *cmd;
        int ret;
@@ -1768,6 +1908,8 @@ int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
                goto out;
        }
 
+       cmd->role_id = wlvif->role_id;
+
        ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("failed to stop channel switch command");
@@ -1782,7 +1924,8 @@ out:
 }
 
 /* start dev role and roc on its channel */
-int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                    enum ieee80211_band band, int channel)
 {
        int ret;
 
@@ -1797,11 +1940,11 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if (ret < 0)
                goto out;
 
-       ret = wl12xx_cmd_role_start_dev(wl, wlvif);
+       ret = wl12xx_cmd_role_start_dev(wl, wlvif, band, channel);
        if (ret < 0)
                goto out_disable;
 
-       ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
+       ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel);
        if (ret < 0)
                goto out_stop;
 
index 2409f3d..fd34123 100644 (file)
@@ -31,6 +31,8 @@ struct acx_header;
 
 int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
                    size_t res_len);
+int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                            size_t res_len, unsigned long valid_rets);
 int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
                           u8 *role_id);
 int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id);
@@ -39,11 +41,14 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                    enum ieee80211_band band, int channel);
 int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
 int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
 int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
+int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
+                                 size_t len, unsigned long valid_rets);
 int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
 int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                       u8 ps_mode, u16 auto_ps_timeout);
@@ -75,22 +80,30 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                          u16 action, u8 id, u8 key_type,
                          u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
                          u16 tx_seq_16);
-int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid);
-int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id);
+int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 hlid);
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
+              enum ieee80211_band band, u8 channel);
 int wl12xx_croc(struct wl1271 *wl, u8 role_id);
 int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                        struct ieee80211_sta *sta, u8 hlid);
 int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
+void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
+                                    enum ieee80211_band band);
+int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl);
 int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
 int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
 int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
 int wl12xx_cmd_channel_switch(struct wl1271 *wl,
                              struct wl12xx_vif *wlvif,
                              struct ieee80211_channel_switch *ch_switch);
-int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl);
+int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif);
 int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                         u8 *hlid);
 void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
+int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
+                                        u32 mask, bool *timeout);
 
 enum wl1271_commands {
        CMD_INTERROGATE = 1, /* use this to read information elements */
@@ -149,8 +162,11 @@ enum wl1271_commands {
        CMD_WFD_START_DISCOVERY = 45,
        CMD_WFD_STOP_DISCOVERY  = 46,
        CMD_WFD_ATTRIBUTE_CONFIG        = 47,
-       CMD_NOP                 = 48,
-       CMD_LAST_COMMAND,
+       CMD_GENERIC_CFG                 = 48,
+       CMD_NOP                         = 49,
+
+       /* start of 18xx specific commands */
+       CMD_DFS_CHANNEL_CONFIG          = 60,
 
        MAX_COMMAND_ID = 0xFFFF,
 };
@@ -167,8 +183,8 @@ enum cmd_templ {
        CMD_TEMPL_PS_POLL,
        CMD_TEMPL_KLV,
        CMD_TEMPL_DISCONNECT,
-       CMD_TEMPL_APP_PROBE_REQ_2_4,
-       CMD_TEMPL_APP_PROBE_REQ_5,
+       CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY,
+       CMD_TEMPL_APP_PROBE_REQ_5_LEGACY,
        CMD_TEMPL_BAR,           /* for firmware internal use only */
        CMD_TEMPL_CTS,           /*
                                  * For CTS-to-self (FastCTS) mechanism
@@ -179,6 +195,8 @@ enum cmd_templ {
        CMD_TEMPL_DEAUTH_AP,
        CMD_TEMPL_TEMPORARY,
        CMD_TEMPL_LINK_MEASUREMENT_REPORT,
+       CMD_TEMPL_PROBE_REQ_2_4_PERIODIC,
+       CMD_TEMPL_PROBE_REQ_5_PERIODIC,
 
        CMD_TEMPL_MAX = 0xff
 };
@@ -220,7 +238,8 @@ enum {
        CMD_STATUS_FW_RESET             = 22, /* Driver internal use.*/
        CMD_STATUS_TEMPLATE_OOM         = 23,
        CMD_STATUS_NO_RX_BA_SESSION     = 24,
-       MAX_COMMAND_STATUS              = 0xff
+
+       MAX_COMMAND_STATUS
 };
 
 #define CMDMBOX_HEADER_LEN 4
@@ -345,7 +364,15 @@ struct wl12xx_cmd_role_start {
 
                        u8 reset_tsf;
 
-                       u8 padding_1[4];
+                       /*
+                        * ap supports wmm (note that there is additional
+                        * per-sta wmm configuration)
+                        */
+                       u8 wmm;
+
+                       u8 bcast_session_id;
+                       u8 global_session_id;
+                       u8 padding_1[1];
                } __packed ap;
        };
 } __packed;
@@ -515,7 +542,14 @@ struct wl12xx_cmd_set_peer_state {
 
        u8 hlid;
        u8 state;
-       u8 padding[2];
+
+       /*
+        * wmm is relevant for sta role only.
+        * ap role configures the per-sta wmm params in
+        * the add_peer command.
+        */
+       u8 wmm;
+       u8 padding[1];
 } __packed;
 
 struct wl12xx_cmd_roc {
@@ -558,7 +592,7 @@ struct wl12xx_cmd_add_peer {
        u8 bss_index;
        u8 sp_len;
        u8 wmm;
-       u8 padding1;
+       u8 session_id;
 } __packed;
 
 struct wl12xx_cmd_remove_peer {
@@ -597,6 +631,13 @@ enum wl12xx_fwlogger_output {
        WL12XX_FWLOG_OUTPUT_HOST,
 };
 
+struct wl12xx_cmd_regdomain_dfs_config {
+       struct wl1271_cmd_header header;
+
+       __le32 ch_bit_map1;
+       __le32 ch_bit_map2;
+} __packed;
+
 struct wl12xx_cmd_config_fwlog {
        struct wl1271_cmd_header header;
 
@@ -626,27 +667,13 @@ struct wl12xx_cmd_stop_fwlog {
        struct wl1271_cmd_header header;
 } __packed;
 
-struct wl12xx_cmd_channel_switch {
+struct wl12xx_cmd_stop_channel_switch {
        struct wl1271_cmd_header header;
 
        u8 role_id;
-
-       /* The new serving channel */
-       u8 channel;
-       /* Relative time of the serving channel switch in TBTT units */
-       u8 switch_time;
-       /* Stop the role TX, should expect it after radar detection */
-       u8 stop_tx;
-       /* The target channel tx status 1-stopped 0-open*/
-       u8 post_switch_tx_disable;
-
        u8 padding[3];
 } __packed;
 
-struct wl12xx_cmd_stop_channel_switch {
-       struct wl1271_cmd_header header;
-} __packed;
-
 /* Used to check radio status after calibration */
 #define MAX_TLV_LENGTH         500
 #define TEST_CMD_P2G_CAL       2       /* TX BiP */
index 9e40760..2b96ff8 100644 (file)
@@ -57,20 +57,49 @@ enum {
 };
 
 enum {
-       CONF_HW_RATE_INDEX_1MBPS   = 0,
-       CONF_HW_RATE_INDEX_2MBPS   = 1,
-       CONF_HW_RATE_INDEX_5_5MBPS = 2,
-       CONF_HW_RATE_INDEX_6MBPS   = 3,
-       CONF_HW_RATE_INDEX_9MBPS   = 4,
-       CONF_HW_RATE_INDEX_11MBPS  = 5,
-       CONF_HW_RATE_INDEX_12MBPS  = 6,
-       CONF_HW_RATE_INDEX_18MBPS  = 7,
-       CONF_HW_RATE_INDEX_22MBPS  = 8,
-       CONF_HW_RATE_INDEX_24MBPS  = 9,
-       CONF_HW_RATE_INDEX_36MBPS  = 10,
-       CONF_HW_RATE_INDEX_48MBPS  = 11,
-       CONF_HW_RATE_INDEX_54MBPS  = 12,
-       CONF_HW_RATE_INDEX_MAX     = CONF_HW_RATE_INDEX_54MBPS,
+       CONF_HW_RATE_INDEX_1MBPS      = 0,
+       CONF_HW_RATE_INDEX_2MBPS      = 1,
+       CONF_HW_RATE_INDEX_5_5MBPS    = 2,
+       CONF_HW_RATE_INDEX_11MBPS     = 3,
+       CONF_HW_RATE_INDEX_6MBPS      = 4,
+       CONF_HW_RATE_INDEX_9MBPS      = 5,
+       CONF_HW_RATE_INDEX_12MBPS     = 6,
+       CONF_HW_RATE_INDEX_18MBPS     = 7,
+       CONF_HW_RATE_INDEX_24MBPS     = 8,
+       CONF_HW_RATE_INDEX_36MBPS     = 9,
+       CONF_HW_RATE_INDEX_48MBPS     = 10,
+       CONF_HW_RATE_INDEX_54MBPS     = 11,
+       CONF_HW_RATE_INDEX_MCS0       = 12,
+       CONF_HW_RATE_INDEX_MCS1       = 13,
+       CONF_HW_RATE_INDEX_MCS2       = 14,
+       CONF_HW_RATE_INDEX_MCS3       = 15,
+       CONF_HW_RATE_INDEX_MCS4       = 16,
+       CONF_HW_RATE_INDEX_MCS5       = 17,
+       CONF_HW_RATE_INDEX_MCS6       = 18,
+       CONF_HW_RATE_INDEX_MCS7       = 19,
+       CONF_HW_RATE_INDEX_MCS7_SGI   = 20,
+       CONF_HW_RATE_INDEX_MCS0_40MHZ = 21,
+       CONF_HW_RATE_INDEX_MCS1_40MHZ = 22,
+       CONF_HW_RATE_INDEX_MCS2_40MHZ = 23,
+       CONF_HW_RATE_INDEX_MCS3_40MHZ = 24,
+       CONF_HW_RATE_INDEX_MCS4_40MHZ = 25,
+       CONF_HW_RATE_INDEX_MCS5_40MHZ = 26,
+       CONF_HW_RATE_INDEX_MCS6_40MHZ = 27,
+       CONF_HW_RATE_INDEX_MCS7_40MHZ = 28,
+       CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI = 29,
+
+       /* MCS8+ rates overlap with 40Mhz rates */
+       CONF_HW_RATE_INDEX_MCS8       = 21,
+       CONF_HW_RATE_INDEX_MCS9       = 22,
+       CONF_HW_RATE_INDEX_MCS10      = 23,
+       CONF_HW_RATE_INDEX_MCS11      = 24,
+       CONF_HW_RATE_INDEX_MCS12      = 25,
+       CONF_HW_RATE_INDEX_MCS13      = 26,
+       CONF_HW_RATE_INDEX_MCS14      = 27,
+       CONF_HW_RATE_INDEX_MCS15      = 28,
+       CONF_HW_RATE_INDEX_MCS15_SGI  = 29,
+
+       CONF_HW_RATE_INDEX_MAX        = CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI,
 };
 
 #define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff
@@ -415,11 +444,11 @@ struct conf_rx_settings {
 #define CONF_TX_RATE_MASK_BASIC_P2P    CONF_HW_BIT_RATE_6MBPS
 
 /*
- * Rates supported for data packets when operating as AP. Note the absence
+ * Rates supported for data packets when operating as STA/AP. Note the absence
  * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop
  * one. The rate dropped is not mandatory under any operating mode.
  */
-#define CONF_TX_AP_ENABLED_RATES       (CONF_HW_BIT_RATE_1MBPS | \
+#define CONF_TX_ENABLED_RATES       (CONF_HW_BIT_RATE_1MBPS |    \
        CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS |      \
        CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS |        \
        CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS |      \
@@ -677,6 +706,18 @@ struct conf_tx_settings {
 
        /* Time in ms for Tx watchdog timer to expire */
        u32 tx_watchdog_timeout;
+
+       /*
+        * when a slow link has this much packets pending, it becomes a low
+        * priority link, scheduling-wise
+        */
+       u8 slow_link_thold;
+
+       /*
+        * when a fast link has this much packets pending, it becomes a low
+        * priority link, scheduling-wise
+        */
+       u8 fast_link_thold;
 } __packed;
 
 enum {
@@ -1047,6 +1088,7 @@ struct conf_roam_trigger_settings {
 struct conf_scan_settings {
        /*
         * The minimum time to wait on each channel for active scans
+        * This value will be used whenever there's a connected interface.
         *
         * Range: u32 tu/1000
         */
@@ -1054,24 +1096,37 @@ struct conf_scan_settings {
 
        /*
         * The maximum time to wait on each channel for active scans
+        * This value will be currently used whenever there's a
+        * connected interface. It shouldn't exceed 30000 (~30ms) to avoid
+        * possible interference of voip traffic going on while scanning.
         *
         * Range: u32 tu/1000
         */
        u32 max_dwell_time_active;
 
-       /*
-        * The minimum time to wait on each channel for passive scans
+       /* The minimum time to wait on each channel for active scans
+        * when it's possible to have longer scan dwell times.
+        * Currently this is used whenever we're idle on all interfaces.
+        * Longer dwell times improve detection of networks within a
+        * single scan.
         *
         * Range: u32 tu/1000
         */
-       u32 min_dwell_time_passive;
+       u32 min_dwell_time_active_long;
 
-       /*
-        * The maximum time to wait on each channel for passive scans
+       /* The maximum time to wait on each channel for active scans
+        * when it's possible to have longer scan dwell times.
+        * See min_dwell_time_active_long
         *
         * Range: u32 tu/1000
         */
-       u32 max_dwell_time_passive;
+       u32 max_dwell_time_active_long;
+
+       /* time to wait on the channel for passive scans (in TU/1000) */
+       u32 dwell_time_passive;
+
+       /* time to wait on the channel for DFS scans (in TU/1000) */
+       u32 dwell_time_dfs;
 
        /*
         * Number of probe requests to transmit on each active scan channel
@@ -1276,12 +1331,20 @@ struct conf_hangover_settings {
        u8 window_size;
 } __packed;
 
+struct conf_recovery_settings {
+       /* BUG() on fw recovery */
+       u8 bug_on_recovery;
+
+       /* Prevent HW recovery. FW will remain stuck. */
+       u8 no_recovery;
+} __packed;
+
 /*
  * The conf version consists of 4 bytes.  The two MSB are the wlcore
  * version, the two LSB are the lower driver's private conf
  * version.
  */
-#define WLCORE_CONF_VERSION    (0x0002 << 16)
+#define WLCORE_CONF_VERSION    (0x0005 << 16)
 #define WLCORE_CONF_MASK       0xffff0000
 #define WLCORE_CONF_SIZE       (sizeof(struct wlcore_conf_header) +    \
                                 sizeof(struct wlcore_conf))
@@ -1309,6 +1372,7 @@ struct wlcore_conf {
        struct conf_fwlog fwlog;
        struct conf_rate_policy_settings rate;
        struct conf_hangover_settings hangover;
+       struct conf_recovery_settings recovery;
 } __packed;
 
 struct wlcore_conf_file {
index c86bb00..e70a7c8 100644 (file)
@@ -490,7 +490,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_HEX(chip.id);
        DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
        DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
-       DRIVER_STATE_PRINT_INT(sched_scanning);
+       DRIVER_STATE_PRINT_INT(recovery_count);
 
 #undef DRIVER_STATE_PRINT_INT
 #undef DRIVER_STATE_PRINT_LONG
@@ -560,7 +560,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
                    wlvif->bss_type == BSS_TYPE_IBSS) {
                        VIF_STATE_PRINT_INT(sta.hlid);
-                       VIF_STATE_PRINT_INT(sta.ba_rx_bitmap);
                        VIF_STATE_PRINT_INT(sta.basic_rate_idx);
                        VIF_STATE_PRINT_INT(sta.ap_rate_idx);
                        VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
@@ -577,6 +576,10 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                        VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
                }
                VIF_STATE_PRINT_INT(last_tx_hlid);
+               VIF_STATE_PRINT_INT(tx_queue_count[0]);
+               VIF_STATE_PRINT_INT(tx_queue_count[1]);
+               VIF_STATE_PRINT_INT(tx_queue_count[2]);
+               VIF_STATE_PRINT_INT(tx_queue_count[3]);
                VIF_STATE_PRINT_LHEX(links_map[0]);
                VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
                VIF_STATE_PRINT_INT(band);
@@ -589,7 +592,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                VIF_STATE_PRINT_INT(beacon_int);
                VIF_STATE_PRINT_INT(default_key);
                VIF_STATE_PRINT_INT(aid);
-               VIF_STATE_PRINT_INT(session_counter);
                VIF_STATE_PRINT_INT(psm_entry_retry);
                VIF_STATE_PRINT_INT(power_level);
                VIF_STATE_PRINT_INT(rssi_thold);
@@ -993,7 +995,7 @@ static ssize_t sleep_auth_write(struct file *file,
                return -EINVAL;
        }
 
-       if (value < 0 || value > WL1271_PSM_MAX) {
+       if (value > WL1271_PSM_MAX) {
                wl1271_warning("sleep_auth must be between 0 and %d",
                               WL1271_PSM_MAX);
                return -ERANGE;
index 4890705..70f289a 100644 (file)
 #include "scan.h"
 #include "wl12xx_80211.h"
 
-static void wl1271_event_rssi_trigger(struct wl1271 *wl,
-                                     struct wl12xx_vif *wlvif,
-                                     struct event_mailbox *mbox)
+void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
 {
-       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
        enum nl80211_cqm_rssi_threshold_event event;
-       s8 metric = mbox->rssi_snr_trigger_metric[0];
+       s8 metric = metric_arr[0];
 
        wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
 
-       if (metric <= wlvif->rssi_thold)
-               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
-       else
-               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
-
-       if (event != wlvif->last_rssi_event)
-               ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
-       wlvif->last_rssi_event = event;
+       /* TODO: check actual multi-role support */
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (metric <= wlvif->rssi_thold)
+                       event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+               else
+                       event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               if (event != wlvif->last_rssi_event)
+                       ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
+               wlvif->last_rssi_event = event;
+       }
 }
+EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger);
 
 static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
        if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
-               if (!wlvif->sta.ba_rx_bitmap)
+               u8 hlid = wlvif->sta.hlid;
+               if (!wl->links[hlid].ba_bitmap)
                        return;
-               ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap,
+               ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap,
                                             vif->bss_conf.bssid);
        } else {
                u8 hlid;
@@ -74,8 +79,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        }
 }
 
-static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
-                                              u8 enable)
+void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable)
 {
        struct wl12xx_vif *wlvif;
 
@@ -87,201 +91,169 @@ static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
                        wl1271_recalc_rx_streaming(wl, wlvif);
                }
        }
-
 }
+EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense);
 
-static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
+void wlcore_event_sched_scan_completed(struct wl1271 *wl,
+                                      u8 status)
 {
-       wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
-       wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
-       wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
+       wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
+                    status);
+
+       if (wl->sched_vif) {
+               ieee80211_sched_scan_stopped(wl->hw);
+               wl->sched_vif = NULL;
+       }
 }
+EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed);
 
-static int wl1271_event_process(struct wl1271 *wl)
+void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
+                                  unsigned long roles_bitmap,
+                                  unsigned long allowed_bitmap)
 {
-       struct event_mailbox *mbox = wl->mbox;
-       struct ieee80211_vif *vif;
        struct wl12xx_vif *wlvif;
-       u32 vector;
-       bool disconnect_sta = false;
-       unsigned long sta_bitmap = 0;
-       int ret;
-
-       wl1271_event_mbox_dump(mbox);
-
-       vector = le32_to_cpu(mbox->events_vector);
-       vector &= ~(le32_to_cpu(mbox->events_mask));
-       wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
 
-       if (vector & SCAN_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "status: 0x%x",
-                            mbox->scheduled_scan_status);
-
-               wl1271_scan_stm(wl, wl->scan_vif);
-       }
+       wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
+                    __func__, roles_bitmap, allowed_bitmap);
 
-       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT "
-                            "(status 0x%0x)", mbox->scheduled_scan_status);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-               wl1271_scan_sched_scan_results(wl);
+               wlvif->ba_allowed = !!test_bit(wlvif->role_id,
+                                              &allowed_bitmap);
+               if (!wlvif->ba_allowed)
+                       wl1271_stop_ba_event(wl, wlvif);
        }
+}
+EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint);
 
-       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT "
-                            "(status 0x%0x)", mbox->scheduled_scan_status);
-               if (wl->sched_scanning) {
-                       ieee80211_sched_scan_stopped(wl->hw);
-                       wl->sched_scanning = false;
-               }
-       }
+void wlcore_event_channel_switch(struct wl1271 *wl,
+                                unsigned long roles_bitmap,
+                                bool success)
+{
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
 
-       if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
-               wl12xx_event_soft_gemini_sense(wl,
-                                              mbox->soft_gemini_sense_info);
+       wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
+                    __func__, roles_bitmap, success);
 
-       /*
-        * We are HW_MONITOR device. On beacon loss - queue
-        * connection loss work. Cancel it on REGAINED event.
-        */
-       if (vector & BSS_LOSE_EVENT_ID) {
-               /* TODO: check for multi-role */
-               int delay = wl->conf.conn.synch_fail_thold *
-                                       wl->conf.conn.bss_lose_timeout;
-               wl1271_info("Beacon loss detected.");
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-               /*
-                * if the work is already queued, it should take place. We
-                * don't want to delay the connection loss indication
-                * any more.
-                */
-               ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work,
-                                            msecs_to_jiffies(delay));
+               if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
+                                       &wlvif->flags))
+                       continue;
 
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       vif = wl12xx_wlvif_to_vif(wlvif);
+               vif = wl12xx_wlvif_to_vif(wlvif);
 
-                       ieee80211_cqm_rssi_notify(
-                                       vif,
-                                       NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
-                                       GFP_KERNEL);
-               }
+               ieee80211_chswitch_done(vif, success);
+               cancel_delayed_work(&wlvif->channel_switch_work);
        }
+}
+EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
 
-       if (vector & REGAINED_BSS_EVENT_ID) {
-               /* TODO: check for multi-role */
-               wl1271_info("Beacon regained.");
-               cancel_delayed_work(&wl->connection_loss_work);
-
-               /* sanity check - we can't lose and gain the beacon together */
-               WARN(vector & BSS_LOSE_EVENT_ID,
-                    "Concurrent beacon loss and gain from FW");
-       }
+void wlcore_event_dummy_packet(struct wl1271 *wl)
+{
+       wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
+       wl1271_tx_dummy_packet(wl);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet);
 
-       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
-               /* TODO: check actual multi-role support */
-               wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       wl1271_event_rssi_trigger(wl, wlvif, mbox);
+static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       u32 num_packets = wl->conf.tx.max_tx_retries;
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
+       struct ieee80211_sta *sta;
+       const u8 *addr;
+       int h;
+
+       for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
+               bool found = false;
+               /* find the ap vif connected to this sta */
+               wl12xx_for_each_wlvif_ap(wl, wlvif) {
+                       if (!test_bit(h, wlvif->ap.sta_hlid_map))
+                               continue;
+                       found = true;
+                       break;
                }
-       }
+               if (!found)
+                       continue;
 
-       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) {
-               u8 role_id = mbox->role_id;
-               wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. "
-                            "ba_allowed = 0x%x, role_id=%d",
-                            mbox->rx_ba_allowed, role_id);
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               addr = wl->links[h].addr;
 
-               wl12xx_for_each_wlvif(wl, wlvif) {
-                       if (role_id != 0xff && role_id != wlvif->role_id)
-                               continue;
-
-                       wlvif->ba_allowed = !!mbox->rx_ba_allowed;
-                       if (!wlvif->ba_allowed)
-                               wl1271_stop_ba_event(wl, wlvif);
+               rcu_read_lock();
+               sta = ieee80211_find_sta(vif, addr);
+               if (sta) {
+                       wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
+                       ieee80211_report_low_ack(sta, num_packets);
                }
+               rcu_read_unlock();
        }
+}
 
-       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
-                                         "status = 0x%x",
-                                         mbox->channel_switch_status);
-               /*
-                * That event uses for two cases:
-                * 1) channel switch complete with status=0
-                * 2) channel switch failed status=1
-                */
-
-               /* TODO: configure only the relevant vif */
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       bool success;
-
-                       if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
-                                               &wlvif->flags))
-                               continue;
-
-                       success = mbox->channel_switch_status ? false : true;
-                       vif = wl12xx_wlvif_to_vif(wlvif);
+void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID");
+       wlcore_disconnect_sta(wl, sta_bitmap);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure);
 
-                       ieee80211_chswitch_done(vif, success);
-               }
-       }
+void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
+       wlcore_disconnect_sta(wl, sta_bitmap);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta);
 
-       if ((vector & DUMMY_PACKET_EVENT_ID)) {
-               wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
-               ret = wl1271_tx_dummy_packet(wl);
-               if (ret < 0)
-                       return ret;
-       }
+void wlcore_event_roc_complete(struct wl1271 *wl)
+{
+       wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID");
+       if (wl->roc_vif)
+               ieee80211_ready_on_channel(wl->hw);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_roc_complete);
 
+void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
+{
        /*
-        * "TX retries exceeded" has a different meaning according to mode.
-        * In AP mode the offending station is disconnected.
+        * We are HW_MONITOR device. On beacon loss - queue
+        * connection loss work. Cancel it on REGAINED event.
         */
-       if (vector & MAX_TX_RETRY_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID");
-               sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded);
-               disconnect_sta = true;
-       }
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
+       int delay = wl->conf.conn.synch_fail_thold *
+                               wl->conf.conn.bss_lose_timeout;
 
-       if (vector & INACTIVE_STA_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
-               sta_bitmap |= le16_to_cpu(mbox->sta_aging_status);
-               disconnect_sta = true;
-       }
+       wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
 
-       if (disconnect_sta) {
-               u32 num_packets = wl->conf.tx.max_tx_retries;
-               struct ieee80211_sta *sta;
-               const u8 *addr;
-               int h;
-
-               for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
-                       bool found = false;
-                       /* find the ap vif connected to this sta */
-                       wl12xx_for_each_wlvif_ap(wl, wlvif) {
-                               if (!test_bit(h, wlvif->ap.sta_hlid_map))
-                                       continue;
-                               found = true;
-                               break;
-                       }
-                       if (!found)
-                               continue;
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-                       vif = wl12xx_wlvif_to_vif(wlvif);
-                       addr = wl->links[h].addr;
+               /*
+                * if the work is already queued, it should take place.
+                * We don't want to delay the connection loss
+                * indication any more.
+                */
+               ieee80211_queue_delayed_work(wl->hw,
+                                            &wlvif->connection_loss_work,
+                                            msecs_to_jiffies(delay));
 
-                       rcu_read_lock();
-                       sta = ieee80211_find_sta(vif, addr);
-                       if (sta) {
-                               wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
-                               ieee80211_report_low_ack(sta, num_packets);
-                       }
-                       rcu_read_unlock();
-               }
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               ieee80211_cqm_rssi_notify(
+                               vif,
+                               NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+                               GFP_KERNEL);
        }
-       return 0;
 }
+EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
 
 int wl1271_event_unmask(struct wl1271 *wl)
 {
@@ -305,12 +277,12 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
 
        /* first we read the mbox descriptor */
        ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox,
-                         sizeof(*wl->mbox), false);
+                         wl->mbox_size, false);
        if (ret < 0)
                return ret;
 
        /* process the descriptor */
-       ret = wl1271_event_process(wl);
+       ret = wl->ops->process_mailbox_events(wl);
        if (ret < 0)
                return ret;
 
index 8adf18d..acc7a59 100644 (file)
@@ -46,33 +46,17 @@ enum {
        RSSI_SNR_TRIGGER_5_EVENT_ID              = BIT(5),
        RSSI_SNR_TRIGGER_6_EVENT_ID              = BIT(6),
        RSSI_SNR_TRIGGER_7_EVENT_ID              = BIT(7),
-       MEASUREMENT_START_EVENT_ID               = BIT(8),
-       MEASUREMENT_COMPLETE_EVENT_ID            = BIT(9),
-       SCAN_COMPLETE_EVENT_ID                   = BIT(10),
-       WFD_DISCOVERY_COMPLETE_EVENT_ID          = BIT(11),
-       AP_DISCOVERY_COMPLETE_EVENT_ID           = BIT(12),
-       RESERVED1                                = BIT(13),
-       PSPOLL_DELIVERY_FAILURE_EVENT_ID         = BIT(14),
-       ROLE_STOP_COMPLETE_EVENT_ID              = BIT(15),
-       RADAR_DETECTED_EVENT_ID                  = BIT(16),
-       CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(17),
-       BSS_LOSE_EVENT_ID                        = BIT(18),
-       REGAINED_BSS_EVENT_ID                    = BIT(19),
-       MAX_TX_RETRY_EVENT_ID                    = BIT(20),
-       DUMMY_PACKET_EVENT_ID                    = BIT(21),
-       SOFT_GEMINI_SENSE_EVENT_ID               = BIT(22),
-       CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID        = BIT(23),
-       SOFT_GEMINI_AVALANCHE_EVENT_ID           = BIT(24),
-       PLT_RX_CALIBRATION_COMPLETE_EVENT_ID     = BIT(25),
-       INACTIVE_STA_EVENT_ID                    = BIT(26),
-       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(27),
-       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(28),
-       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(29),
-       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(30),
-       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(31),
+
        EVENT_MBOX_ALL_EVENT_ID                  = 0x7fffffff,
 };
 
+/* events the driver might want to wait for */
+enum wlcore_wait_event {
+       WLCORE_EVENT_ROLE_STOP_COMPLETE,
+       WLCORE_EVENT_PEER_REMOVE_COMPLETE,
+       WLCORE_EVENT_DFS_CONFIG_COMPLETE
+};
+
 enum {
        EVENT_ENTER_POWER_SAVE_FAIL = 0,
        EVENT_ENTER_POWER_SAVE_SUCCESS,
@@ -80,61 +64,24 @@ enum {
 
 #define NUM_OF_RSSI_SNR_TRIGGERS 8
 
-struct event_mailbox {
-       __le32 events_vector;
-       __le32 events_mask;
-       __le32 reserved_1;
-       __le32 reserved_2;
-
-       u8 number_of_scan_results;
-       u8 scan_tag;
-       u8 completed_scan_status;
-       u8 reserved_3;
-
-       u8 soft_gemini_sense_info;
-       u8 soft_gemini_protective_info;
-       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
-       u8 change_auto_mode_timeout;
-       u8 scheduled_scan_status;
-       u8 reserved4;
-       /* tuned channel (roc) */
-       u8 roc_channel;
-
-       __le16 hlid_removed_bitmap;
-
-       /* bitmap of aged stations (by HLID) */
-       __le16 sta_aging_status;
-
-       /* bitmap of stations (by HLID) which exceeded max tx retries */
-       __le16 sta_tx_retry_exceeded;
-
-       /* discovery completed results */
-       u8 discovery_tag;
-       u8 number_of_preq_results;
-       u8 number_of_prsp_results;
-       u8 reserved_5;
-
-       /* rx ba constraint */
-       u8 role_id; /* 0xFF means any role. */
-       u8 rx_ba_allowed;
-       u8 reserved_6[2];
-
-       /* Channel switch results */
-
-       u8 channel_switch_role_id;
-       u8 channel_switch_status;
-       u8 reserved_7[2];
-
-       u8 ps_poll_delivery_failure_role_ids;
-       u8 stopped_role_ids;
-       u8 started_role_ids;
-
-       u8 reserved_8[9];
-} __packed;
-
 struct wl1271;
 
 int wl1271_event_unmask(struct wl1271 *wl);
 int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
 
+void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable);
+void wlcore_event_sched_scan_completed(struct wl1271 *wl,
+                                      u8 status);
+void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
+                                  unsigned long roles_bitmap,
+                                  unsigned long allowed_bitmap);
+void wlcore_event_channel_switch(struct wl1271 *wl,
+                                unsigned long roles_bitmap,
+                                bool success);
+void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap);
+void wlcore_event_dummy_packet(struct wl1271 *wl);
+void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap);
+void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap);
+void wlcore_event_roc_complete(struct wl1271 *wl);
+void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr);
 #endif
index 2673d78..7fd260c 100644 (file)
@@ -201,4 +201,45 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
        return buf_offset;
 }
 
+static inline void
+wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                       struct ieee80211_sta *sta, u32 changed)
+{
+       if (wl->ops->sta_rc_update)
+               wl->ops->sta_rc_update(wl, wlvif, sta, changed);
+}
+
+static inline int
+wlcore_hw_set_peer_cap(struct wl1271 *wl,
+                      struct ieee80211_sta_ht_cap *ht_cap,
+                      bool allow_ht_operation,
+                      u32 rate_set, u8 hlid)
+{
+       if (wl->ops->set_peer_cap)
+               return wl->ops->set_peer_cap(wl, ht_cap, allow_ht_operation,
+                                            rate_set, hlid);
+
+       return 0;
+}
+
+static inline bool
+wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                       struct wl1271_link *lnk)
+{
+       if (!wl->ops->lnk_high_prio)
+               BUG_ON(1);
+
+       return wl->ops->lnk_high_prio(wl, hlid, lnk);
+}
+
+static inline bool
+wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                      struct wl1271_link *lnk)
+{
+       if (!wl->ops->lnk_low_prio)
+               BUG_ON(1);
+
+       return wl->ops->lnk_low_prio(wl, hlid, lnk);
+}
+
 #endif
index 32d157f..5c6f11e 100644 (file)
@@ -41,14 +41,14 @@ int wl1271_init_templates_config(struct wl1271 *wl)
 
        /* send empty templates for fw memory reservation */
        ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                     CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
+                                     wl->scan_templ_id_2_4, NULL,
                                      WL1271_CMD_TEMPL_MAX_SIZE,
                                      0, WL1271_RATE_AUTOMATIC);
        if (ret < 0)
                return ret;
 
        ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                     CMD_TEMPL_CFG_PROBE_REQ_5,
+                                     wl->scan_templ_id_5,
                                      NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0,
                                      WL1271_RATE_AUTOMATIC);
        if (ret < 0)
@@ -56,14 +56,16 @@ int wl1271_init_templates_config(struct wl1271 *wl)
 
        if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) {
                ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                             CMD_TEMPL_APP_PROBE_REQ_2_4, NULL,
+                                             wl->sched_scan_templ_id_2_4,
+                                             NULL,
                                              WL1271_CMD_TEMPL_MAX_SIZE,
                                              0, WL1271_RATE_AUTOMATIC);
                if (ret < 0)
                        return ret;
 
                ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                             CMD_TEMPL_APP_PROBE_REQ_5, NULL,
+                                             wl->sched_scan_templ_id_5,
+                                             NULL,
                                              WL1271_CMD_TEMPL_MAX_SIZE,
                                              0, WL1271_RATE_AUTOMATIC);
                if (ret < 0)
@@ -463,7 +465,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
                supported_rates = CONF_TX_OFDM_RATES;
        else
-               supported_rates = CONF_TX_AP_ENABLED_RATES;
+               supported_rates = CONF_TX_ENABLED_RATES;
 
        /* unconditionally enable HT rates */
        supported_rates |= CONF_TX_MCS_RATES;
@@ -575,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
                /* Configure for power according to debugfs */
                if (sta_auth != WL1271_PSM_ILLEGAL)
                        ret = wl1271_acx_sleep_auth(wl, sta_auth);
-               /* Configure for power always on */
-               else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
-                       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                /* Configure for ELP power saving */
                else
                        ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
@@ -679,6 +678,10 @@ int wl1271_hw_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
+       ret = wlcore_cmd_regdomain_config_locked(wl);
+       if (ret < 0)
+               return ret;
+
        /* Bluetooth WLAN coexistence */
        ret = wl1271_init_pta(wl);
        if (ret < 0)
index f48530f..af7d9f9 100644 (file)
@@ -105,13 +105,13 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
 {
        int ret;
 
-       ret = wlcore_raw_read(wl, addr, &wl->buffer_32,
-                             sizeof(wl->buffer_32), false);
+       ret = wlcore_raw_read(wl, addr, wl->buffer_32,
+                             sizeof(*wl->buffer_32), false);
        if (ret < 0)
                return ret;
 
        if (val)
-               *val = le32_to_cpu(wl->buffer_32);
+               *val = le32_to_cpu(*wl->buffer_32);
 
        return 0;
 }
@@ -119,9 +119,9 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
 static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr,
                                                  u32 val)
 {
-       wl->buffer_32 = cpu_to_le32(val);
-       return wlcore_raw_write(wl, addr, &wl->buffer_32,
-                               sizeof(wl->buffer_32), false);
+       *wl->buffer_32 = cpu_to_le32(val);
+       return wlcore_raw_write(wl, addr, wl->buffer_32,
+                               sizeof(*wl->buffer_32), false);
 }
 
 static inline int __must_check wlcore_read(struct wl1271 *wl, int addr,
index ce6e62a..e1dfdf9 100644 (file)
@@ -56,8 +56,8 @@
 #define WL1271_BOOT_RETRIES 3
 
 static char *fwlog_param;
-static bool bug_on_recovery;
-static bool no_recovery;
+static int bug_on_recovery = -1;
+static int no_recovery     = -1;
 
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
                                         struct ieee80211_vif *vif,
@@ -79,12 +79,10 @@ static int wl12xx_set_authorized(struct wl1271 *wl,
        if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
                return 0;
 
-       ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid);
+       ret = wl12xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid);
        if (ret < 0)
                return ret;
 
-       wl12xx_croc(wl, wlvif->role_id);
-
        wl1271_info("Association completed.");
        return 0;
 }
@@ -95,6 +93,8 @@ static void wl1271_reg_notify(struct wiphy *wiphy,
        struct ieee80211_supported_band *band;
        struct ieee80211_channel *ch;
        int i;
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
 
        band = wiphy->bands[IEEE80211_BAND_5GHZ];
        for (i = 0; i < band->n_channels; i++) {
@@ -107,6 +107,9 @@ static void wl1271_reg_notify(struct wiphy *wiphy,
                                     IEEE80211_CHAN_PASSIVE_SCAN;
 
        }
+
+       if (likely(wl->state == WLCORE_STATE_ON))
+               wlcore_regdomain_config(wl);
 }
 
 static int wl1271_set_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
@@ -301,6 +304,7 @@ out:
 static void wlcore_adjust_conf(struct wl1271 *wl)
 {
        /* Adjust settings according to optional module parameters */
+
        if (fwlog_param) {
                if (!strcmp(fwlog_param, "continuous")) {
                        wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
@@ -316,16 +320,22 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
                        wl1271_error("Unknown fwlog parameter %s", fwlog_param);
                }
        }
+
+       if (bug_on_recovery != -1)
+               wl->conf.recovery.bug_on_recovery = (u8) bug_on_recovery;
+
+       if (no_recovery != -1)
+               wl->conf.recovery.no_recovery = (u8) no_recovery;
 }
 
 static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
                                        struct wl12xx_vif *wlvif,
                                        u8 hlid, u8 tx_pkts)
 {
-       bool fw_ps, single_sta;
+       bool fw_ps, single_link;
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
-       single_sta = (wl->active_sta_count == 1);
+       single_link = (wl->active_link_count == 1);
 
        /*
         * Wake up from high level PS if the STA is asleep with too little
@@ -336,10 +346,10 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
 
        /*
         * Start high-level PS if the STA is asleep with enough blocks in FW.
-        * Make an exception if this is the only connected station. In this
-        * case FW-memory congestion is not a problem.
+        * Make an exception if this is the only connected link. In this
+        * case FW-memory congestion is less of a problem.
         */
-       else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+       else if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
@@ -347,11 +357,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
                                           struct wl_fw_status_2 *status)
 {
-       struct wl1271_link *lnk;
        u32 cur_fw_ps_map;
-       u8 hlid, cnt;
-
-       /* TODO: also use link_fast_bitmap here */
+       u8 hlid;
 
        cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
@@ -363,17 +370,9 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                wl->ap_fw_ps_map = cur_fw_ps_map;
        }
 
-       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) {
-               lnk = &wl->links[hlid];
-               cnt = status->counters.tx_lnk_free_pkts[hlid] -
-                       lnk->prev_freed_pkts;
-
-               lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[hlid];
-               lnk->allocated_pkts -= cnt;
-
+       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
                wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
-                                           lnk->allocated_pkts);
-       }
+                                           wl->links[hlid].allocated_pkts);
 }
 
 static int wlcore_fw_status(struct wl1271 *wl,
@@ -387,6 +386,7 @@ static int wlcore_fw_status(struct wl1271 *wl,
        int i;
        size_t status_len;
        int ret;
+       struct wl1271_link *lnk;
 
        status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
                sizeof(*status_2) + wl->fw_status_priv_len;
@@ -412,6 +412,17 @@ static int wlcore_fw_status(struct wl1271 *wl,
                wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
        }
 
+
+       for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+               lnk = &wl->links[i];
+               /* prevent wrap-around in freed-packets counter */
+               lnk->allocated_pkts -=
+                       (status_2->counters.tx_lnk_free_pkts[i] -
+                        lnk->prev_freed_pkts) & 0xff;
+
+               lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+       }
+
        /* prevent wrap-around in total blocks counter */
        if (likely(wl->tx_blocks_freed <=
                   le32_to_cpu(status_2->total_released_blks)))
@@ -464,6 +475,8 @@ static int wlcore_fw_status(struct wl1271 *wl,
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
                (s64)le32_to_cpu(status_2->fw_localtime);
 
+       wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
+
        return 0;
 }
 
@@ -800,11 +813,13 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        /*
         * Make sure the chip is awake and the logger isn't active.
-        * Do not send a stop fwlog command if the fw is hanged.
+        * Do not send a stop fwlog command if the fw is hanged or if
+        * dbgpins are used (due to some fw bug).
         */
        if (wl1271_ps_elp_wakeup(wl))
                goto out;
-       if (!wl->watchdog_recovery)
+       if (!wl->watchdog_recovery &&
+           wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
                wl12xx_cmd_stop_fwlog(wl);
 
        /* Read the first memory block address */
@@ -872,7 +887,8 @@ static void wlcore_print_recovery(struct wl1271 *wl)
        if (ret < 0)
                return;
 
-       wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts);
+       wl1271_info("pc: 0x%x, hint_sts: 0x%08x count: %d",
+                               pc, hint_sts, ++wl->recovery_count);
 
        wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
 }
@@ -895,10 +911,10 @@ static void wl1271_recovery_work(struct work_struct *work)
                wlcore_print_recovery(wl);
        }
 
-       BUG_ON(bug_on_recovery &&
+       BUG_ON(wl->conf.recovery.bug_on_recovery &&
               !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
 
-       if (no_recovery) {
+       if (wl->conf.recovery.no_recovery) {
                wl1271_info("No recovery (chosen on module load). Fw will remain stuck.");
                goto out_unlock;
        }
@@ -918,11 +934,6 @@ static void wl1271_recovery_work(struct work_struct *work)
        /* Prevent spurious TX during FW restart */
        wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
 
-       if (wl->sched_scanning) {
-               ieee80211_sched_scan_stopped(wl->hw);
-               wl->sched_scanning = false;
-       }
-
        /* reboot the chipset */
        while (!list_empty(&wl->wlvif_list)) {
                wlvif = list_first_entry(&wl->wlvif_list,
@@ -1139,7 +1150,6 @@ int wl1271_plt_stop(struct wl1271 *wl)
        cancel_work_sync(&wl->recovery_work);
        cancel_delayed_work_sync(&wl->elp_work);
        cancel_delayed_work_sync(&wl->tx_watchdog_work);
-       cancel_delayed_work_sync(&wl->connection_loss_work);
 
        mutex_lock(&wl->mutex);
        wl1271_power_off(wl);
@@ -1167,9 +1177,13 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
        int q, mapping;
        u8 hlid;
 
-       if (vif)
-               wlvif = wl12xx_vif_to_data(vif);
+       if (!vif) {
+               wl1271_debug(DEBUG_TX, "DROP skb with no vif");
+               ieee80211_free_txskb(hw, skb);
+               return;
+       }
 
+       wlvif = wl12xx_vif_to_data(vif);
        mapping = skb_get_queue_mapping(skb);
        q = wl1271_tx_get_queue(mapping);
 
@@ -1183,9 +1197,9 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
         * allow these packets through.
         */
        if (hlid == WL12XX_INVALID_LINK_ID ||
-           (wlvif && !test_bit(hlid, wlvif->links_map)) ||
-            (wlcore_is_queue_stopped(wl, q) &&
-             !wlcore_is_queue_stopped_by_reason(wl, q,
+           (!test_bit(hlid, wlvif->links_map)) ||
+            (wlcore_is_queue_stopped_locked(wl, wlvif, q) &&
+             !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
                        WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
                wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
                ieee80211_free_txskb(hw, skb);
@@ -1197,16 +1211,17 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
        skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
 
        wl->tx_queue_count[q]++;
+       wlvif->tx_queue_count[q]++;
 
        /*
         * The workqueue is slow to process the tx_queue and we need stop
         * the queue here, otherwise the queue will get too long.
         */
-       if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK &&
-           !wlcore_is_queue_stopped_by_reason(wl, q,
+       if (wlvif->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK &&
+           !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
                                        WLCORE_QUEUE_STOP_REASON_WATERMARK)) {
                wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
-               wlcore_stop_queue_locked(wl, q,
+               wlcore_stop_queue_locked(wl, wlvif, q,
                                         WLCORE_QUEUE_STOP_REASON_WATERMARK);
        }
 
@@ -1841,11 +1856,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        cancel_work_sync(&wl->tx_work);
        cancel_delayed_work_sync(&wl->elp_work);
        cancel_delayed_work_sync(&wl->tx_watchdog_work);
-       cancel_delayed_work_sync(&wl->connection_loss_work);
 
        /* let's notify MAC80211 about the remaining pending TX frames */
-       wl12xx_tx_reset(wl);
        mutex_lock(&wl->mutex);
+       wl12xx_tx_reset(wl);
 
        wl1271_power_off(wl);
        /*
@@ -1868,14 +1882,17 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        wl->time_offset = 0;
        wl->ap_fw_ps_map = 0;
        wl->ap_ps_map = 0;
-       wl->sched_scanning = false;
        wl->sleep_auth = WL1271_PSM_ILLEGAL;
        memset(wl->roles_map, 0, sizeof(wl->roles_map));
        memset(wl->links_map, 0, sizeof(wl->links_map));
        memset(wl->roc_map, 0, sizeof(wl->roc_map));
+       memset(wl->session_ids, 0, sizeof(wl->session_ids));
        wl->active_sta_count = 0;
+       wl->active_link_count = 0;
 
        /* The system link is always allocated */
+       wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0;
+       wl->links[WL12XX_SYSTEM_HLID].prev_freed_pkts = 0;
        __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
 
        /*
@@ -1901,6 +1918,12 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        wl->tx_res_if = NULL;
        kfree(wl->target_mem_map);
        wl->target_mem_map = NULL;
+
+       /*
+        * FW channels must be re-calibrated after recovery,
+        * clear the last Reg-Domain channel configuration.
+        */
+       memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
 }
 
 static void wlcore_op_stop(struct ieee80211_hw *hw)
@@ -1916,6 +1939,71 @@ static void wlcore_op_stop(struct ieee80211_hw *hw)
        mutex_unlock(&wl->mutex);
 }
 
+static void wlcore_channel_switch_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif, channel_switch_work);
+       wl = wlvif->wl;
+
+       wl1271_info("channel switch failed (role_id: %d).", wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* check the channel switch is still ongoing */
+       if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags))
+               goto out;
+
+       vif = wl12xx_wlvif_to_vif(wlvif);
+       ieee80211_chswitch_done(vif, false);
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl12xx_cmd_stop_channel_switch(wl, wlvif);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static void wlcore_connection_loss_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif, connection_loss_work);
+       wl = wlvif->wl;
+
+       wl1271_info("Connection loss work (role_id: %d).", wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* Call mac80211 connection loss */
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               goto out;
+
+       vif = wl12xx_wlvif_to_vif(wlvif);
+       ieee80211_connection_loss(vif);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
 {
        u8 policy = find_first_zero_bit(wl->rate_policies_map,
@@ -2035,15 +2123,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
                        wl12xx_allocate_rate_policy(wl,
                                                &wlvif->ap.ucast_rate_idx[i]);
-               wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES;
+               wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
                /*
                 * TODO: check if basic_rate shouldn't be
                 * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
                 * instead (the same thing for STA above).
                */
-               wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES;
+               wlvif->basic_rate = CONF_TX_ENABLED_RATES;
                /* TODO: this seems to be used only for STA, check it */
-               wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
+               wlvif->rate_set = CONF_TX_ENABLED_RATES;
        }
 
        wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
@@ -2063,6 +2151,10 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                  wl1271_rx_streaming_enable_work);
        INIT_WORK(&wlvif->rx_streaming_disable_work,
                  wl1271_rx_streaming_disable_work);
+       INIT_DELAYED_WORK(&wlvif->channel_switch_work,
+                         wlcore_channel_switch_work);
+       INIT_DELAYED_WORK(&wlvif->connection_loss_work,
+                         wlcore_connection_loss_work);
        INIT_LIST_HEAD(&wlvif->list);
 
        setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
@@ -2196,6 +2288,81 @@ static void wl12xx_force_active_psm(struct wl1271 *wl)
        }
 }
 
+struct wlcore_hw_queue_iter_data {
+       unsigned long hw_queue_map[BITS_TO_LONGS(WLCORE_NUM_MAC_ADDRESSES)];
+       /* current vif */
+       struct ieee80211_vif *vif;
+       /* is the current vif among those iterated */
+       bool cur_running;
+};
+
+static void wlcore_hw_queue_iter(void *data, u8 *mac,
+                                struct ieee80211_vif *vif)
+{
+       struct wlcore_hw_queue_iter_data *iter_data = data;
+
+       if (WARN_ON_ONCE(vif->hw_queue[0] == IEEE80211_INVAL_HW_QUEUE))
+               return;
+
+       if (iter_data->cur_running || vif == iter_data->vif) {
+               iter_data->cur_running = true;
+               return;
+       }
+
+       __set_bit(vif->hw_queue[0] / NUM_TX_QUEUES, iter_data->hw_queue_map);
+}
+
+static int wlcore_allocate_hw_queue_base(struct wl1271 *wl,
+                                        struct wl12xx_vif *wlvif)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wlcore_hw_queue_iter_data iter_data = {};
+       int i, q_base;
+
+       iter_data.vif = vif;
+
+       /* mark all bits taken by active interfaces */
+       ieee80211_iterate_active_interfaces_atomic(wl->hw,
+                                       IEEE80211_IFACE_ITER_RESUME_ALL,
+                                       wlcore_hw_queue_iter, &iter_data);
+
+       /* the current vif is already running in mac80211 (resume/recovery) */
+       if (iter_data.cur_running) {
+               wlvif->hw_queue_base = vif->hw_queue[0];
+               wl1271_debug(DEBUG_MAC80211,
+                            "using pre-allocated hw queue base %d",
+                            wlvif->hw_queue_base);
+
+               /* interface type might have changed type */
+               goto adjust_cab_queue;
+       }
+
+       q_base = find_first_zero_bit(iter_data.hw_queue_map,
+                                    WLCORE_NUM_MAC_ADDRESSES);
+       if (q_base >= WLCORE_NUM_MAC_ADDRESSES)
+               return -EBUSY;
+
+       wlvif->hw_queue_base = q_base * NUM_TX_QUEUES;
+       wl1271_debug(DEBUG_MAC80211, "allocating hw queue base: %d",
+                    wlvif->hw_queue_base);
+
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
+               wl->queue_stop_reasons[wlvif->hw_queue_base + i] = 0;
+               /* register hw queues in mac80211 */
+               vif->hw_queue[i] = wlvif->hw_queue_base + i;
+       }
+
+adjust_cab_queue:
+       /* the last places are reserved for cab queues per interface */
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               vif->cab_queue = NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES +
+                                wlvif->hw_queue_base / NUM_TX_QUEUES;
+       else
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+
+       return 0;
+}
+
 static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif)
 {
@@ -2242,6 +2409,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
+       ret = wlcore_allocate_hw_queue_base(wl, wlvif);
+       if (ret < 0)
+               goto out;
+
        if (wl12xx_need_fw_change(wl, vif_count, true)) {
                wl12xx_force_active_psm(wl);
                set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
@@ -2312,7 +2483,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
        wl1271_info("down");
 
        if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
-           wl->scan_vif == vif) {
+           wl->scan_wlvif == wlvif) {
                /*
                 * Rearm the tx watchdog just before idling scan. This
                 * prevents just-finished scans from triggering the watchdog
@@ -2321,11 +2492,21 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
 
                wl->scan.state = WL1271_SCAN_STATE_IDLE;
                memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-               wl->scan_vif = NULL;
+               wl->scan_wlvif = NULL;
                wl->scan.req = NULL;
                ieee80211_scan_completed(wl->hw, true);
        }
 
+       if (wl->sched_vif == wlvif) {
+               ieee80211_sched_scan_stopped(wl->hw);
+               wl->sched_vif = NULL;
+       }
+
+       if (wl->roc_vif == vif) {
+               wl->roc_vif = NULL;
+               ieee80211_remain_on_channel_expired(wl->hw);
+       }
+
        if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
                /* disable active roles */
                ret = wl1271_ps_elp_wakeup(wl);
@@ -2394,9 +2575,6 @@ deinit:
                /* Configure for power according to debugfs */
                if (sta_auth != WL1271_PSM_ILLEGAL)
                        wl1271_acx_sleep_auth(wl, sta_auth);
-               /* Configure for power always on */
-               else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
-                       wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                /* Configure for ELP power saving */
                else
                        wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
@@ -2408,6 +2586,7 @@ unlock:
        del_timer_sync(&wlvif->rx_streaming_timer);
        cancel_work_sync(&wlvif->rx_streaming_enable_work);
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
+       cancel_delayed_work_sync(&wlvif->connection_loss_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -2466,8 +2645,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
        return ret;
 }
 
-static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                         bool set_assoc)
+static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
        bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
@@ -2487,18 +2665,111 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        /* clear encryption type */
        wlvif->encryption_type = KEY_NONE;
 
-       if (set_assoc)
-               set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
-
        if (is_ibss)
                ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
-       else
+       else {
+               if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) {
+                       /*
+                        * TODO: this is an ugly workaround for wl12xx fw
+                        * bug - we are not able to tx/rx after the first
+                        * start_sta, so make dummy start+stop calls,
+                        * and then call start_sta again.
+                        * this should be fixed in the fw.
+                        */
+                       wl12xx_cmd_role_start_sta(wl, wlvif);
+                       wl12xx_cmd_role_stop_sta(wl, wlvif);
+               }
+
                ret = wl12xx_cmd_role_start_sta(wl, wlvif);
+       }
+
+       return ret;
+}
+
+static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb,
+                           int offset)
+{
+       u8 ssid_len;
+       const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
+                                        skb->len - offset);
+
+       if (!ptr) {
+               wl1271_error("No SSID in IEs!");
+               return -ENOENT;
+       }
+
+       ssid_len = ptr[1];
+       if (ssid_len > IEEE80211_MAX_SSID_LEN) {
+               wl1271_error("SSID is too long!");
+               return -EINVAL;
+       }
+
+       wlvif->ssid_len = ssid_len;
+       memcpy(wlvif->ssid, ptr+2, ssid_len);
+       return 0;
+}
+
+static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct sk_buff *skb;
+       int ieoffset;
+
+       /* we currently only support setting the ssid from the ap probe req */
+       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+               return -EINVAL;
+
+       skb = ieee80211_ap_probereq_get(wl->hw, vif);
+       if (!skb)
+               return -EINVAL;
+
+       ieoffset = offsetof(struct ieee80211_mgmt,
+                           u.probe_req.variable);
+       wl1271_ssid_set(wlvif, skb, ieoffset);
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct ieee80211_bss_conf *bss_conf,
+                           u32 sta_rate_set)
+{
+       int ieoffset;
+       int ret;
+
+       wlvif->aid = bss_conf->aid;
+       wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef);
+       wlvif->beacon_int = bss_conf->beacon_int;
+       wlvif->wmm_enabled = bss_conf->qos;
+
+       set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
+
+       /*
+        * with wl1271, we don't need to update the
+        * beacon_int and dtim_period, because the firmware
+        * updates it by itself when the first beacon is
+        * received after a join.
+        */
+       ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
        if (ret < 0)
-               goto out;
+               return ret;
 
-       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-               goto out;
+       /*
+        * Get a template for hardware connection maintenance
+        */
+       dev_kfree_skb(wlvif->probereq);
+       wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
+                                                       wlvif,
+                                                       NULL);
+       ieoffset = offsetof(struct ieee80211_mgmt,
+                           u.probe_req.variable);
+       wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset);
+
+       /* enable the connection monitoring feature */
+       ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
+       if (ret < 0)
+               return ret;
 
        /*
         * The join command disable the keep-alive mode, shut down its process,
@@ -2508,35 +2779,83 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
         */
        ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl1271_acx_keep_alive_config(wl, wlvif,
                                           wlvif->sta.klv_template_id,
                                           ACX_KEEP_ALIVE_TPL_VALID);
        if (ret < 0)
-               goto out;
+               return ret;
+
+       /*
+        * The default fw psm configuration is AUTO, while mac80211 default
+        * setting is off (ACTIVE), so sync the fw with the correct value.
+        */
+       ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE);
+       if (ret < 0)
+               return ret;
+
+       if (sta_rate_set) {
+               wlvif->rate_set =
+                       wl1271_tx_enabled_rates_get(wl,
+                                                   sta_rate_set,
+                                                   wlvif->band);
+               ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+       }
 
-out:
        return ret;
 }
 
-static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
+       bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+
+       /* make sure we are connected (sta) joined */
+       if (sta &&
+           !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               return false;
+
+       /* make sure we are joined (ibss) */
+       if (!sta &&
+           test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))
+               return false;
+
+       if (sta) {
+               /* use defaults when not associated */
+               wlvif->aid = 0;
+
+               /* free probe-request template */
+               dev_kfree_skb(wlvif->probereq);
+               wlvif->probereq = NULL;
+
+               /* disable connection monitor features */
+               ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
+
+               /* Disable the keep-alive feature */
+               ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
+       }
 
        if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
                struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
-               wl12xx_cmd_stop_channel_switch(wl);
+               wl12xx_cmd_stop_channel_switch(wl, wlvif);
                ieee80211_chswitch_done(vif, false);
+               cancel_delayed_work(&wlvif->channel_switch_work);
        }
 
        /* invalidate keep-alive template */
@@ -2544,17 +2863,11 @@ static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                                     wlvif->sta.klv_template_id,
                                     ACX_KEEP_ALIVE_TPL_INVALID);
 
-       /* to stop listening to a channel, we disconnect */
-       ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
-       if (ret < 0)
-               goto out;
-
        /* reset TX security counters on a clean disconnect */
        wlvif->tx_security_last_seq_lsb = 0;
        wlvif->tx_security_seq = 0;
 
-out:
-       return ret;
+       return 0;
 }
 
 static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
@@ -2563,195 +2876,38 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        wlvif->rate_set = wlvif->basic_rate_set;
 }
 
-static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                                 bool idle)
+static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            struct ieee80211_conf *conf, u32 changed)
 {
        int ret;
-       bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
-
-       if (idle == cur_idle)
-               return 0;
 
-       if (idle) {
-               /* no need to croc if we weren't busy (e.g. during boot) */
-               if (wl12xx_dev_role_started(wlvif)) {
-                       ret = wl12xx_stop_dev(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-               }
-               wlvif->rate_set =
-                       wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
-               ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+       if (conf->power_level != wlvif->power_level) {
+               ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
                if (ret < 0)
-                       goto out;
-               clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
-       } else {
-               /* The current firmware only supports sched_scan in idle */
-               if (wl->sched_scanning) {
-                       wl1271_scan_sched_scan_stop(wl, wlvif);
-                       ieee80211_sched_scan_stopped(wl->hw);
-               }
+                       return ret;
 
-               ret = wl12xx_start_dev(wl, wlvif);
-               if (ret < 0)
-                       goto out;
-               set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+               wlvif->power_level = conf->power_level;
        }
 
-out:
-       return ret;
+       return 0;
 }
 
-static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                            struct ieee80211_conf *conf, u32 changed)
+static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
 {
-       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
-       int channel, ret;
-
-       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_conf *conf = &hw->conf;
+       int ret = 0;
 
-       /* if the channel changes while joined, join again */
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
-           ((wlvif->band != conf->channel->band) ||
-            (wlvif->channel != channel) ||
-            (wlvif->channel_type != conf->channel_type))) {
-               /* send all pending packets */
-               ret = wlcore_tx_work_locked(wl);
-               if (ret < 0)
-                       return ret;
-
-               wlvif->band = conf->channel->band;
-               wlvif->channel = channel;
-               wlvif->channel_type = conf->channel_type;
-
-               if (is_ap) {
-                       wl1271_set_band_rate(wl, wlvif);
-                       ret = wl1271_init_ap_rates(wl, wlvif);
-                       if (ret < 0)
-                               wl1271_error("AP rate policy change failed %d",
-                                            ret);
-               } else {
-                       /*
-                        * FIXME: the mac80211 should really provide a fixed
-                        * rate to use here. for now, just use the smallest
-                        * possible rate for the band as a fixed rate for
-                        * association frames and other control messages.
-                        */
-                       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-                               wl1271_set_band_rate(wl, wlvif);
-
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               wl1271_warning("rate policy for channel "
-                                              "failed %d", ret);
-
-                       /*
-                        * change the ROC channel. do it only if we are
-                        * not idle. otherwise, CROC will be called
-                        * anyway.
-                        */
-                       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED,
-                                     &wlvif->flags) &&
-                           wl12xx_dev_role_started(wlvif) &&
-                           !(conf->flags & IEEE80211_CONF_IDLE)) {
-                               ret = wl12xx_stop_dev(wl, wlvif);
-                               if (ret < 0)
-                                       return ret;
-
-                               ret = wl12xx_start_dev(wl, wlvif);
-                               if (ret < 0)
-                                       return ret;
-                       }
-               }
-       }
-
-       if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) {
-
-               if ((conf->flags & IEEE80211_CONF_PS) &&
-                   test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
-                   !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
-
-                       int ps_mode;
-                       char *ps_mode_str;
-
-                       if (wl->conf.conn.forced_ps) {
-                               ps_mode = STATION_POWER_SAVE_MODE;
-                               ps_mode_str = "forced";
-                       } else {
-                               ps_mode = STATION_AUTO_PS_MODE;
-                               ps_mode_str = "auto";
-                       }
-
-                       wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
-
-                       ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
-
-                       if (ret < 0)
-                               wl1271_warning("enter %s ps failed %d",
-                                              ps_mode_str, ret);
-
-               } else if (!(conf->flags & IEEE80211_CONF_PS) &&
-                          test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
-
-                       wl1271_debug(DEBUG_PSM, "auto ps disabled");
-
-                       ret = wl1271_ps_set_mode(wl, wlvif,
-                                                STATION_ACTIVE_MODE);
-                       if (ret < 0)
-                               wl1271_warning("exit auto ps failed %d", ret);
-               }
-       }
-
-       if (conf->power_level != wlvif->power_level) {
-               ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
-               if (ret < 0)
-                       return ret;
-
-               wlvif->power_level = conf->power_level;
-       }
-
-       return 0;
-}
-
-static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
-{
-       struct wl1271 *wl = hw->priv;
-       struct wl12xx_vif *wlvif;
-       struct ieee80211_conf *conf = &hw->conf;
-       int channel, ret = 0;
-
-       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
-
-       wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
+       wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
                     " changed 0x%x",
-                    channel,
                     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
                     conf->power_level,
                     conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
                         changed);
 
-       /*
-        * mac80211 will go to idle nearly immediately after transmitting some
-        * frames, such as the deauth. To make sure those frames reach the air,
-        * wait here until the TX queue is fully flushed.
-        */
-       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
-           ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
-            (conf->flags & IEEE80211_CONF_IDLE)))
-               wl1271_tx_flush(wl);
-
        mutex_lock(&wl->mutex);
 
-       /* we support configuring the channel and band even while off */
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               wl->band = conf->channel->band;
-               wl->channel = channel;
-               wl->channel_type = conf->channel_type;
-       }
-
        if (changed & IEEE80211_CONF_CHANGE_POWER)
                wl->power_level = conf->power_level;
 
@@ -3071,10 +3227,7 @@ static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                 * stop the queues and flush to ensure the next packets are
                 * in sync with FW spare block accounting
                 */
-               mutex_lock(&wl->mutex);
                wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
-               mutex_unlock(&wl->mutex);
-
                wl1271_tx_flush(wl);
        }
 
@@ -3200,6 +3353,29 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
 }
 EXPORT_SYMBOL_GPL(wlcore_set_key);
 
+void wlcore_regdomain_config(struct wl1271 *wl)
+{
+       int ret;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return;
+
+       mutex_lock(&wl->mutex);
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_cmd_regdomain_config_locked(wl);
+       if (ret < 0) {
+               wl12xx_queue_recovery_work(wl);
+               goto out;
+       }
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             struct cfg80211_scan_request *req)
@@ -3239,7 +3415,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                goto out_sleep;
        }
 
-       ret = wl1271_scan(hw->priv, vif, ssid, len, req);
+       ret = wlcore_scan(hw->priv, vif, ssid, len, req);
 out_sleep:
        wl1271_ps_elp_sleep(wl);
 out:
@@ -3252,6 +3428,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
@@ -3269,7 +3446,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
                goto out;
 
        if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
-               ret = wl1271_scan_stop(wl);
+               ret = wl->ops->scan_stop(wl, wlvif);
                if (ret < 0)
                        goto out_sleep;
        }
@@ -3282,7 +3459,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
 
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-       wl->scan_vif = NULL;
+       wl->scan_wlvif = NULL;
        wl->scan.req = NULL;
        ieee80211_scan_completed(wl->hw, true);
 
@@ -3316,15 +3493,11 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
+       ret = wl->ops->sched_scan_start(wl, wlvif, req, ies);
        if (ret < 0)
                goto out_sleep;
 
-       ret = wl1271_scan_sched_scan_start(wl, wlvif);
-       if (ret < 0)
-               goto out_sleep;
-
-       wl->sched_scanning = true;
+       wl->sched_vif = wlvif;
 
 out_sleep:
        wl1271_ps_elp_sleep(wl);
@@ -3351,7 +3524,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       wl1271_scan_sched_scan_stop(wl, wlvif);
+       wl->ops->sched_scan_stop(wl, wlvif);
 
        wl1271_ps_elp_sleep(wl);
 out:
@@ -3416,30 +3589,6 @@ out:
        return ret;
 }
 
-static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
-                           int offset)
-{
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       u8 ssid_len;
-       const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
-                                        skb->len - offset);
-
-       if (!ptr) {
-               wl1271_error("No SSID in IEs!");
-               return -ENOENT;
-       }
-
-       ssid_len = ptr[1];
-       if (ssid_len > IEEE80211_MAX_SSID_LEN) {
-               wl1271_error("SSID is too long!");
-               return -EINVAL;
-       }
-
-       wlvif->ssid_len = ssid_len;
-       memcpy(wlvif->ssid, ptr+2, ssid_len);
-       return 0;
-}
-
 static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
 {
        int len;
@@ -3620,7 +3769,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
 
        wl1271_debug(DEBUG_MASTER, "beacon updated");
 
-       ret = wl1271_ssid_set(vif, beacon, ieoffset);
+       ret = wl1271_ssid_set(wlvif, beacon, ieoffset);
        if (ret < 0) {
                dev_kfree_skb(beacon);
                goto out;
@@ -3637,6 +3786,12 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
                goto out;
        }
 
+       wlvif->wmm_enabled =
+               cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                                       WLAN_OUI_TYPE_MICROSOFT_WMM,
+                                       beacon->data + ieoffset,
+                                       beacon->len - ieoffset);
+
        /*
         * In case we already have a probe-resp beacon set explicitly
         * by usermode, don't use the beacon data.
@@ -3690,7 +3845,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
        bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret = 0;
 
-       if ((changed & BSS_CHANGED_BEACON_INT)) {
+       if (changed & BSS_CHANGED_BEACON_INT) {
                wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
                        bss_conf->beacon_int);
 
@@ -3703,7 +3858,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
                wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
        }
 
-       if ((changed & BSS_CHANGED_BEACON)) {
+       if (changed & BSS_CHANGED_BEACON) {
                ret = wlcore_set_beacon_template(wl, vif, is_ap);
                if (ret < 0)
                        goto out;
@@ -3724,7 +3879,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret = 0;
 
-       if ((changed & BSS_CHANGED_BASIC_RATES)) {
+       if (changed & BSS_CHANGED_BASIC_RATES) {
                u32 rates = bss_conf->basic_rates;
 
                wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
@@ -3755,7 +3910,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
        if (ret < 0)
                goto out;
 
-       if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
                if (bss_conf->enable_beacon) {
                        if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
                                ret = wl12xx_cmd_role_start_ap(wl, wlvif);
@@ -3802,6 +3957,79 @@ out:
        return;
 }
 
+static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct ieee80211_bss_conf *bss_conf,
+                           u32 sta_rate_set)
+{
+       u32 rates;
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211,
+            "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x",
+            bss_conf->bssid, bss_conf->aid,
+            bss_conf->beacon_int,
+            bss_conf->basic_rates, sta_rate_set);
+
+       wlvif->beacon_int = bss_conf->beacon_int;
+       rates = bss_conf->basic_rates;
+       wlvif->basic_rate_set =
+               wl1271_tx_enabled_rates_get(wl, rates,
+                                           wlvif->band);
+       wlvif->basic_rate =
+               wl1271_tx_min_rate_get(wl,
+                                      wlvif->basic_rate_set);
+
+       if (sta_rate_set)
+               wlvif->rate_set =
+                       wl1271_tx_enabled_rates_get(wl,
+                                               sta_rate_set,
+                                               wlvif->band);
+
+       /* we only support sched_scan while not connected */
+       if (wl->sched_vif == wlvif)
+               wl->ops->sched_scan_stop(wl, wlvif);
+
+       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       ret = wl12xx_cmd_build_null_data(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif));
+       if (ret < 0)
+               return ret;
+
+       wlcore_set_ssid(wl, wlvif);
+
+       set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+
+       return 0;
+}
+
+static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret;
+
+       /* revert back to minimum rates for the current band */
+       wl1271_set_band_rate(wl, wlvif);
+       wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+
+       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) {
+               ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+       }
+
+       clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+       return 0;
+}
 /* STA/IBSS mode changes */
 static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                        struct ieee80211_vif *vif,
@@ -3809,7 +4037,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                        u32 changed)
 {
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       bool do_join = false, set_assoc = false;
+       bool do_join = false;
        bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
        bool ibss_joined = false;
        u32 sta_rate_set = 0;
@@ -3830,9 +4058,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
                        ibss_joined = true;
                } else {
-                       if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
-                                              &wlvif->flags))
-                               wl1271_unjoin(wl, wlvif);
+                       wlcore_unset_assoc(wl, wlvif);
+                       wl12xx_cmd_role_stop_sta(wl, wlvif);
                }
        }
 
@@ -3850,13 +4077,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                do_join = true;
        }
 
-       if (changed & BSS_CHANGED_IDLE && !is_ibss) {
-               ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
-               if (ret < 0)
-                       wl1271_warning("idle mode change failed %d", ret);
-       }
-
-       if ((changed & BSS_CHANGED_CQM)) {
+       if (changed & BSS_CHANGED_CQM) {
                bool enable = false;
                if (bss_conf->cqm_rssi_thold)
                        enable = true;
@@ -3868,150 +4089,39 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
        }
 
-       if (changed & BSS_CHANGED_BSSID)
-               if (!is_zero_ether_addr(bss_conf->bssid)) {
-                       ret = wl12xx_cmd_build_null_data(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       ret = wl1271_build_qos_null_data(wl, vif);
-                       if (ret < 0)
-                               goto out;
-               }
-
-       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
+       if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT |
+                      BSS_CHANGED_ASSOC)) {
                rcu_read_lock();
                sta = ieee80211_find_sta(vif, bss_conf->bssid);
-               if (!sta)
-                       goto sta_not_found;
-
-               /* save the supp_rates of the ap */
-               sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
-               if (sta->ht_cap.ht_supported)
-                       sta_rate_set |=
-                         (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
-                         (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
-               sta_ht_cap = sta->ht_cap;
-               sta_exists = true;
-
-sta_not_found:
+               if (sta) {
+                       u8 *rx_mask = sta->ht_cap.mcs.rx_mask;
+
+                       /* save the supp_rates of the ap */
+                       sta_rate_set = sta->supp_rates[wlvif->band];
+                       if (sta->ht_cap.ht_supported)
+                               sta_rate_set |=
+                                       (rx_mask[0] << HW_HT_RATES_OFFSET) |
+                                       (rx_mask[1] << HW_MIMO_RATES_OFFSET);
+                       sta_ht_cap = sta->ht_cap;
+                       sta_exists = true;
+               }
+
                rcu_read_unlock();
        }
 
-       if ((changed & BSS_CHANGED_ASSOC)) {
-               if (bss_conf->assoc) {
-                       u32 rates;
-                       int ieoffset;
-                       wlvif->aid = bss_conf->aid;
-                       wlvif->channel_type =
-                               cfg80211_get_chandef_type(&bss_conf->chandef);
-                       wlvif->beacon_int = bss_conf->beacon_int;
-                       do_join = true;
-                       set_assoc = true;
-
-                       /*
-                        * use basic rates from AP, and determine lowest rate
-                        * to use with control frames.
-                        */
-                       rates = bss_conf->basic_rates;
-                       wlvif->basic_rate_set =
-                               wl1271_tx_enabled_rates_get(wl, rates,
-                                                           wlvif->band);
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       if (sta_rate_set)
-                               wlvif->rate_set =
-                                       wl1271_tx_enabled_rates_get(wl,
-                                                               sta_rate_set,
-                                                               wlvif->band);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       /*
-                        * with wl1271, we don't need to update the
-                        * beacon_int and dtim_period, because the firmware
-                        * updates it by itself when the first beacon is
-                        * received after a join.
-                        */
-                       ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
+       if (changed & BSS_CHANGED_BSSID) {
+               if (!is_zero_ether_addr(bss_conf->bssid)) {
+                       ret = wlcore_set_bssid(wl, wlvif, bss_conf,
+                                              sta_rate_set);
                        if (ret < 0)
                                goto out;
 
-                       /*
-                        * Get a template for hardware connection maintenance
-                        */
-                       dev_kfree_skb(wlvif->probereq);
-                       wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
-                                                                       wlvif,
-                                                                       NULL);
-                       ieoffset = offsetof(struct ieee80211_mgmt,
-                                           u.probe_req.variable);
-                       wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
-
-                       /* enable the connection monitoring feature */
-                       ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
-                       if (ret < 0)
-                               goto out;
+                       /* Need to update the BSSID (for filtering etc) */
+                       do_join = true;
                } else {
-                       /* use defaults when not associated */
-                       bool was_assoc =
-                           !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
-                                                &wlvif->flags);
-                       bool was_ifup =
-                           !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
-                                                &wlvif->flags);
-                       wlvif->aid = 0;
-
-                       /* free probe-request template */
-                       dev_kfree_skb(wlvif->probereq);
-                       wlvif->probereq = NULL;
-
-                       /* revert back to minimum rates for the current band */
-                       wl1271_set_band_rate(wl, wlvif);
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       /* disable connection monitor features */
-                       ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
-
-                       /* Disable the keep-alive feature */
-                       ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
+                       ret = wlcore_clear_bssid(wl, wlvif);
                        if (ret < 0)
                                goto out;
-
-                       /* restore the bssid filter and go to dummy bssid */
-                       if (was_assoc) {
-                               /*
-                                * we might have to disable roc, if there was
-                                * no IF_OPER_UP notification.
-                                */
-                               if (!was_ifup) {
-                                       ret = wl12xx_croc(wl, wlvif->role_id);
-                                       if (ret < 0)
-                                               goto out;
-                               }
-                               /*
-                                * (we also need to disable roc in case of
-                                * roaming on the same channel. until we will
-                                * have a better flow...)
-                                */
-                               if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
-                                       ret = wl12xx_croc(wl,
-                                                         wlvif->dev_role_id);
-                                       if (ret < 0)
-                                               goto out;
-                               }
-
-                               wl1271_unjoin(wl, wlvif);
-                               if (!bss_conf->idle)
-                                       wl12xx_start_dev(wl, wlvif);
-                       }
                }
        }
 
@@ -4041,71 +4151,87 @@ sta_not_found:
                goto out;
 
        if (do_join) {
-               ret = wl1271_join(wl, wlvif, set_assoc);
+               ret = wlcore_join(wl, wlvif);
                if (ret < 0) {
                        wl1271_warning("cmd join failed %d", ret);
                        goto out;
                }
+       }
 
-               /* ROC until connected (after EAPOL exchange) */
-               if (!is_ibss) {
-                       ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
+       if (changed & BSS_CHANGED_ASSOC) {
+               if (bss_conf->assoc) {
+                       ret = wlcore_set_assoc(wl, wlvif, bss_conf,
+                                              sta_rate_set);
                        if (ret < 0)
                                goto out;
 
                        if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
                                wl12xx_set_authorized(wl, wlvif);
+               } else {
+                       wlcore_unset_assoc(wl, wlvif);
                }
-               /*
-                * stop device role if started (we might already be in
-                * STA/IBSS role).
-                */
-               if (wl12xx_dev_role_started(wlvif)) {
-                       ret = wl12xx_stop_dev(wl, wlvif);
+       }
+
+       if (changed & BSS_CHANGED_PS) {
+               if ((bss_conf->ps) &&
+                   test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
+                   !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+                       int ps_mode;
+                       char *ps_mode_str;
+
+                       if (wl->conf.conn.forced_ps) {
+                               ps_mode = STATION_POWER_SAVE_MODE;
+                               ps_mode_str = "forced";
+                       } else {
+                               ps_mode = STATION_AUTO_PS_MODE;
+                               ps_mode_str = "auto";
+                       }
+
+                       wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
+
+                       ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
                        if (ret < 0)
-                               goto out;
+                               wl1271_warning("enter %s ps failed %d",
+                                              ps_mode_str, ret);
+               } else if (!bss_conf->ps &&
+                          test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+                       wl1271_debug(DEBUG_PSM, "auto ps disabled");
+
+                       ret = wl1271_ps_set_mode(wl, wlvif,
+                                                STATION_ACTIVE_MODE);
+                       if (ret < 0)
+                               wl1271_warning("exit auto ps failed %d", ret);
                }
        }
 
        /* Handle new association with HT. Do this after join. */
-       if (sta_exists) {
-               if ((changed & BSS_CHANGED_HT) &&
-                   (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
-                       ret = wl1271_acx_set_ht_capabilities(wl,
-                                                            &sta_ht_cap,
-                                                            true,
-                                                            wlvif->sta.hlid);
-                       if (ret < 0) {
-                               wl1271_warning("Set ht cap true failed %d",
-                                              ret);
-                               goto out;
-                       }
+       if (sta_exists &&
+           (changed & BSS_CHANGED_HT)) {
+               bool enabled =
+                       bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
+
+               ret = wlcore_hw_set_peer_cap(wl,
+                                            &sta_ht_cap,
+                                            enabled,
+                                            wlvif->rate_set,
+                                            wlvif->sta.hlid);
+               if (ret < 0) {
+                       wl1271_warning("Set ht cap failed %d", ret);
+                       goto out;
+
                }
-               /* handle new association without HT and disassociation */
-               else if (changed & BSS_CHANGED_ASSOC) {
-                       ret = wl1271_acx_set_ht_capabilities(wl,
-                                                            &sta_ht_cap,
-                                                            false,
-                                                            wlvif->sta.hlid);
+
+               if (enabled) {
+                       ret = wl1271_acx_set_ht_information(wl, wlvif,
+                                               bss_conf->ht_operation_mode);
                        if (ret < 0) {
-                               wl1271_warning("Set ht cap false failed %d",
+                               wl1271_warning("Set ht information failed %d",
                                               ret);
                                goto out;
                        }
                }
        }
 
-       /* Handle HT information change. Done after join. */
-       if ((changed & BSS_CHANGED_HT) &&
-           (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
-               ret = wl1271_acx_set_ht_information(wl, wlvif,
-                                       bss_conf->ht_operation_mode);
-               if (ret < 0) {
-                       wl1271_warning("Set ht information failed %d", ret);
-                       goto out;
-               }
-       }
-
        /* Handle arp filtering. Done after join. */
        if ((changed & BSS_CHANGED_ARP_FILTER) ||
            (!is_ibss && (changed & BSS_CHANGED_QOS))) {
@@ -4113,8 +4239,7 @@ sta_not_found:
                wlvif->sta.qos = bss_conf->qos;
                WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
 
-               if (bss_conf->arp_addr_cnt == 1 &&
-                   bss_conf->arp_filter_enabled) {
+               if (bss_conf->arp_addr_cnt == 1 && bss_conf->assoc) {
                        wlvif->ip_addr = addr;
                        /*
                         * The template should have been configured only upon
@@ -4155,15 +4280,15 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret;
 
-       wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
-                    (int)changed);
+       wl1271_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x",
+                    wlvif->role_id, (int)changed);
 
        /*
         * make sure to cancel pending disconnections if our association
         * state changed
         */
        if (!is_ap && (changed & BSS_CHANGED_ASSOC))
-               cancel_delayed_work_sync(&wl->connection_loss_work);
+               cancel_delayed_work_sync(&wlvif->connection_loss_work);
 
        if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
            !bss_conf->enable_beacon)
@@ -4192,6 +4317,76 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
+static int wlcore_op_add_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_chanctx_conf *ctx)
+{
+       wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+       return 0;
+}
+
+static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw,
+                                    struct ieee80211_chanctx_conf *ctx)
+{
+       wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+}
+
+static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
+                                    struct ieee80211_chanctx_conf *ctx,
+                                    u32 changed)
+{
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 change chanctx %d (type %d) changed 0x%x",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def), changed);
+}
+
+static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_chanctx_conf *ctx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int channel = ieee80211_frequency_to_channel(
+               ctx->def.chan->center_freq);
+
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 assign chanctx (role %d) %d (type %d)",
+                    wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
+
+       mutex_lock(&wl->mutex);
+
+       wlvif->band = ctx->def.chan->band;
+       wlvif->channel = channel;
+       wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
+
+       /* update default rates according to the band */
+       wl1271_set_band_rate(wl, wlvif);
+
+       mutex_unlock(&wl->mutex);
+
+       return 0;
+}
+
+static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                          struct ieee80211_vif *vif,
+                                          struct ieee80211_chanctx_conf *ctx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 unassign chanctx (role %d) %d (type %d)",
+                    wlvif->role_id,
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+
+       wl1271_tx_flush(wl);
+}
+
 static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif, u16 queue,
                             const struct ieee80211_tx_queue_params *params)
@@ -4319,8 +4514,6 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
                return;
 
        clear_bit(hlid, wlvif->ap.sta_hlid_map);
-       memset(wl->links[hlid].addr, 0, ETH_ALEN);
-       wl->links[hlid].ba_bitmap = 0;
        __clear_bit(hlid, &wl->ap_ps_map);
        __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
        wl12xx_free_link(wl, wlvif, &hlid);
@@ -4380,6 +4573,45 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
        return ret;
 }
 
+static void wlcore_roc_if_possible(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif)
+{
+       if (find_first_bit(wl->roc_map,
+                          WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)
+               return;
+
+       if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID))
+               return;
+
+       wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
+}
+
+static void wlcore_update_inconn_sta(struct wl1271 *wl,
+                                    struct wl12xx_vif *wlvif,
+                                    struct wl1271_station *wl_sta,
+                                    bool in_connection)
+{
+       if (in_connection) {
+               if (WARN_ON(wl_sta->in_connection))
+                       return;
+               wl_sta->in_connection = true;
+               if (!wlvif->inconn_count++)
+                       wlcore_roc_if_possible(wl, wlvif);
+       } else {
+               if (!wl_sta->in_connection)
+                       return;
+
+               wl_sta->in_connection = false;
+               wlvif->inconn_count--;
+               if (WARN_ON(wlvif->inconn_count < 0))
+                       return;
+
+               if (!wlvif->inconn_count)
+                       if (test_bit(wlvif->role_id, wl->roc_map))
+                               wl12xx_croc(wl, wlvif->role_id);
+       }
+}
+
 static int wl12xx_update_sta_state(struct wl1271 *wl,
                                   struct wl12xx_vif *wlvif,
                                   struct ieee80211_sta *sta,
@@ -4398,8 +4630,13 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
        /* Add station (AP mode) */
        if (is_ap &&
            old_state == IEEE80211_STA_NOTEXIST &&
-           new_state == IEEE80211_STA_NONE)
-               return wl12xx_sta_add(wl, wlvif, sta);
+           new_state == IEEE80211_STA_NONE) {
+               ret = wl12xx_sta_add(wl, wlvif, sta);
+               if (ret)
+                       return ret;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, true);
+       }
 
        /* Remove station (AP mode) */
        if (is_ap &&
@@ -4407,35 +4644,59 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
            new_state == IEEE80211_STA_NOTEXIST) {
                /* must not fail */
                wl12xx_sta_remove(wl, wlvif, sta);
-               return 0;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
        }
 
        /* Authorize station (AP mode) */
        if (is_ap &&
            new_state == IEEE80211_STA_AUTHORIZED) {
-               ret = wl12xx_cmd_set_peer_state(wl, hlid);
+               ret = wl12xx_cmd_set_peer_state(wl, wlvif, hlid);
                if (ret < 0)
                        return ret;
 
                ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
                                                     hlid);
-               return ret;
+               if (ret)
+                       return ret;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
        }
 
        /* Authorize station */
        if (is_sta &&
            new_state == IEEE80211_STA_AUTHORIZED) {
                set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
-               return wl12xx_set_authorized(wl, wlvif);
+               ret = wl12xx_set_authorized(wl, wlvif);
+               if (ret)
+                       return ret;
        }
 
        if (is_sta &&
            old_state == IEEE80211_STA_AUTHORIZED &&
            new_state == IEEE80211_STA_ASSOC) {
                clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
-               return 0;
+               clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
+       }
+
+       /* clear ROCs on failure or authorization */
+       if (is_sta &&
+           (new_state == IEEE80211_STA_AUTHORIZED ||
+            new_state == IEEE80211_STA_NOTEXIST)) {
+               if (test_bit(wlvif->role_id, wl->roc_map))
+                       wl12xx_croc(wl, wlvif->role_id);
        }
 
+       if (is_sta &&
+           old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE) {
+               if (find_first_bit(wl->roc_map,
+                                  WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) {
+                       WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID);
+                       wl12xx_roc(wl, wlvif, wlvif->role_id,
+                                  wlvif->band, wlvif->channel);
+               }
+       }
        return 0;
 }
 
@@ -4500,18 +4761,18 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
 
        if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
                hlid = wlvif->sta.hlid;
-               ba_bitmap = &wlvif->sta.ba_rx_bitmap;
        } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
                struct wl1271_station *wl_sta;
 
                wl_sta = (struct wl1271_station *)sta->drv_priv;
                hlid = wl_sta->hlid;
-               ba_bitmap = &wl->links[hlid].ba_bitmap;
        } else {
                ret = -EINVAL;
                goto out;
        }
 
+       ba_bitmap = &wl->links[hlid].ba_bitmap;
+
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
@@ -4665,12 +4926,23 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
 
        /* TODO: change mac80211 to pass vif as param */
        wl12xx_for_each_wlvif_sta(wl, wlvif) {
-               ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch);
+               unsigned long delay_usec;
 
-               if (!ret)
-                       set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+               ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
+               if (ret)
+                       goto out_sleep;
+
+               set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+
+               /* indicate failure 5 seconds after channel switch time */
+               delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
+                            ch_switch->count;
+               ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
+                               usecs_to_jiffies(delay_usec) +
+                               msecs_to_jiffies(5000));
        }
 
+out_sleep:
        wl1271_ps_elp_sleep(wl);
 
 out:
@@ -4684,6 +4956,144 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
        wl1271_tx_flush(wl);
 }
 
+static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_channel *chan,
+                                      int duration)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl1271 *wl = hw->priv;
+       int channel, ret = 0;
+
+       channel = ieee80211_frequency_to_channel(chan->center_freq);
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)",
+                    channel, wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* return EBUSY if we can't ROC right now */
+       if (WARN_ON(wl->roc_vif ||
+                   find_first_bit(wl->roc_map,
+                                  WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
+       if (ret < 0)
+               goto out_sleep;
+
+       wl->roc_vif = vif;
+       ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
+                                    msecs_to_jiffies(duration));
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+       return ret;
+}
+
+static int __wlcore_roc_completed(struct wl1271 *wl)
+{
+       struct wl12xx_vif *wlvif;
+       int ret;
+
+       /* already completed */
+       if (unlikely(!wl->roc_vif))
+               return 0;
+
+       wlvif = wl12xx_vif_to_data(wl->roc_vif);
+
+       if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+               return -EBUSY;
+
+       ret = wl12xx_stop_dev(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       wl->roc_vif = NULL;
+
+       return 0;
+}
+
+static int wlcore_roc_completed(struct wl1271 *wl)
+{
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211, "roc complete");
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = __wlcore_roc_completed(wl);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
+static void wlcore_roc_complete_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wl = container_of(dwork, struct wl1271, roc_complete_work);
+
+       ret = wlcore_roc_completed(wl);
+       if (!ret)
+               ieee80211_remain_on_channel_expired(wl->hw);
+}
+
+static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+       struct wl1271 *wl = hw->priv;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 croc");
+
+       /* TODO: per-vif */
+       wl1271_tx_flush(wl);
+
+       /*
+        * we can't just flush_work here, because it might deadlock
+        * (as we might get called from the same workqueue)
+        */
+       cancel_delayed_work_sync(&wl->roc_complete_work);
+       wlcore_roc_completed(wl);
+
+       return 0;
+}
+
+static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_sta *sta,
+                                   u32 changed)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl1271 *wl = hw->priv;
+
+       wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
+}
+
 static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
 {
        struct wl1271 *wl = hw->priv;
@@ -4747,20 +5157,20 @@ static struct ieee80211_rate wl1271_rates[] = {
 
 /* can't be const, mac80211 writes to this */
 static struct ieee80211_channel wl1271_channels[] = {
-       { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
-       { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
-       { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
-       { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
-       { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
-       { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
-       { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
-       { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
-       { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
-       { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
-       { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
-       { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
-       { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
-       { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
+       { .hw_value = 1, .center_freq = 2412, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 2, .center_freq = 2417, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 3, .center_freq = 2422, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 4, .center_freq = 2427, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 5, .center_freq = 2432, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 6, .center_freq = 2437, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 7, .center_freq = 2442, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 8, .center_freq = 2447, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 9, .center_freq = 2452, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 10, .center_freq = 2457, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR },
 };
 
 /* can't be const, mac80211 writes to this */
@@ -4801,40 +5211,40 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = {
 
 /* 5 GHz band channels for WL1273 */
 static struct ieee80211_channel wl1271_channels_5ghz[] = {
-       { .hw_value = 7, .center_freq = 5035, .max_power = 25 },
-       { .hw_value = 8, .center_freq = 5040, .max_power = 25 },
-       { .hw_value = 9, .center_freq = 5045, .max_power = 25 },
-       { .hw_value = 11, .center_freq = 5055, .max_power = 25 },
-       { .hw_value = 12, .center_freq = 5060, .max_power = 25 },
-       { .hw_value = 16, .center_freq = 5080, .max_power = 25 },
-       { .hw_value = 34, .center_freq = 5170, .max_power = 25 },
-       { .hw_value = 36, .center_freq = 5180, .max_power = 25 },
-       { .hw_value = 38, .center_freq = 5190, .max_power = 25 },
-       { .hw_value = 40, .center_freq = 5200, .max_power = 25 },
-       { .hw_value = 42, .center_freq = 5210, .max_power = 25 },
-       { .hw_value = 44, .center_freq = 5220, .max_power = 25 },
-       { .hw_value = 46, .center_freq = 5230, .max_power = 25 },
-       { .hw_value = 48, .center_freq = 5240, .max_power = 25 },
-       { .hw_value = 52, .center_freq = 5260, .max_power = 25 },
-       { .hw_value = 56, .center_freq = 5280, .max_power = 25 },
-       { .hw_value = 60, .center_freq = 5300, .max_power = 25 },
-       { .hw_value = 64, .center_freq = 5320, .max_power = 25 },
-       { .hw_value = 100, .center_freq = 5500, .max_power = 25 },
-       { .hw_value = 104, .center_freq = 5520, .max_power = 25 },
-       { .hw_value = 108, .center_freq = 5540, .max_power = 25 },
-       { .hw_value = 112, .center_freq = 5560, .max_power = 25 },
-       { .hw_value = 116, .center_freq = 5580, .max_power = 25 },
-       { .hw_value = 120, .center_freq = 5600, .max_power = 25 },
-       { .hw_value = 124, .center_freq = 5620, .max_power = 25 },
-       { .hw_value = 128, .center_freq = 5640, .max_power = 25 },
-       { .hw_value = 132, .center_freq = 5660, .max_power = 25 },
-       { .hw_value = 136, .center_freq = 5680, .max_power = 25 },
-       { .hw_value = 140, .center_freq = 5700, .max_power = 25 },
-       { .hw_value = 149, .center_freq = 5745, .max_power = 25 },
-       { .hw_value = 153, .center_freq = 5765, .max_power = 25 },
-       { .hw_value = 157, .center_freq = 5785, .max_power = 25 },
-       { .hw_value = 161, .center_freq = 5805, .max_power = 25 },
-       { .hw_value = 165, .center_freq = 5825, .max_power = 25 },
+       { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 36, .center_freq = 5180, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 38, .center_freq = 5190, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 40, .center_freq = 5200, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 42, .center_freq = 5210, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 44, .center_freq = 5220, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 46, .center_freq = 5230, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 48, .center_freq = 5240, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 52, .center_freq = 5260, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 56, .center_freq = 5280, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 60, .center_freq = 5300, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 64, .center_freq = 5320, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 100, .center_freq = 5500, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 104, .center_freq = 5520, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 108, .center_freq = 5540, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 112, .center_freq = 5560, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 116, .center_freq = 5580, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 120, .center_freq = 5600, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 124, .center_freq = 5620, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 128, .center_freq = 5640, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 132, .center_freq = 5660, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 136, .center_freq = 5680, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 140, .center_freq = 5700, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 149, .center_freq = 5745, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 153, .center_freq = 5765, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 157, .center_freq = 5785, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 161, .center_freq = 5805, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 165, .center_freq = 5825, .max_power = WLCORE_MAX_TXPWR },
 };
 
 static struct ieee80211_supported_band wl1271_band_5ghz = {
@@ -4875,6 +5285,14 @@ static const struct ieee80211_ops wl1271_ops = {
        .set_bitrate_mask = wl12xx_set_bitrate_mask,
        .channel_switch = wl12xx_op_channel_switch,
        .flush = wlcore_op_flush,
+       .remain_on_channel = wlcore_op_remain_on_channel,
+       .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
+       .add_chanctx = wlcore_op_add_chanctx,
+       .remove_chanctx = wlcore_op_remove_chanctx,
+       .change_chanctx = wlcore_op_change_chanctx,
+       .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
+       .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
+       .sta_rc_update = wlcore_op_sta_rc_update,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 
@@ -5044,34 +5462,6 @@ static struct bin_attribute fwlog_attr = {
        .read = wl1271_sysfs_read_fwlog,
 };
 
-static void wl1271_connection_loss_work(struct work_struct *work)
-{
-       struct delayed_work *dwork;
-       struct wl1271 *wl;
-       struct ieee80211_vif *vif;
-       struct wl12xx_vif *wlvif;
-
-       dwork = container_of(work, struct delayed_work, work);
-       wl = container_of(dwork, struct wl1271, connection_loss_work);
-
-       wl1271_info("Connection loss work.");
-
-       mutex_lock(&wl->mutex);
-
-       if (unlikely(wl->state != WLCORE_STATE_ON))
-               goto out;
-
-       /* Call mac80211 connection loss */
-       wl12xx_for_each_wlvif_sta(wl, wlvif) {
-               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-                       goto out;
-               vif = wl12xx_wlvif_to_vif(wlvif);
-               ieee80211_connection_loss(vif);
-       }
-out:
-       mutex_unlock(&wl->mutex);
-}
-
 static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
 {
        int i;
@@ -5117,7 +5507,7 @@ static int wl12xx_get_hw_info(struct wl1271 *wl)
 
        ret = wl12xx_set_power_on(wl);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id);
        if (ret < 0)
@@ -5207,10 +5597,9 @@ static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
        },
 };
 
-static const struct ieee80211_iface_combination
+static struct ieee80211_iface_combination
 wlcore_iface_combinations[] = {
        {
-         .num_different_channels = 1,
          .max_interfaces = 3,
          .limits = wlcore_iface_limits,
          .n_limits = ARRAY_SIZE(wlcore_iface_limits),
@@ -5219,6 +5608,7 @@ wlcore_iface_combinations[] = {
 
 static int wl1271_init_ieee80211(struct wl1271 *wl)
 {
+       int i;
        static const u32 cipher_suites[] = {
                WLAN_CIPHER_SUITE_WEP40,
                WLAN_CIPHER_SUITE_WEP104,
@@ -5249,7 +5639,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                IEEE80211_HW_AP_LINK_PS |
                IEEE80211_HW_AMPDU_AGGREGATION |
                IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
-               IEEE80211_HW_SCAN_WHILE_IDLE;
+               IEEE80211_HW_SCAN_WHILE_IDLE |
+               IEEE80211_HW_QUEUE_CONTROL;
 
        wl->hw->wiphy->cipher_suites = cipher_suites;
        wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
@@ -5271,6 +5662,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
                sizeof(struct ieee80211_header);
 
+       wl->hw->wiphy->max_remain_on_channel_duration = 5000;
+
        wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
                                WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
@@ -5278,6 +5671,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
                     ARRAY_SIZE(wl1271_channels_5ghz) >
                     WL1271_MAX_CHANNELS);
+       /*
+       * clear channel flags from the previous usage
+       * and restore max_power & max_antenna_gain values.
+       */
+       for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
+               wl1271_band_2ghz.channels[i].flags = 0;
+               wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
+               wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
+               wl1271_band_5ghz.channels[i].flags = 0;
+               wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
+               wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
+       }
+
        /*
         * We keep local copies of the band structs because we need to
         * modify them on a per-device basis.
@@ -5298,7 +5707,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
                &wl->bands[IEEE80211_BAND_5GHZ];
 
-       wl->hw->queues = 4;
+       /*
+        * allow 4 queues per mac address we support +
+        * 1 cab queue per mac + one global offchannel Tx queue
+        */
+       wl->hw->queues = (NUM_TX_QUEUES + 1) * WLCORE_NUM_MAC_ADDRESSES + 1;
+
+       /* the last queue is the offchannel queue */
+       wl->hw->offchannel_tx_hw_queue = wl->hw->queues - 1;
        wl->hw->max_rates = 1;
 
        wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
@@ -5311,6 +5727,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
 
        /* allowed interface combinations */
+       wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
        wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
        wl->hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(wlcore_iface_combinations);
@@ -5327,7 +5744,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
 
 #define WL1271_DEFAULT_CHANNEL 0
 
-struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
+struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
+                                    u32 mbox_size)
 {
        struct ieee80211_hw *hw;
        struct wl1271 *wl;
@@ -5369,9 +5787,8 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
        INIT_WORK(&wl->tx_work, wl1271_tx_work);
        INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
        INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
+       INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work);
        INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
-       INIT_DELAYED_WORK(&wl->connection_loss_work,
-                         wl1271_connection_loss_work);
 
        wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
        if (!wl->freezable_wq) {
@@ -5387,14 +5804,15 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
        wl->flags = 0;
        wl->sg_enabled = true;
        wl->sleep_auth = WL1271_PSM_ILLEGAL;
+       wl->recovery_count = 0;
        wl->hw_pg_ver = -1;
        wl->ap_ps_map = 0;
        wl->ap_fw_ps_map = 0;
        wl->quirks = 0;
        wl->platform_quirks = 0;
-       wl->sched_scanning = false;
        wl->system_hlid = WL12XX_SYSTEM_HLID;
        wl->active_sta_count = 0;
+       wl->active_link_count = 0;
        wl->fwlog_size = 0;
        init_waitqueue_head(&wl->fwlog_waitq);
 
@@ -5434,14 +5852,24 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
                goto err_dummy_packet;
        }
 
-       wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA);
+       wl->mbox_size = mbox_size;
+       wl->mbox = kmalloc(wl->mbox_size, GFP_KERNEL | GFP_DMA);
        if (!wl->mbox) {
                ret = -ENOMEM;
                goto err_fwlog;
        }
 
+       wl->buffer_32 = kmalloc(sizeof(*wl->buffer_32), GFP_KERNEL);
+       if (!wl->buffer_32) {
+               ret = -ENOMEM;
+               goto err_mbox;
+       }
+
        return hw;
 
+err_mbox:
+       kfree(wl->mbox);
+
 err_fwlog:
        free_page((unsigned long)wl->fwlog);
 
@@ -5480,6 +5908,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
 
        device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+       kfree(wl->buffer_32);
+       kfree(wl->mbox);
        free_page((unsigned long)wl->fwlog);
        dev_kfree_skb(wl->dummy_packet);
        free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
@@ -5712,10 +6142,10 @@ module_param_named(fwlog, fwlog_param, charp, 0);
 MODULE_PARM_DESC(fwlog,
                 "FW logger options: continuous, ondemand, dbgpins or disable");
 
-module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
+module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
 
-module_param(no_recovery, bool, S_IRUSR | S_IWUSR);
+module_param(no_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
 
 MODULE_LICENSE("GPL");
index 4d1414a..9b7b6e2 100644 (file)
@@ -151,9 +151,6 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
                        wl12xx_queue_recovery_work(wl);
                        ret = -ETIMEDOUT;
                        goto err;
-               } else if (ret < 0) {
-                       wl1271_error("ELP wakeup completion error.");
-                       goto err;
                }
        }
 
@@ -242,11 +239,12 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
        struct ieee80211_tx_info *info;
        unsigned long flags;
        int filtered[NUM_TX_QUEUES];
+       struct wl1271_link *lnk = &wl->links[hlid];
 
        /* filter all frames currently in the low level queues for this hlid */
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                filtered[i] = 0;
-               while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+               while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
                        filtered[i]++;
 
                        if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
@@ -260,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       for (i = 0; i < NUM_TX_QUEUES; i++)
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
                wl->tx_queue_count[i] -= filtered[i];
+               if (lnk->wlvif)
+                       lnk->wlvif->tx_queue_count[i] -= filtered[i];
+       }
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
        wl1271_handle_tx_low_watermark(wl);
index 9ee0ec6..6791a1a 100644 (file)
@@ -92,11 +92,16 @@ static void wl1271_rx_status(struct wl1271 *wl,
                status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED |
                                RX_FLAG_DECRYPTED;
 
-               if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) {
+               if (unlikely(desc_err_code & WL1271_RX_DESC_MIC_FAIL)) {
                        status->flag |= RX_FLAG_MMIC_ERROR;
-                       wl1271_warning("Michael MIC error");
+                       wl1271_warning("Michael MIC error. Desc: 0x%x",
+                                      desc_err_code);
                }
        }
+
+       if (beacon)
+               wlcore_set_pending_regdomain_ch(wl, (u16)desc->channel,
+                                               status->band);
 }
 
 static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
@@ -108,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
        u8 *buf;
        u8 beacon = 0;
        u8 is_data = 0;
-       u8 reserved = 0;
+       u8 reserved = 0, offset_to_data = 0;
        u16 seq_num;
        u32 pkt_data_len;
 
@@ -128,6 +133,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
 
        if (rx_align == WLCORE_RX_BUF_UNALIGNED)
                reserved = RX_BUF_ALIGN;
+       else if (rx_align == WLCORE_RX_BUF_PADDED)
+               offset_to_data = RX_BUF_ALIGN;
 
        /* the data read starts with the descriptor */
        desc = (struct wl1271_rx_descriptor *) data;
@@ -139,19 +146,15 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
                return 0;
        }
 
-       switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
        /* discard corrupted packets */
-       case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
-       case WL1271_RX_DESC_DECRYPT_FAIL:
-               wl1271_warning("corrupted packet in RX with status: 0x%x",
-                              desc->status & WL1271_RX_DESC_STATUS_MASK);
-               return -EINVAL;
-       case WL1271_RX_DESC_SUCCESS:
-       case WL1271_RX_DESC_MIC_FAIL:
-               break;
-       default:
-               wl1271_error("invalid RX descriptor status: 0x%x",
-                            desc->status & WL1271_RX_DESC_STATUS_MASK);
+       if (desc->status & WL1271_RX_DESC_DECRYPT_FAIL) {
+               hdr = (void *)(data + sizeof(*desc) + offset_to_data);
+               wl1271_warning("corrupted packet in RX: status: 0x%x len: %d",
+                              desc->status & WL1271_RX_DESC_STATUS_MASK,
+                              pkt_data_len);
+               wl1271_dump((DEBUG_RX|DEBUG_CMD), "PKT: ", data + sizeof(*desc),
+                           min(pkt_data_len,
+                               ieee80211_hdrlen(hdr->frame_control)));
                return -EINVAL;
        }
 
index 71eba18..3363f60 100644 (file)
  * Bits 3-5 - process_id tag (AP mode FW)
  * Bits 6-7 - reserved
  */
-#define WL1271_RX_DESC_STATUS_MASK      0x03
+#define WL1271_RX_DESC_STATUS_MASK      0x07
 
 #define WL1271_RX_DESC_SUCCESS          0x00
 #define WL1271_RX_DESC_DECRYPT_FAIL     0x01
 #define WL1271_RX_DESC_MIC_FAIL         0x02
-#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03
 
 #define RX_MEM_BLOCK_MASK            0xFF
 #define RX_BUF_SIZE_MASK             0xFFF00
index d005014..f407101 100644 (file)
@@ -35,7 +35,6 @@ void wl1271_scan_complete_work(struct work_struct *work)
 {
        struct delayed_work *dwork;
        struct wl1271 *wl;
-       struct ieee80211_vif *vif;
        struct wl12xx_vif *wlvif;
        int ret;
 
@@ -52,8 +51,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
        if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
                goto out;
 
-       vif = wl->scan_vif;
-       wlvif = wl12xx_vif_to_data(vif);
+       wlvif = wl->scan_wlvif;
 
        /*
         * Rearm the tx watchdog just before idling scan. This
@@ -64,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
        wl->scan.req = NULL;
-       wl->scan_vif = NULL;
+       wl->scan_wlvif = NULL;
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
@@ -82,6 +80,8 @@ void wl1271_scan_complete_work(struct work_struct *work)
                wl12xx_queue_recovery_work(wl);
        }
 
+       wlcore_cmd_regdomain_config_locked(wl);
+
        ieee80211_scan_completed(wl->hw, false);
 
 out:
@@ -89,371 +89,99 @@ out:
 
 }
 
-
-static int wl1271_get_scan_channels(struct wl1271 *wl,
-                                   struct cfg80211_scan_request *req,
-                                   struct basic_scan_channel_params *channels,
-                                   enum ieee80211_band band, bool passive)
-{
-       struct conf_scan_settings *c = &wl->conf.scan;
-       int i, j;
-       u32 flags;
-
-       for (i = 0, j = 0;
-            i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
-            i++) {
-               flags = req->channels[i]->flags;
-
-               if (!test_bit(i, wl->scan.scanned_ch) &&
-                   !(flags & IEEE80211_CHAN_DISABLED) &&
-                   (req->channels[i]->band == band) &&
-                   /*
-                    * In passive scans, we scan all remaining
-                    * channels, even if not marked as such.
-                    * In active scans, we only scan channels not
-                    * marked as passive.
-                    */
-                   (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
-                       wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
-                                    req->channels[i]->band,
-                                    req->channels[i]->center_freq);
-                       wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
-                                    req->channels[i]->hw_value,
-                                    req->channels[i]->flags);
-                       wl1271_debug(DEBUG_SCAN,
-                                    "max_antenna_gain %d, max_power %d",
-                                    req->channels[i]->max_antenna_gain,
-                                    req->channels[i]->max_power);
-                       wl1271_debug(DEBUG_SCAN, "beacon_found %d",
-                                    req->channels[i]->beacon_found);
-
-                       if (!passive) {
-                               channels[j].min_duration =
-                                       cpu_to_le32(c->min_dwell_time_active);
-                               channels[j].max_duration =
-                                       cpu_to_le32(c->max_dwell_time_active);
-                       } else {
-                               channels[j].min_duration =
-                                       cpu_to_le32(c->min_dwell_time_passive);
-                               channels[j].max_duration =
-                                       cpu_to_le32(c->max_dwell_time_passive);
-                       }
-                       channels[j].early_termination = 0;
-                       channels[j].tx_power_att = req->channels[i]->max_power;
-                       channels[j].channel = req->channels[i]->hw_value;
-
-                       memset(&channels[j].bssid_lsb, 0xff, 4);
-                       memset(&channels[j].bssid_msb, 0xff, 2);
-
-                       /* Mark the channels we already used */
-                       set_bit(i, wl->scan.scanned_ch);
-
-                       j++;
-               }
-       }
-
-       return j;
-}
-
-#define WL1271_NOTHING_TO_SCAN 1
-
-static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
-                           enum ieee80211_band band,
-                           bool passive, u32 basic_rate)
+static void wlcore_started_vifs_iter(void *data, u8 *mac,
+                                    struct ieee80211_vif *vif)
 {
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       struct wl1271_cmd_scan *cmd;
-       struct wl1271_cmd_trigger_scan_to *trigger;
-       int ret;
-       u16 scan_options = 0;
-
-       /* skip active scans if we don't have SSIDs */
-       if (!passive && wl->scan.req->n_ssids == 0)
-               return WL1271_NOTHING_TO_SCAN;
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
-       if (!cmd || !trigger) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       if (wl->conf.scan.split_scan_timeout)
-               scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
-
-       if (passive)
-               scan_options |= WL1271_SCAN_OPT_PASSIVE;
-
-       cmd->params.role_id = wlvif->role_id;
-
-       if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       cmd->params.scan_options = cpu_to_le16(scan_options);
-
-       cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
-                                                   cmd->channels,
-                                                   band, passive);
-       if (cmd->params.n_ch == 0) {
-               ret = WL1271_NOTHING_TO_SCAN;
-               goto out;
-       }
-
-       cmd->params.tx_rate = cpu_to_le32(basic_rate);
-       cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
-       cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
-       cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
-
-       if (band == IEEE80211_BAND_2GHZ)
-               cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
-       else
-               cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
-
-       if (wl->scan.ssid_len && wl->scan.ssid) {
-               cmd->params.ssid_len = wl->scan.ssid_len;
-               memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
-       }
-
-       memcpy(cmd->addr, vif->addr, ETH_ALEN);
-
-       ret = wl12xx_cmd_build_probe_req(wl, wlvif,
-                                        cmd->params.role_id, band,
-                                        wl->scan.ssid, wl->scan.ssid_len,
-                                        wl->scan.req->ie,
-                                        wl->scan.req->ie_len, false);
-       if (ret < 0) {
-               wl1271_error("PROBE request template failed");
-               goto out;
-       }
-
-       trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
-       ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
-                             sizeof(*trigger), 0);
-       if (ret < 0) {
-               wl1271_error("trigger scan to failed for hw scan");
-               goto out;
-       }
-
-       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+       int *count = (int *)data;
 
-       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
-       if (ret < 0) {
-               wl1271_error("SCAN failed");
-               goto out;
-       }
-
-out:
-       kfree(cmd);
-       kfree(trigger);
-       return ret;
+       if (!vif->bss_conf.idle)
+               (*count)++;
 }
 
-void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif)
+static int wlcore_count_started_vifs(struct wl1271 *wl)
 {
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       int ret = 0;
-       enum ieee80211_band band;
-       u32 rate, mask;
-
-       switch (wl->scan.state) {
-       case WL1271_SCAN_STATE_IDLE:
-               break;
-
-       case WL1271_SCAN_STATE_2GHZ_ACTIVE:
-               band = IEEE80211_BAND_2GHZ;
-               mask = wlvif->bitrate_masks[band];
-               if (wl->scan.req->no_cck) {
-                       mask &= ~CONF_TX_CCK_RATES;
-                       if (!mask)
-                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
-               }
-               rate = wl1271_tx_min_rate_get(wl, mask);
-               ret = wl1271_scan_send(wl, vif, band, false, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_2GHZ_PASSIVE:
-               band = IEEE80211_BAND_2GHZ;
-               mask = wlvif->bitrate_masks[band];
-               if (wl->scan.req->no_cck) {
-                       mask &= ~CONF_TX_CCK_RATES;
-                       if (!mask)
-                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
-               }
-               rate = wl1271_tx_min_rate_get(wl, mask);
-               ret = wl1271_scan_send(wl, vif, band, true, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       if (wl->enable_11a)
-                               wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
-                       else
-                               wl->scan.state = WL1271_SCAN_STATE_DONE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
+       int count = 0;
 
-       case WL1271_SCAN_STATE_5GHZ_ACTIVE:
-               band = IEEE80211_BAND_5GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, vif, band, false, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_5GHZ_PASSIVE:
-               band = IEEE80211_BAND_5GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, vif, band, true, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_DONE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_DONE:
-               wl->scan.failed = false;
-               cancel_delayed_work(&wl->scan_complete_work);
-               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                            msecs_to_jiffies(0));
-               break;
-
-       default:
-               wl1271_error("invalid scan state");
-               break;
-       }
-
-       if (ret < 0) {
-               cancel_delayed_work(&wl->scan_complete_work);
-               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                            msecs_to_jiffies(0));
-       }
-}
-
-int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
-               const u8 *ssid, size_t ssid_len,
-               struct cfg80211_scan_request *req)
-{
-       /*
-        * cfg80211 should guarantee that we don't get more channels
-        * than what we have registered.
-        */
-       BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
-
-       if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
-               return -EBUSY;
-
-       wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
-
-       if (ssid_len && ssid) {
-               wl->scan.ssid_len = ssid_len;
-               memcpy(wl->scan.ssid, ssid, ssid_len);
-       } else {
-               wl->scan.ssid_len = 0;
-       }
-
-       wl->scan_vif = vif;
-       wl->scan.req = req;
-       memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-
-       /* we assume failure so that timeout scenarios are handled correctly */
-       wl->scan.failed = true;
-       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                    msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
-
-       wl1271_scan_stm(wl, vif);
-
-       return 0;
-}
-
-int wl1271_scan_stop(struct wl1271 *wl)
-{
-       struct wl1271_cmd_header *cmd = NULL;
-       int ret = 0;
-
-       if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
-               return -EINVAL;
-
-       wl1271_debug(DEBUG_CMD, "cmd scan stop");
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       if (!cmd) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
-                             sizeof(*cmd), 0);
-       if (ret < 0) {
-               wl1271_error("cmd stop_scan failed");
-               goto out;
-       }
-out:
-       kfree(cmd);
-       return ret;
+       ieee80211_iterate_active_interfaces_atomic(wl->hw,
+                                       IEEE80211_IFACE_ITER_RESUME_ALL,
+                                       wlcore_started_vifs_iter, &count);
+       return count;
 }
 
 static int
-wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
-                                   struct cfg80211_sched_scan_request *req,
-                                   struct conn_scan_ch_params *channels,
-                                   u32 band, bool radar, bool passive,
-                                   int start, int max_channels,
-                                   u8 *n_pactive_ch)
+wlcore_scan_get_channels(struct wl1271 *wl,
+                        struct ieee80211_channel *req_channels[],
+                        u32 n_channels,
+                        u32 n_ssids,
+                        struct conn_scan_ch_params *channels,
+                        u32 band, bool radar, bool passive,
+                        int start, int max_channels,
+                        u8 *n_pactive_ch,
+                        int scan_type)
 {
-       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
        int i, j;
        u32 flags;
-       bool force_passive = !req->n_ssids;
-       u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe;
+       bool force_passive = !n_ssids;
+       u32 min_dwell_time_active, max_dwell_time_active;
        u32 dwell_time_passive, dwell_time_dfs;
 
-       if (band == IEEE80211_BAND_5GHZ)
-               delta_per_probe = c->dwell_time_delta_per_probe_5;
-       else
-               delta_per_probe = c->dwell_time_delta_per_probe;
+       /* configure dwell times according to scan type */
+       if (scan_type == SCAN_TYPE_SEARCH) {
+               struct conf_scan_settings *c = &wl->conf.scan;
+               bool active_vif_exists = !!wlcore_count_started_vifs(wl);
+
+               min_dwell_time_active = active_vif_exists ?
+                       c->min_dwell_time_active :
+                       c->min_dwell_time_active_long;
+               max_dwell_time_active = active_vif_exists ?
+                       c->max_dwell_time_active :
+                       c->max_dwell_time_active_long;
+               dwell_time_passive = c->dwell_time_passive;
+               dwell_time_dfs = c->dwell_time_dfs;
+       } else {
+               struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+               u32 delta_per_probe;
 
-       min_dwell_time_active = c->base_dwell_time +
-                req->n_ssids * c->num_probe_reqs * delta_per_probe;
+               if (band == IEEE80211_BAND_5GHZ)
+                       delta_per_probe = c->dwell_time_delta_per_probe_5;
+               else
+                       delta_per_probe = c->dwell_time_delta_per_probe;
 
-       max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta;
+               min_dwell_time_active = c->base_dwell_time +
+                        n_ssids * c->num_probe_reqs * delta_per_probe;
 
+               max_dwell_time_active = min_dwell_time_active +
+                                       c->max_dwell_time_delta;
+               dwell_time_passive = c->dwell_time_passive;
+               dwell_time_dfs = c->dwell_time_dfs;
+       }
        min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000);
        max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000);
-       dwell_time_passive = DIV_ROUND_UP(c->dwell_time_passive, 1000);
-       dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000);
+       dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000);
+       dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000);
 
        for (i = 0, j = start;
-            i < req->n_channels && j < max_channels;
+            i < n_channels && j < max_channels;
             i++) {
-               flags = req->channels[i]->flags;
+               flags = req_channels[i]->flags;
 
                if (force_passive)
                        flags |= IEEE80211_CHAN_PASSIVE_SCAN;
 
-               if ((req->channels[i]->band == band) &&
+               if ((req_channels[i]->band == band) &&
                    !(flags & IEEE80211_CHAN_DISABLED) &&
                    (!!(flags & IEEE80211_CHAN_RADAR) == radar) &&
                    /* if radar is set, we ignore the passive flag */
                    (radar ||
                     !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) {
                        wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
-                                    req->channels[i]->band,
-                                    req->channels[i]->center_freq);
+                                    req_channels[i]->band,
+                                    req_channels[i]->center_freq);
                        wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
-                                    req->channels[i]->hw_value,
-                                    req->channels[i]->flags);
+                                    req_channels[i]->hw_value,
+                                    req_channels[i]->flags);
                        wl1271_debug(DEBUG_SCAN, "max_power %d",
-                                    req->channels[i]->max_power);
+                                    req_channels[i]->max_power);
                        wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d",
                                     min_dwell_time_active,
                                     max_dwell_time_active);
@@ -473,10 +201,11 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
                        channels[j].max_duration =
                                cpu_to_le16(max_dwell_time_active);
 
-                       channels[j].tx_power_att = req->channels[i]->max_power;
-                       channels[j].channel = req->channels[i]->hw_value;
+                       channels[j].tx_power_att = req_channels[i]->max_power;
+                       channels[j].channel = req_channels[i]->hw_value;
 
-                       if ((band == IEEE80211_BAND_2GHZ) &&
+                       if (n_pactive_ch &&
+                           (band == IEEE80211_BAND_2GHZ) &&
                            (channels[j].channel >= 12) &&
                            (channels[j].channel <= 14) &&
                            (flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
@@ -500,51 +229,80 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
        return j - start;
 }
 
-static bool
-wl1271_scan_sched_scan_channels(struct wl1271 *wl,
-                               struct cfg80211_sched_scan_request *req,
-                               struct wl1271_cmd_sched_scan_config *cfg)
+bool
+wlcore_set_scan_chan_params(struct wl1271 *wl,
+                           struct wlcore_scan_channels *cfg,
+                           struct ieee80211_channel *channels[],
+                           u32 n_channels,
+                           u32 n_ssids,
+                           int scan_type)
 {
        u8 n_pactive_ch = 0;
 
        cfg->passive[0] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
-                                                   IEEE80211_BAND_2GHZ,
-                                                   false, true, 0,
-                                                   MAX_CHANNELS_2GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_2,
+                                        IEEE80211_BAND_2GHZ,
+                                        false, true, 0,
+                                        MAX_CHANNELS_2GHZ,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->active[0] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
-                                                   IEEE80211_BAND_2GHZ,
-                                                   false, false,
-                                                   cfg->passive[0],
-                                                   MAX_CHANNELS_2GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_2,
+                                        IEEE80211_BAND_2GHZ,
+                                        false, false,
+                                        cfg->passive[0],
+                                        MAX_CHANNELS_2GHZ,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->passive[1] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   false, true, 0,
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        false, true, 0,
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->dfs =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   true, true,
-                                                   cfg->passive[1],
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        true, true,
+                                        cfg->passive[1],
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->active[1] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   false, false,
-                                                   cfg->passive[1] + cfg->dfs,
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        false, false,
+                                        cfg->passive[1] + cfg->dfs,
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
+
        /* 802.11j channels are not supported yet */
        cfg->passive[2] = 0;
        cfg->active[2] = 0;
 
-       cfg->n_pactive_ch = n_pactive_ch;
+       cfg->passive_active = n_pactive_ch;
 
        wl1271_debug(DEBUG_SCAN, "    2.4GHz: active %d passive %d",
                     cfg->active[0], cfg->passive[0]);
@@ -556,10 +314,48 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
                cfg->passive[1] || cfg->active[1] || cfg->dfs ||
                cfg->passive[2] || cfg->active[2];
 }
+EXPORT_SYMBOL_GPL(wlcore_set_scan_chan_params);
+
+int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
+               const u8 *ssid, size_t ssid_len,
+               struct cfg80211_scan_request *req)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+
+       /*
+        * cfg80211 should guarantee that we don't get more channels
+        * than what we have registered.
+        */
+       BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
+
+       if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
+               return -EBUSY;
+
+       wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
+
+       if (ssid_len && ssid) {
+               wl->scan.ssid_len = ssid_len;
+               memcpy(wl->scan.ssid, ssid, ssid_len);
+       } else {
+               wl->scan.ssid_len = 0;
+       }
+
+       wl->scan_wlvif = wlvif;
+       wl->scan.req = req;
+       memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+
+       /* we assume failure so that timeout scenarios are handled correctly */
+       wl->scan.failed = true;
+       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                    msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
 
+       wl->ops->scan_start(wl, wlvif, req);
+
+       return 0;
+}
 /* Returns the scan type to be used or a negative value on error */
-static int
-wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
+int
+wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
                                 struct wl12xx_vif *wlvif,
                                 struct cfg80211_sched_scan_request *req)
 {
@@ -662,160 +458,12 @@ out:
                return ret;
        return type;
 }
+EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list);
 
-int wl1271_scan_sched_scan_config(struct wl1271 *wl,
-                                 struct wl12xx_vif *wlvif,
-                                 struct cfg80211_sched_scan_request *req,
-                                 struct ieee80211_sched_scan_ies *ies)
-{
-       struct wl1271_cmd_sched_scan_config *cfg = NULL;
-       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
-       int i, ret;
-       bool force_passive = !req->n_ssids;
-
-       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
-
-       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-       if (!cfg)
-               return -ENOMEM;
-
-       cfg->role_id = wlvif->role_id;
-       cfg->rssi_threshold = c->rssi_threshold;
-       cfg->snr_threshold  = c->snr_threshold;
-       cfg->n_probe_reqs = c->num_probe_reqs;
-       /* cycles set to 0 it means infinite (until manually stopped) */
-       cfg->cycles = 0;
-       /* report APs when at least 1 is found */
-       cfg->report_after = 1;
-       /* don't stop scanning automatically when something is found */
-       cfg->terminate = 0;
-       cfg->tag = WL1271_SCAN_DEFAULT_TAG;
-       /* don't filter on BSS type */
-       cfg->bss_type = SCAN_BSS_TYPE_ANY;
-       /* currently NL80211 supports only a single interval */
-       for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
-               cfg->intervals[i] = cpu_to_le32(req->interval);
-
-       cfg->ssid_len = 0;
-       ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req);
-       if (ret < 0)
-               goto out;
-
-       cfg->filter_type = ret;
-
-       wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
-
-       if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
-               wl1271_error("scan channel list is empty");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (!force_passive && cfg->active[0]) {
-               u8 band = IEEE80211_BAND_2GHZ;
-               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
-                                                wlvif->role_id, band,
-                                                req->ssids[0].ssid,
-                                                req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
-               if (ret < 0) {
-                       wl1271_error("2.4GHz PROBE request template failed");
-                       goto out;
-               }
-       }
-
-       if (!force_passive && cfg->active[1]) {
-               u8 band = IEEE80211_BAND_5GHZ;
-               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
-                                                wlvif->role_id, band,
-                                                req->ssids[0].ssid,
-                                                req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
-               if (ret < 0) {
-                       wl1271_error("5GHz PROBE request template failed");
-                       goto out;
-               }
-       }
-
-       wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
-
-       ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
-                             sizeof(*cfg), 0);
-       if (ret < 0) {
-               wl1271_error("SCAN configuration failed");
-               goto out;
-       }
-out:
-       kfree(cfg);
-       return ret;
-}
-
-int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
-{
-       struct wl1271_cmd_sched_scan_start *start;
-       int ret = 0;
-
-       wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
-
-       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
-               return -EOPNOTSUPP;
-
-       if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
-           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
-               return -EBUSY;
-
-       start = kzalloc(sizeof(*start), GFP_KERNEL);
-       if (!start)
-               return -ENOMEM;
-
-       start->role_id = wlvif->role_id;
-       start->tag = WL1271_SCAN_DEFAULT_TAG;
-
-       ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
-                             sizeof(*start), 0);
-       if (ret < 0) {
-               wl1271_error("failed to send scan start command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(start);
-       return ret;
-}
-
-void wl1271_scan_sched_scan_results(struct wl1271 *wl)
+void wlcore_scan_sched_scan_results(struct wl1271 *wl)
 {
        wl1271_debug(DEBUG_SCAN, "got periodic scan results");
 
        ieee80211_sched_scan_results(wl->hw);
 }
-
-void wl1271_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif)
-{
-       struct wl1271_cmd_sched_scan_stop *stop;
-       int ret = 0;
-
-       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
-
-       /* FIXME: what to do if alloc'ing to stop fails? */
-       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
-       if (!stop) {
-               wl1271_error("failed to alloc memory to send sched scan stop");
-               return;
-       }
-
-       stop->role_id = wlvif->role_id;
-       stop->tag = WL1271_SCAN_DEFAULT_TAG;
-
-       ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
-                             sizeof(*stop), 0);
-       if (ret < 0) {
-               wl1271_error("failed to send sched scan stop command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(stop);
-}
+EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_results);
index 29f3c8d..a6ab24b 100644 (file)
 
 #include "wlcore.h"
 
-int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
+int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
                const u8 *ssid, size_t ssid_len,
                struct cfg80211_scan_request *req);
-int wl1271_scan_stop(struct wl1271 *wl);
 int wl1271_scan_build_probe_req(struct wl1271 *wl,
                                const u8 *ssid, size_t ssid_len,
                                const u8 *ie, size_t ie_len, u8 band);
-void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif);
+void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl1271_scan_complete_work(struct work_struct *work);
 int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                     struct wl12xx_vif *wlvif,
                                     struct cfg80211_sched_scan_request *req,
                                     struct ieee80211_sched_scan_ies *ies);
 int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-void wl1271_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
-void wl1271_scan_sched_scan_results(struct wl1271 *wl);
+void wlcore_scan_sched_scan_results(struct wl1271 *wl);
 
 #define WL1271_SCAN_MAX_CHANNELS       24
 #define WL1271_SCAN_DEFAULT_TAG        1
@@ -66,56 +64,6 @@ enum {
        WL1271_SCAN_STATE_DONE
 };
 
-struct basic_scan_params {
-       /* Scan option flags (WL1271_SCAN_OPT_*) */
-       __le16 scan_options;
-       u8 role_id;
-       /* Number of scan channels in the list (maximum 30) */
-       u8 n_ch;
-       /* This field indicates the number of probe requests to send
-          per channel for an active scan */
-       u8 n_probe_reqs;
-       u8 tid_trigger;
-       u8 ssid_len;
-       u8 use_ssid_list;
-
-       /* Rate bit field for sending the probes */
-       __le32 tx_rate;
-
-       u8 ssid[IEEE80211_MAX_SSID_LEN];
-       /* Band to scan */
-       u8 band;
-
-       u8 scan_tag;
-       u8 padding2[2];
-} __packed;
-
-struct basic_scan_channel_params {
-       /* Duration in TU to wait for frames on a channel for active scan */
-       __le32 min_duration;
-       __le32 max_duration;
-       __le32 bssid_lsb;
-       __le16 bssid_msb;
-       u8 early_termination;
-       u8 tx_power_att;
-       u8 channel;
-       /* FW internal use only! */
-       u8 dfs_candidate;
-       u8 activity_detected;
-       u8 pad;
-} __packed;
-
-struct wl1271_cmd_scan {
-       struct wl1271_cmd_header header;
-
-       struct basic_scan_params params;
-       struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
-
-       /* src mac address */
-       u8 addr[ETH_ALEN];
-       u8 padding[2];
-} __packed;
-
 struct wl1271_cmd_trigger_scan_to {
        struct wl1271_cmd_header header;
 
@@ -123,9 +71,17 @@ struct wl1271_cmd_trigger_scan_to {
 } __packed;
 
 #define MAX_CHANNELS_2GHZ      14
-#define MAX_CHANNELS_5GHZ      23
 #define MAX_CHANNELS_4GHZ      4
 
+/*
+ * This max value here is used only for the struct definition of
+ * wlcore_scan_channels. This struct is used by both 12xx
+ * and 18xx (which have different max 5ghz channels value).
+ * In order to make sure this is large enough, just use the
+ * max possible 5ghz channels.
+ */
+#define MAX_CHANNELS_5GHZ      42
+
 #define SCAN_MAX_CYCLE_INTERVALS 16
 #define SCAN_MAX_BANDS 3
 
@@ -160,43 +116,6 @@ struct conn_scan_ch_params {
        u8  padding[3];
 } __packed;
 
-struct wl1271_cmd_sched_scan_config {
-       struct wl1271_cmd_header header;
-
-       __le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
-
-       s8 rssi_threshold; /* for filtering (in dBm) */
-       s8 snr_threshold;  /* for filtering (in dB) */
-
-       u8 cycles;       /* maximum number of scan cycles */
-       u8 report_after; /* report when this number of results are received */
-       u8 terminate;    /* stop scanning after reporting */
-
-       u8 tag;
-       u8 bss_type; /* for filtering */
-       u8 filter_type;
-
-       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
-       u8 ssid[IEEE80211_MAX_SSID_LEN];
-
-       u8 n_probe_reqs; /* Number of probes requests per channel */
-
-       u8 passive[SCAN_MAX_BANDS];
-       u8 active[SCAN_MAX_BANDS];
-
-       u8 dfs;
-
-       u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
-                           channels in BG band */
-       u8 role_id;
-       u8 padding[1];
-
-       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
-       struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
-       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
-} __packed;
-
-
 #define SCHED_SCAN_MAX_SSIDS 16
 
 enum {
@@ -220,21 +139,34 @@ struct wl1271_cmd_sched_scan_ssid_list {
        u8 padding[2];
 } __packed;
 
-struct wl1271_cmd_sched_scan_start {
-       struct wl1271_cmd_header header;
+struct wlcore_scan_channels {
+       u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
+       u8 active[SCAN_MAX_BANDS];  /* number of active scan channels */
+       u8 dfs;            /* number of dfs channels in 5ghz */
+       u8 passive_active; /* number of passive before active channels 2.4ghz */
 
-       u8 tag;
-       u8 role_id;
-       u8 padding[2];
-} __packed;
-
-struct wl1271_cmd_sched_scan_stop {
-       struct wl1271_cmd_header header;
+       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+       struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
+       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+};
 
-       u8 tag;
-       u8 role_id;
-       u8 padding[2];
-} __packed;
+enum {
+       SCAN_TYPE_SEARCH        = 0,
+       SCAN_TYPE_PERIODIC      = 1,
+       SCAN_TYPE_TRACKING      = 2,
+};
 
+bool
+wlcore_set_scan_chan_params(struct wl1271 *wl,
+                           struct wlcore_scan_channels *cfg,
+                           struct ieee80211_channel *channels[],
+                           u32 n_channels,
+                           u32 n_ssids,
+                           int scan_type);
+
+int
+wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
+                                struct cfg80211_sched_scan_request *req);
 
 #endif /* __WL1271_SCAN_H__ */
index a3a20be..75622f6 100644 (file)
@@ -324,8 +324,7 @@ static void wl1271_remove(struct sdio_func *func)
        /* Undo decrement done above in wl1271_probe */
        pm_runtime_get_noresume(&func->dev);
 
-       platform_device_del(glue->core);
-       platform_device_put(glue->core);
+       platform_device_unregister(glue->core);
        kfree(glue);
 }
 
index 18cadc0..53790d1 100644 (file)
@@ -264,7 +264,7 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
                                             void *buf, size_t len, bool fixed)
 {
        struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-       struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
+       struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)];
        struct spi_message m;
        u32 commands[WSPI_MAX_NUM_OF_CHUNKS];
        u32 *cmd;
@@ -399,8 +399,7 @@ static int wl1271_remove(struct spi_device *spi)
 {
        struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
 
-       platform_device_del(glue->core);
-       platform_device_put(glue->core);
+       platform_device_unregister(glue->core);
        kfree(glue);
 
        return 0;
index a90d3cd..ece392c 100644 (file)
@@ -104,7 +104,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
                                    struct wl12xx_vif *wlvif,
                                    u8 hlid)
 {
-       bool fw_ps, single_sta;
+       bool fw_ps, single_link;
        u8 tx_pkts;
 
        if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
@@ -112,15 +112,15 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
        tx_pkts = wl->links[hlid].allocated_pkts;
-       single_sta = (wl->active_sta_count == 1);
+       single_link = (wl->active_link_count == 1);
 
        /*
         * if in FW PS and there is enough data in FW we can put the link
         * into high-level PS and clean out its TX queues.
-        * Make an exception if this is the only connected station. In this
-        * case FW-memory congestion is not a problem.
+        * Make an exception if this is the only connected link. In this
+        * case FW-memory congestion is less of a problem.
         */
-       if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+       if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
@@ -155,21 +155,18 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                      struct sk_buff *skb, struct ieee80211_sta *sta)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
-       if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
-               return wl->system_hlid;
+       struct ieee80211_tx_info *control;
 
        if (wlvif->bss_type == BSS_TYPE_AP_BSS)
                return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta);
 
-       if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
-            test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
-           !ieee80211_is_auth(hdr->frame_control) &&
-           !ieee80211_is_assoc_req(hdr->frame_control))
-               return wlvif->sta.hlid;
-       else
+       control = IEEE80211_SKB_CB(skb);
+       if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+               wl1271_debug(DEBUG_TX, "tx offchannel");
                return wlvif->dev_hlid;
+       }
+
+       return wlvif->sta.hlid;
 }
 
 unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
@@ -224,9 +221,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                wl->tx_allocated_pkts[ac]++;
 
-               if (!wl12xx_is_dummy_packet(wl, skb) && wlvif &&
-                   wlvif->bss_type == BSS_TYPE_AP_BSS &&
-                   test_bit(hlid, wlvif->ap.sta_hlid_map))
+               if (test_bit(hlid, wl->links_map))
                        wl->links[hlid].allocated_pkts++;
 
                ret = 0;
@@ -293,9 +288,14 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
                tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
        } else if (wlvif) {
+               u8 session_id = wl->session_ids[hlid];
+
+               if ((wl->quirks & WLCORE_QUIRK_AP_ZERO_SESSION_ID) &&
+                   (wlvif->bss_type == BSS_TYPE_AP_BSS))
+                       session_id = 0;
+
                /* configure the tx attributes */
-               tx_attr = wlvif->session_counter <<
-                         TX_HW_ATTR_OFST_SESSION_COUNTER;
+               tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER;
        }
 
        desc->hlid = hlid;
@@ -452,20 +452,22 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
 void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
 {
        int i;
+       struct wl12xx_vif *wlvif;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++) {
-               if (wlcore_is_queue_stopped_by_reason(wl, i,
-                       WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
-                   wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) {
-                       /* firmware buffer has space, restart queues */
-                       wlcore_wake_queue(wl, i,
-                                         WLCORE_QUEUE_STOP_REASON_WATERMARK);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               for (i = 0; i < NUM_TX_QUEUES; i++) {
+                       if (wlcore_is_queue_stopped_by_reason(wl, wlvif, i,
+                                       WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
+                           wlvif->tx_queue_count[i] <=
+                                       WL1271_TX_QUEUE_LOW_WATERMARK)
+                               /* firmware buffer has space, restart queues */
+                               wlcore_wake_queue(wl, wlvif, i,
+                                       WLCORE_QUEUE_STOP_REASON_WATERMARK);
                }
        }
 }
 
-static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
-                                               struct sk_buff_head *queues)
+static int wlcore_select_ac(struct wl1271 *wl)
 {
        int i, q = -1, ac;
        u32 min_pkts = 0xffffffff;
@@ -479,45 +481,60 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
         */
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                ac = wl1271_tx_get_queue(i);
-               if (!skb_queue_empty(&queues[ac]) &&
-                   (wl->tx_allocated_pkts[ac] < min_pkts)) {
+               if (wl->tx_queue_count[ac] &&
+                   wl->tx_allocated_pkts[ac] < min_pkts) {
                        q = ac;
                        min_pkts = wl->tx_allocated_pkts[q];
                }
        }
 
-       if (q == -1)
-               return NULL;
-
-       return &queues[q];
+       return q;
 }
 
-static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
-                                             struct wl1271_link *lnk)
+static struct sk_buff *wlcore_lnk_dequeue(struct wl1271 *wl,
+                                         struct wl1271_link *lnk, u8 q)
 {
        struct sk_buff *skb;
        unsigned long flags;
-       struct sk_buff_head *queue;
 
-       queue = wl1271_select_queue(wl, lnk->tx_queue);
-       if (!queue)
-               return NULL;
-
-       skb = skb_dequeue(queue);
+       skb = skb_dequeue(&lnk->tx_queue[q]);
        if (skb) {
-               int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                spin_lock_irqsave(&wl->wl_lock, flags);
                WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
                wl->tx_queue_count[q]--;
+               if (lnk->wlvif) {
+                       WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0);
+                       lnk->wlvif->tx_queue_count[q]--;
+               }
                spin_unlock_irqrestore(&wl->wl_lock, flags);
        }
 
        return skb;
 }
 
-static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
-                                             struct wl12xx_vif *wlvif,
-                                             u8 *hlid)
+static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct wl1271 *wl,
+                                                   u8 hlid, u8 ac,
+                                                   u8 *low_prio_hlid)
+{
+       struct wl1271_link *lnk = &wl->links[hlid];
+
+       if (!wlcore_hw_lnk_high_prio(wl, hlid, lnk)) {
+               if (*low_prio_hlid == WL12XX_INVALID_LINK_ID &&
+                   !skb_queue_empty(&lnk->tx_queue[ac]) &&
+                   wlcore_hw_lnk_low_prio(wl, hlid, lnk))
+                       /* we found the first non-empty low priority queue */
+                       *low_prio_hlid = hlid;
+
+               return NULL;
+       }
+
+       return wlcore_lnk_dequeue(wl, lnk, ac);
+}
+
+static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl,
+                                                   struct wl12xx_vif *wlvif,
+                                                   u8 ac, u8 *hlid,
+                                                   u8 *low_prio_hlid)
 {
        struct sk_buff *skb = NULL;
        int i, h, start_hlid;
@@ -533,7 +550,8 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
                if (!test_bit(h, wlvif->links_map))
                        continue;
 
-               skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]);
+               skb = wlcore_lnk_dequeue_high_prio(wl, h, ac,
+                                                  low_prio_hlid);
                if (!skb)
                        continue;
 
@@ -553,42 +571,74 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
        unsigned long flags;
        struct wl12xx_vif *wlvif = wl->last_wlvif;
        struct sk_buff *skb = NULL;
+       int ac;
+       u8 low_prio_hlid = WL12XX_INVALID_LINK_ID;
+
+       ac = wlcore_select_ac(wl);
+       if (ac < 0)
+               goto out;
 
        /* continue from last wlvif (round robin) */
        if (wlvif) {
                wl12xx_for_each_wlvif_continue(wl, wlvif) {
-                       skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
-                       if (skb) {
-                               wl->last_wlvif = wlvif;
-                               break;
-                       }
+                       if (!wlvif->tx_queue_count[ac])
+                               continue;
+
+                       skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+                                                          &low_prio_hlid);
+                       if (!skb)
+                               continue;
+
+                       wl->last_wlvif = wlvif;
+                       break;
                }
        }
 
        /* dequeue from the system HLID before the restarting wlvif list */
        if (!skb) {
-               skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
-               *hlid = wl->system_hlid;
+               skb = wlcore_lnk_dequeue_high_prio(wl, wl->system_hlid,
+                                                  ac, &low_prio_hlid);
+               if (skb) {
+                       *hlid = wl->system_hlid;
+                       wl->last_wlvif = NULL;
+               }
        }
 
-       /* do a new pass over the wlvif list */
+       /* Do a new pass over the wlvif list. But no need to continue
+        * after last_wlvif. The previous pass should have found it. */
        if (!skb) {
                wl12xx_for_each_wlvif(wl, wlvif) {
-                       skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
+                       if (!wlvif->tx_queue_count[ac])
+                               goto next;
+
+                       skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+                                                          &low_prio_hlid);
                        if (skb) {
                                wl->last_wlvif = wlvif;
                                break;
                        }
 
-                       /*
-                        * No need to continue after last_wlvif. The previous
-                        * pass should have found it.
-                        */
+next:
                        if (wlvif == wl->last_wlvif)
                                break;
                }
        }
 
+       /* no high priority skbs found - but maybe a low priority one? */
+       if (!skb && low_prio_hlid != WL12XX_INVALID_LINK_ID) {
+               struct wl1271_link *lnk = &wl->links[low_prio_hlid];
+               skb = wlcore_lnk_dequeue(wl, lnk, ac);
+
+               WARN_ON(!skb); /* we checked this before */
+               *hlid = low_prio_hlid;
+
+               /* ensure proper round robin in the vif/link levels */
+               wl->last_wlvif = lnk->wlvif;
+               if (lnk->wlvif)
+                       lnk->wlvif->last_tx_hlid = low_prio_hlid;
+
+       }
+
        if (!skb &&
            test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
                int q;
@@ -602,6 +652,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
                spin_unlock_irqrestore(&wl->wl_lock, flags);
        }
 
+out:
        return skb;
 }
 
@@ -623,6 +674,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        spin_lock_irqsave(&wl->wl_lock, flags);
        wl->tx_queue_count[q]++;
+       if (wlvif)
+               wlvif->tx_queue_count[q]++;
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
@@ -699,7 +752,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
                bool has_data = false;
 
                wlvif = NULL;
-               if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
+               if (!wl12xx_is_dummy_packet(wl, skb))
                        wlvif = wl12xx_vif_to_data(info->control.vif);
                else
                        hlid = wl->system_hlid;
@@ -972,10 +1025,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
        unsigned long flags;
        struct ieee80211_tx_info *info;
        int total[NUM_TX_QUEUES];
+       struct wl1271_link *lnk = &wl->links[hlid];
 
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                total[i] = 0;
-               while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+               while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
                        wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb);
 
                        if (!wl12xx_is_dummy_packet(wl, skb)) {
@@ -990,8 +1044,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       for (i = 0; i < NUM_TX_QUEUES; i++)
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
                wl->tx_queue_count[i] -= total[i];
+               if (lnk->wlvif)
+                       lnk->wlvif->tx_queue_count[i] -= total[i];
+       }
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
        wl1271_handle_tx_low_watermark(wl);
@@ -1004,16 +1061,18 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 
        /* TX failure */
        for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
-               if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
+                       /* this calls wl12xx_free_link */
                        wl1271_free_sta(wl, wlvif, i);
-               else
-                       wlvif->sta.ba_rx_bitmap = 0;
-
-               wl->links[i].allocated_pkts = 0;
-               wl->links[i].prev_freed_pkts = 0;
+               } else {
+                       u8 hlid = i;
+                       wl12xx_free_link(wl, wlvif, &hlid);
+               }
        }
        wlvif->last_tx_hlid = 0;
 
+       for (i = 0; i < NUM_TX_QUEUES; i++)
+               wlvif->tx_queue_count[i] = 0;
 }
 /* caller must hold wl->mutex and TX must be stopped */
 void wl12xx_tx_reset(struct wl1271 *wl)
@@ -1023,7 +1082,7 @@ void wl12xx_tx_reset(struct wl1271 *wl)
        struct ieee80211_tx_info *info;
 
        /* only reset the queues if something bad happened */
-       if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) {
+       if (wl1271_tx_total_queue_count(wl) != 0) {
                for (i = 0; i < WL12XX_MAX_LINKS; i++)
                        wl1271_tx_reset_link_queues(wl, i);
 
@@ -1135,45 +1194,48 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
 
        return BIT(__ffs(rate_set));
 }
+EXPORT_SYMBOL_GPL(wl1271_tx_min_rate_get);
 
-void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
-                             enum wlcore_queue_stop_reason reason)
+void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 queue, enum wlcore_queue_stop_reason reason)
 {
-       bool stopped = !!wl->queue_stop_reasons[queue];
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+       bool stopped = !!wl->queue_stop_reasons[hwq];
 
        /* queue should not be stopped for this reason */
-       WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue]));
+       WARN_ON_ONCE(test_and_set_bit(reason, &wl->queue_stop_reasons[hwq]));
 
        if (stopped)
                return;
 
-       ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+       ieee80211_stop_queue(wl->hw, hwq);
 }
 
-void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       wlcore_stop_queue_locked(wl, queue, reason);
+       wlcore_stop_queue_locked(wl, wlvif, queue, reason);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
-void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason)
 {
        unsigned long flags;
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
 
        spin_lock_irqsave(&wl->wl_lock, flags);
 
        /* queue should not be clear for this reason */
-       WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue]));
+       WARN_ON_ONCE(!test_and_clear_bit(reason, &wl->queue_stop_reasons[hwq]));
 
-       if (wl->queue_stop_reasons[queue])
+       if (wl->queue_stop_reasons[hwq])
                goto out;
 
-       ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+       ieee80211_wake_queue(wl->hw, hwq);
 
 out:
        spin_unlock_irqrestore(&wl->wl_lock, flags);
@@ -1183,48 +1245,74 @@ void wlcore_stop_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason)
 {
        int i;
+       unsigned long flags;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               wlcore_stop_queue(wl, i, reason);
+       spin_lock_irqsave(&wl->wl_lock, flags);
+
+       /* mark all possible queues as stopped */
+        for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
+                WARN_ON_ONCE(test_and_set_bit(reason,
+                                             &wl->queue_stop_reasons[i]));
+
+       /* use the global version to make sure all vifs in mac80211 we don't
+        * know are stopped.
+        */
+       ieee80211_stop_queues(wl->hw);
+
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
-EXPORT_SYMBOL_GPL(wlcore_stop_queues);
 
 void wlcore_wake_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason)
 {
        int i;
+       unsigned long flags;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               wlcore_wake_queue(wl, i, reason);
+       spin_lock_irqsave(&wl->wl_lock, flags);
+
+       /* mark all possible queues as awake */
+        for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
+               WARN_ON_ONCE(!test_and_clear_bit(reason,
+                                                &wl->queue_stop_reasons[i]));
+
+       /* use the global version to make sure all vifs in mac80211 we don't
+        * know are woken up.
+        */
+       ieee80211_wake_queues(wl->hw);
+
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
-EXPORT_SYMBOL_GPL(wlcore_wake_queues);
 
-void wlcore_reset_stopped_queues(struct wl1271 *wl)
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
+                                      enum wlcore_queue_stop_reason reason)
 {
-       int i;
        unsigned long flags;
+       bool stopped;
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-
-       for (i = 0; i < NUM_TX_QUEUES; i++) {
-               if (!wl->queue_stop_reasons[i])
-                       continue;
-
-               wl->queue_stop_reasons[i] = 0;
-               ieee80211_wake_queue(wl->hw,
-                                    wl1271_tx_get_mac80211_queue(i));
-       }
-
+       stopped = wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, queue,
+                                                          reason);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       return stopped;
 }
 
-bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
-                            enum wlcore_queue_stop_reason reason)
+bool wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
+                                      enum wlcore_queue_stop_reason reason)
 {
-       return test_bit(reason, &wl->queue_stop_reasons[queue]);
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+       WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock));
+       return test_bit(reason, &wl->queue_stop_reasons[hwq]);
 }
 
-bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue)
+bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                   u8 queue)
 {
-       return !!wl->queue_stop_reasons[queue];
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+       WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock));
+       return !!wl->queue_stop_reasons[hwq];
 }
index 349520d..55aa4ac 100644 (file)
@@ -207,19 +207,22 @@ static inline int wl1271_tx_get_queue(int queue)
        }
 }
 
-static inline int wl1271_tx_get_mac80211_queue(int queue)
+static inline
+int wlcore_tx_get_mac80211_queue(struct wl12xx_vif *wlvif, int queue)
 {
+       int mac_queue = wlvif->hw_queue_base;
+
        switch (queue) {
        case CONF_TX_AC_VO:
-               return 0;
+               return mac_queue + 0;
        case CONF_TX_AC_VI:
-               return 1;
+               return mac_queue + 1;
        case CONF_TX_AC_BE:
-               return 2;
+               return mac_queue + 2;
        case CONF_TX_AC_BK:
-               return 3;
+               return mac_queue + 3;
        default:
-               return 2;
+               return mac_queue + 2;
        }
 }
 
@@ -252,20 +255,26 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
 unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
                                          unsigned int packet_length);
 void wl1271_free_tx_id(struct wl1271 *wl, int id);
-void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
-                             enum wlcore_queue_stop_reason reason);
-void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 queue, enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason);
-void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason);
 void wlcore_stop_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason);
 void wlcore_wake_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason);
-void wlcore_reset_stopped_queues(struct wl1271 *wl);
-bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
                                       enum wlcore_queue_stop_reason reason);
-bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue);
+bool
+wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
+                                        struct wl12xx_vif *wlvif,
+                                        u8 queue,
+                                        enum wlcore_queue_stop_reason reason);
+bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                   u8 queue);
 
 /* from main.c */
 void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
index c388493..ebd8c6f 100644 (file)
@@ -37,6 +37,9 @@
  */
 #define WLCORE_NUM_MAC_ADDRESSES 3
 
+/* wl12xx/wl18xx maximum transmission power (in dBm) */
+#define WLCORE_MAX_TXPWR        25
+
 /* forward declaration */
 struct wl1271_tx_hw_descr;
 enum wl_rx_buf_align;
@@ -51,6 +54,9 @@ struct wlcore_ops {
        int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
                           void *buf, size_t len);
        int (*ack_event)(struct wl1271 *wl);
+       int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event,
+                             bool *timeout);
+       int (*process_mailbox_events)(struct wl1271 *wl);
        u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks);
        void (*set_tx_desc_blocks)(struct wl1271 *wl,
                                   struct wl1271_tx_hw_descr *desc,
@@ -82,12 +88,32 @@ struct wlcore_ops {
        int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
        int (*handle_static_data)(struct wl1271 *wl,
                                  struct wl1271_static_data *static_data);
+       int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         struct cfg80211_scan_request *req);
+       int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+       int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                               struct cfg80211_sched_scan_request *req,
+                               struct ieee80211_sched_scan_ies *ies);
+       void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
        int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
        int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
                       struct ieee80211_vif *vif,
                       struct ieee80211_sta *sta,
                       struct ieee80211_key_conf *key_conf);
+       int (*channel_switch)(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
        u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
+       void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct ieee80211_sta *sta, u32 changed);
+       int (*set_peer_cap)(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid);
+       bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
+                             struct wl1271_link *lnk);
+       bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
+                            struct wl1271_link *lnk);
 };
 
 enum wlcore_partitions {
@@ -202,6 +228,8 @@ struct wl1271 {
        unsigned long klv_templates_map[
                        BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
 
+       u8 session_ids[WL12XX_MAX_LINKS];
+
        struct list_head wlvif_list;
 
        u8 sta_count;
@@ -227,7 +255,8 @@ struct wl1271 {
 
        /* Frames scheduled for transmission, not handled yet */
        int tx_queue_count[NUM_TX_QUEUES];
-       unsigned long queue_stop_reasons[NUM_TX_QUEUES];
+       unsigned long queue_stop_reasons[
+                               NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES];
 
        /* Frames received, not handled yet by mac80211 */
        struct sk_buff_head deferred_rx_queue;
@@ -269,24 +298,30 @@ struct wl1271 {
        struct work_struct recovery_work;
        bool watchdog_recovery;
 
+       /* Reg domain last configuration */
+       u32 reg_ch_conf_last[2];
+       /* Reg domain pending configuration */
+       u32 reg_ch_conf_pending[2];
+
        /* Pointer that holds DMA-friendly block for the mailbox */
-       struct event_mailbox *mbox;
+       void *mbox;
 
        /* The mbox event mask */
        u32 event_mask;
 
        /* Mailbox pointers */
+       u32 mbox_size;
        u32 mbox_ptr[2];
 
        /* Are we currently scanning */
-       struct ieee80211_vif *scan_vif;
+       struct wl12xx_vif *scan_wlvif;
        struct wl1271_scan scan;
        struct delayed_work scan_complete_work;
 
-       /* Connection loss work */
-       struct delayed_work connection_loss_work;
+       struct ieee80211_vif *roc_vif;
+       struct delayed_work roc_complete_work;
 
-       bool sched_scanning;
+       struct wl12xx_vif *sched_vif;
 
        /* The current band */
        enum ieee80211_band band;
@@ -299,7 +334,7 @@ struct wl1271 {
 
        struct wl1271_stats stats;
 
-       __le32 buffer_32;
+       __le32 *buffer_32;
        u32 buffer_cmd;
        u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
 
@@ -314,6 +349,8 @@ struct wl1271 {
 
        bool enable_11a;
 
+       int recovery_count;
+
        /* Most recently reported noise in dBm */
        s8 noise;
 
@@ -333,6 +370,12 @@ struct wl1271 {
         */
        struct wl1271_link links[WL12XX_MAX_LINKS];
 
+       /* number of currently active links */
+       int active_link_count;
+
+       /* Fast/slow links bitmap according to FW */
+       u32 fw_fast_lnk_map;
+
        /* AP-mode - a bitmap of links currently in PS mode according to FW */
        u32 ap_fw_ps_map;
 
@@ -367,6 +410,12 @@ struct wl1271 {
        const char *sr_fw_name;
        const char *mr_fw_name;
 
+       u8 scan_templ_id_2_4;
+       u8 scan_templ_id_5;
+       u8 sched_scan_templ_id_2_4;
+       u8 sched_scan_templ_id_5;
+       u8 max_channels_5;
+
        /* per-chip-family private structure */
        void *priv;
 
@@ -408,20 +457,28 @@ struct wl1271 {
        /* the number of allocated MAC addresses in this chip */
        int num_mac_addr;
 
-       /* the minimum FW version required for the driver to work */
-       unsigned int min_fw_ver[NUM_FW_VER];
+       /* minimum FW version required for the driver to work in single-role */
+       unsigned int min_sr_fw_ver[NUM_FW_VER];
+
+       /* minimum FW version required for the driver to work in multi-role */
+       unsigned int min_mr_fw_ver[NUM_FW_VER];
 
        struct completion nvs_loading_complete;
+
+       /* number of concurrent channels the HW supports */
+       u32 num_channels;
 };
 
 int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
 int wlcore_remove(struct platform_device *pdev);
-struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size);
+struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
+                                    u32 mbox_size);
 int wlcore_free_hw(struct wl1271 *wl);
 int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                   struct ieee80211_vif *vif,
                   struct ieee80211_sta *sta,
                   struct ieee80211_key_conf *key_conf);
+void wlcore_regdomain_config(struct wl1271 *wl);
 
 static inline void
 wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
@@ -430,16 +487,27 @@ wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
        memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
 }
 
+/* Tell wlcore not to care about this element when checking the version */
+#define WLCORE_FW_VER_IGNORE   -1
+
 static inline void
 wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
-                     unsigned int iftype, unsigned int major,
-                     unsigned int subtype, unsigned int minor)
+                     unsigned int iftype_sr, unsigned int major_sr,
+                     unsigned int subtype_sr, unsigned int minor_sr,
+                     unsigned int iftype_mr, unsigned int major_mr,
+                     unsigned int subtype_mr, unsigned int minor_mr)
 {
-       wl->min_fw_ver[FW_VER_CHIP] = chip;
-       wl->min_fw_ver[FW_VER_IF_TYPE] = iftype;
-       wl->min_fw_ver[FW_VER_MAJOR] = major;
-       wl->min_fw_ver[FW_VER_SUBTYPE] = subtype;
-       wl->min_fw_ver[FW_VER_MINOR] = minor;
+       wl->min_sr_fw_ver[FW_VER_CHIP] = chip;
+       wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr;
+       wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr;
+       wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr;
+       wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr;
+
+       wl->min_mr_fw_ver[FW_VER_CHIP] = chip;
+       wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr;
+       wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr;
+       wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr;
+       wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr;
 }
 
 /* Firmware image load chunk size */
@@ -450,6 +518,9 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* Each RX/TX transaction requires an end-of-transaction transfer */
 #define WLCORE_QUIRK_END_OF_TRANSACTION                BIT(0)
 
+/* the first start_role(sta) sometimes doesn't work on wl12xx */
+#define WLCORE_QUIRK_START_STA_FAILS           BIT(1)
+
 /* wl127x and SPI don't support SDIO block size alignment */
 #define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN                BIT(2)
 
@@ -462,9 +533,6 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* Older firmwares use an old NVS format */
 #define WLCORE_QUIRK_LEGACY_NVS                        BIT(5)
 
-/* Some firmwares may not support ELP */
-#define WLCORE_QUIRK_NO_ELP                    BIT(6)
-
 /* pad only the last frame in the aggregate buffer */
 #define WLCORE_QUIRK_TX_PAD_LAST_FRAME         BIT(7)
 
@@ -477,11 +545,11 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* separate probe response templates for one-shot and sched scans */
 #define WLCORE_QUIRK_DUAL_PROBE_TMPL           BIT(10)
 
-/* TODO: move to the lower drivers when all usages are abstracted */
-#define CHIP_ID_1271_PG10              (0x4030101)
-#define CHIP_ID_1271_PG20              (0x4030111)
-#define CHIP_ID_1283_PG10              (0x05030101)
-#define CHIP_ID_1283_PG20              (0x05030111)
+/* Firmware requires reg domain configuration for active calibration */
+#define WLCORE_QUIRK_REGDOMAIN_CONF            BIT(11)
+
+/* The FW only support a zero session id for AP */
+#define WLCORE_QUIRK_AP_ZERO_SESSION_ID                BIT(12)
 
 /* TODO: move all these common registers and values elsewhere */
 #define HW_ACCESS_ELP_CTRL_REG         0x1FFFC
index 5ce26cf..910f8e2 100644 (file)
@@ -109,17 +109,6 @@ enum {
        NUM_FW_VER
 };
 
-#define FW_VER_CHIP_WL127X 6
-#define FW_VER_CHIP_WL128X 7
-
-#define FW_VER_IF_TYPE_STA 1
-#define FW_VER_IF_TYPE_AP  2
-
-#define FW_VER_MINOR_1_SPARE_STA_MIN 58
-#define FW_VER_MINOR_1_SPARE_AP_MIN  47
-
-#define FW_VER_MINOR_FWLOG_STA_MIN 70
-
 struct wl1271_chip {
        u32 id;
        char fw_ver_str[ETHTOOL_FWVERS_LEN];
@@ -141,7 +130,10 @@ struct wl_fw_packet_counters {
        /* Cumulative counter of released Voice memory blocks */
        u8 tx_voice_released_blks;
 
-       u8 padding[3];
+       /* Tx rate of the last transmitted packet */
+       u8 tx_last_rate;
+
+       u8 padding[2];
 } __packed;
 
 /* FW status registers */
@@ -260,6 +252,8 @@ enum wl12xx_vif_flags {
        WLVIF_FLAG_IN_USE,
 };
 
+struct wl12xx_vif;
+
 struct wl1271_link {
        /* AP-mode - TX queue per AC in link */
        struct sk_buff_head tx_queue[NUM_TX_QUEUES];
@@ -272,6 +266,9 @@ struct wl1271_link {
 
        /* bitmap of TIDs where RX BA sessions are active for this link */
        u8 ba_bitmap;
+
+       /* The wlvif this link belongs to. Might be null for global links */
+       struct wl12xx_vif *wlvif;
 };
 
 #define WL1271_MAX_RX_FILTERS 5
@@ -315,6 +312,7 @@ struct wl12xx_rx_filter {
 
 struct wl1271_station {
        u8 hlid;
+       bool in_connection;
 };
 
 struct wl12xx_vif {
@@ -332,7 +330,6 @@ struct wl12xx_vif {
        union {
                struct {
                        u8 hlid;
-                       u8 ba_rx_bitmap;
 
                        u8 basic_rate_idx;
                        u8 ap_rate_idx;
@@ -341,6 +338,8 @@ struct wl12xx_vif {
                        u8 klv_template_id;
 
                        bool qos;
+                       /* channel type we started the STA role with */
+                       enum nl80211_channel_type role_chan_type;
                } sta;
                struct {
                        u8 global_hlid;
@@ -362,6 +361,9 @@ struct wl12xx_vif {
        /* the hlid of the last transmitted skb */
        int last_tx_hlid;
 
+       /* counters of packets per AC, across all links in the vif */
+       int tx_queue_count[NUM_TX_QUEUES];
+
        unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
 
        u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
@@ -396,9 +398,6 @@ struct wl12xx_vif {
        /* Our association ID */
        u16 aid;
 
-       /* Session counter for the chipset */
-       int session_counter;
-
        /* retry counter for PSM entries */
        u8 psm_entry_retry;
 
@@ -416,11 +415,28 @@ struct wl12xx_vif {
        bool ba_support;
        bool ba_allowed;
 
+       bool wmm_enabled;
+
        /* Rx Streaming */
        struct work_struct rx_streaming_enable_work;
        struct work_struct rx_streaming_disable_work;
        struct timer_list rx_streaming_timer;
 
+       struct delayed_work channel_switch_work;
+       struct delayed_work connection_loss_work;
+
+       /* number of in connection stations */
+       int inconn_count;
+
+       /*
+        * This vif's queues are mapped to mac80211 HW queues as:
+        * VO - hw_queue_base
+        * VI - hw_queue_base + 1
+        * BE - hw_queue_base + 2
+        * BK - hw_queue_base + 3
+        */
+       int hw_queue_base;
+
        /*
         * This struct must be last!
         * data that has to be saved acrossed reconfigs (e.g. recovery)
@@ -443,6 +459,7 @@ struct wl12xx_vif {
 
 static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif)
 {
+       WARN_ON(!vif);
        return (struct wl12xx_vif *)vif->drv_priv;
 }
 
index 97ac0a3..accabe3 100644 (file)
@@ -74,6 +74,16 @@ static void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned gpio)
        ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0);
 }
 
+static int ssb_gpio_chipco_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+       struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+       if (bus->bustype == SSB_BUSTYPE_SSB)
+               return ssb_mips_irq(bus->chipco.dev) + 2;
+       else
+               return -EINVAL;
+}
+
 static int ssb_gpio_chipco_init(struct ssb_bus *bus)
 {
        struct gpio_chip *chip = &bus->gpio;
@@ -86,6 +96,7 @@ static int ssb_gpio_chipco_init(struct ssb_bus *bus)
        chip->set               = ssb_gpio_chipco_set_value;
        chip->direction_input   = ssb_gpio_chipco_direction_input;
        chip->direction_output  = ssb_gpio_chipco_direction_output;
+       chip->to_irq            = ssb_gpio_chipco_to_irq;
        chip->ngpio             = 16;
        /* There is just one SoC in one device and its GPIO addresses should be
         * deterministic to address them more easily. The other buses could get
@@ -134,6 +145,16 @@ static int ssb_gpio_extif_direction_output(struct gpio_chip *chip,
        return 0;
 }
 
+static int ssb_gpio_extif_to_irq(struct gpio_chip *chip, unsigned gpio)
+{
+       struct ssb_bus *bus = ssb_gpio_get_bus(chip);
+
+       if (bus->bustype == SSB_BUSTYPE_SSB)
+               return ssb_mips_irq(bus->extif.dev) + 2;
+       else
+               return -EINVAL;
+}
+
 static int ssb_gpio_extif_init(struct ssb_bus *bus)
 {
        struct gpio_chip *chip = &bus->gpio;
@@ -144,6 +165,7 @@ static int ssb_gpio_extif_init(struct ssb_bus *bus)
        chip->set               = ssb_gpio_extif_set_value;
        chip->direction_input   = ssb_gpio_extif_direction_input;
        chip->direction_output  = ssb_gpio_extif_direction_output;
+       chip->to_irq            = ssb_gpio_extif_to_irq;
        chip->ngpio             = 5;
        /* There is just one SoC in one device and its GPIO addresses should be
         * deterministic to address them more easily. The other buses could get
index 2a7684c..33b37da 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/ssb/ssb.h>
 
+#include <linux/mtd/physmap.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
 #include <linux/serial_reg.h>
 
 #include "ssb_private.h"
 
+static const char *part_probes[] = { "bcm47xxpart", NULL };
+
+static struct physmap_flash_data ssb_pflash_data = {
+       .part_probe_types       = part_probes,
+};
+
+static struct resource ssb_pflash_resource = {
+       .name   = "ssb_pflash",
+       .flags  = IORESOURCE_MEM,
+};
+
+struct platform_device ssb_pflash_dev = {
+       .name           = "physmap-flash",
+       .dev            = {
+               .platform_data  = &ssb_pflash_data,
+       },
+       .resource       = &ssb_pflash_resource,
+       .num_resources  = 1,
+};
 
 static inline u32 mips_read32(struct ssb_mipscore *mcore,
                              u16 offset)
@@ -189,14 +209,15 @@ static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
 static void ssb_mips_flash_detect(struct ssb_mipscore *mcore)
 {
        struct ssb_bus *bus = mcore->dev->bus;
+       struct ssb_pflash *pflash = &mcore->pflash;
 
        /* When there is no chipcommon on the bus there is 4MB flash */
        if (!ssb_chipco_available(&bus->chipco)) {
-               mcore->pflash.present = true;
-               mcore->pflash.buswidth = 2;
-               mcore->pflash.window = SSB_FLASH1;
-               mcore->pflash.window_size = SSB_FLASH1_SZ;
-               return;
+               pflash->present = true;
+               pflash->buswidth = 2;
+               pflash->window = SSB_FLASH1;
+               pflash->window_size = SSB_FLASH1_SZ;
+               goto ssb_pflash;
        }
 
        /* There is ChipCommon, so use it to read info about flash */
@@ -208,16 +229,23 @@ static void ssb_mips_flash_detect(struct ssb_mipscore *mcore)
                break;
        case SSB_CHIPCO_FLASHT_PARA:
                pr_debug("Found parallel flash\n");
-               mcore->pflash.present = true;
-               mcore->pflash.window = SSB_FLASH2;
-               mcore->pflash.window_size = SSB_FLASH2_SZ;
+               pflash->present = true;
+               pflash->window = SSB_FLASH2;
+               pflash->window_size = SSB_FLASH2_SZ;
                if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG)
                               & SSB_CHIPCO_CFG_DS16) == 0)
-                       mcore->pflash.buswidth = 1;
+                       pflash->buswidth = 1;
                else
-                       mcore->pflash.buswidth = 2;
+                       pflash->buswidth = 2;
                break;
        }
+
+ssb_pflash:
+       if (pflash->present) {
+               ssb_pflash_data.width = pflash->buswidth;
+               ssb_pflash_resource.start = pflash->window;
+               ssb_pflash_resource.end = pflash->window + pflash->window_size;
+       }
 }
 
 u32 ssb_cpu_clock(struct ssb_mipscore *mcore)
index 772ad9b..9987d9f 100644 (file)
@@ -540,6 +540,14 @@ static int ssb_devices_register(struct ssb_bus *bus)
                dev_idx++;
        }
 
+#ifdef CONFIG_SSB_DRIVER_MIPS
+       if (bus->mipscore.pflash.present) {
+               err = platform_device_register(&ssb_pflash_dev);
+               if (err)
+                       pr_err("Error registering parallel flash\n");
+       }
+#endif
+
        return 0;
 error:
        /* Unwind the already registered devices. */
index 77d9426..53198dc 100644 (file)
@@ -228,6 +228,10 @@ static inline int ssb_sflash_init(struct ssb_chipcommon *cc)
 }
 #endif /* CONFIG_SSB_SFLASH */
 
+#ifdef CONFIG_SSB_DRIVER_MIPS
+extern struct platform_device ssb_pflash_dev;
+#endif
+
 #ifdef CONFIG_SSB_DRIVER_EXTIF
 extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks);
 extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
index ee332fa..1d002b5 100644 (file)
@@ -27,7 +27,7 @@
 #define   BCMA_CC_FLASHT_NONE          0x00000000      /* No flash */
 #define   BCMA_CC_FLASHT_STSER         0x00000100      /* ST serial flash */
 #define   BCMA_CC_FLASHT_ATSER         0x00000200      /* Atmel serial flash */
-#define   BCMA_CC_FLASHT_NFLASH                0x00000200      /* NAND flash */
+#define   BCMA_CC_FLASHT_NAND          0x00000300      /* NAND flash */
 #define          BCMA_CC_FLASHT_PARA           0x00000700      /* Parallel flash */
 #define  BCMA_CC_CAP_PLLT              0x00038000      /* PLL Type */
 #define   BCMA_PLLTYPE_NONE            0x00000000
index 0d1ea29..fb61f3f 100644 (file)
@@ -42,13 +42,18 @@ struct bcma_drv_mips {
 #ifdef CONFIG_BCMA_DRIVER_MIPS
 extern void bcma_core_mips_init(struct bcma_drv_mips *mcore);
 extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore);
+
+extern unsigned int bcma_core_irq(struct bcma_device *core);
 #else
 static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { }
 static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { }
+
+static inline unsigned int bcma_core_irq(struct bcma_device *core)
+{
+       return 0;
+}
 #endif
 
 extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore);
 
-extern unsigned int bcma_core_irq(struct bcma_device *core);
-
 #endif /* LINUX_BCMA_DRIVER_MIPS_H_ */
index ccf9ee1..11c8bc8 100644 (file)
@@ -1898,7 +1898,10 @@ enum ieee80211_sa_query_action {
 /* AKM suite selectors */
 #define WLAN_AKM_SUITE_8021X           0x000FAC01
 #define WLAN_AKM_SUITE_PSK             0x000FAC02
-#define WLAN_AKM_SUITE_SAE                     0x000FAC08
+#define WLAN_AKM_SUITE_8021X_SHA256    0x000FAC05
+#define WLAN_AKM_SUITE_PSK_SHA256      0x000FAC06
+#define WLAN_AKM_SUITE_TDLS            0x000FAC07
+#define WLAN_AKM_SUITE_SAE             0x000FAC08
 #define WLAN_AKM_SUITE_FT_OVER_SAE     0x000FAC09
 
 #define WLAN_MAX_KEY_LEN               32
index 07a9c7a..afe79d4 100644 (file)
@@ -45,6 +45,11 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore)
 {
 }
 
+static inline unsigned int ssb_mips_irq(struct ssb_device *dev)
+{
+       return 0;
+}
+
 #endif /* CONFIG_SSB_DRIVER_MIPS */
 
 #endif /* LINUX_SSB_MIPSCORE_H_ */
index 42f2176..487b54c 100644 (file)
@@ -23,6 +23,7 @@ enum amp_mgr_state {
        READ_LOC_AMP_INFO,
        READ_LOC_AMP_ASSOC,
        READ_LOC_AMP_ASSOC_FINAL,
+       WRITE_REMOTE_AMP_ASSOC,
 };
 
 struct amp_mgr {
@@ -33,7 +34,7 @@ struct amp_mgr {
        struct kref             kref;
        __u8                    ident;
        __u8                    handle;
-       enum amp_mgr_state      state;
+       unsigned long           state;
        unsigned long           flags;
 
        struct list_head        amp_ctrls;
@@ -144,5 +145,6 @@ void a2mp_discover_amp(struct l2cap_chan *chan);
 void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
 void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
 void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
+void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status);
 
 #endif /* __A2MP_H */
index 2554b3f..9531bee 100644 (file)
@@ -166,6 +166,29 @@ typedef struct {
 #define BDADDR_LE_PUBLIC       0x01
 #define BDADDR_LE_RANDOM       0x02
 
+static inline bool bdaddr_type_is_valid(__u8 type)
+{
+       switch (type) {
+       case BDADDR_BREDR:
+       case BDADDR_LE_PUBLIC:
+       case BDADDR_LE_RANDOM:
+               return true;
+       }
+
+       return false;
+}
+
+static inline bool bdaddr_type_is_le(__u8 type)
+{
+       switch (type) {
+       case BDADDR_LE_PUBLIC:
+       case BDADDR_LE_RANDOM:
+               return true;
+       }
+
+       return false;
+}
+
 #define BDADDR_ANY   (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} })
 #define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} })
 
index 45eee08..7f12c25 100644 (file)
@@ -943,6 +943,12 @@ struct hci_rp_le_read_buffer_size {
        __u8     le_max_pkt;
 } __packed;
 
+#define HCI_OP_LE_READ_LOCAL_FEATURES  0x2003
+struct hci_rp_le_read_local_features {
+       __u8     status;
+       __u8     features[8];
+} __packed;
+
 #define HCI_OP_LE_READ_ADV_TX_POWER    0x2007
 struct hci_rp_le_read_adv_tx_power {
        __u8    status;
@@ -995,6 +1001,12 @@ struct hci_cp_le_create_conn {
 
 #define HCI_OP_LE_CREATE_CONN_CANCEL   0x200e
 
+#define HCI_OP_LE_READ_WHITE_LIST_SIZE 0x200f
+struct hci_rp_le_read_white_list_size {
+       __u8    status;
+       __u8    size;
+} __packed;
+
 #define HCI_OP_LE_CONN_UPDATE          0x2013
 struct hci_cp_le_conn_update {
        __le16   handle;
@@ -1033,6 +1045,12 @@ struct hci_rp_le_ltk_neg_reply {
        __le16  handle;
 } __packed;
 
+#define HCI_OP_LE_READ_SUPPORTED_STATES        0x201c
+struct hci_rp_le_read_supported_states {
+       __u8    status;
+       __u8    le_states[8];
+} __packed;
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE                0x01
 
index 014a2ea..90cf75a 100644 (file)
@@ -86,6 +86,7 @@ struct bdaddr_list {
 struct bt_uuid {
        struct list_head list;
        u8 uuid[16];
+       u8 size;
        u8 svc_hint;
 };
 
@@ -152,6 +153,9 @@ struct hci_dev {
        __u8            minor_class;
        __u8            features[8];
        __u8            host_features[8];
+       __u8            le_features[8];
+       __u8            le_white_list_size;
+       __u8            le_states[8];
        __u8            commands[64];
        __u8            hci_ver;
        __u16           hci_rev;
@@ -216,6 +220,7 @@ struct hci_dev {
        unsigned long   le_last_tx;
 
        struct workqueue_struct *workqueue;
+       struct workqueue_struct *req_workqueue;
 
        struct work_struct      power_on;
        struct delayed_work     power_off;
index 7588ef4..cdd3302 100644 (file)
@@ -496,7 +496,6 @@ struct l2cap_chan {
        __u16           frames_sent;
        __u16           unacked_frames;
        __u8            retry_count;
-       __u16           srej_queue_next;
        __u16           sdu_len;
        struct sk_buff  *sdu;
        struct sk_buff  *sdu_last_frag;
index 1b9830e..f9df200 100644 (file)
@@ -527,6 +527,26 @@ struct cfg80211_beacon_data {
        size_t probe_resp_len;
 };
 
+struct mac_address {
+       u8 addr[ETH_ALEN];
+};
+
+/**
+ * struct cfg80211_acl_data - Access control list data
+ *
+ * @acl_policy: ACL policy to be applied on the station's
+       entry specified by mac_addr
+ * @n_acl_entries: Number of MAC address entries passed
+ * @mac_addrs: List of MAC addresses of stations to be used for ACL
+ */
+struct cfg80211_acl_data {
+       enum nl80211_acl_policy acl_policy;
+       int n_acl_entries;
+
+       /* Keep it last */
+       struct mac_address mac_addrs[];
+};
+
 /**
  * struct cfg80211_ap_settings - AP configuration
  *
@@ -546,6 +566,8 @@ struct cfg80211_beacon_data {
  * @inactivity_timeout: time in seconds to determine station's inactivity.
  * @p2p_ctwindow: P2P CT Window
  * @p2p_opp_ps: P2P opportunistic PS
+ * @acl: ACL configuration used by the drivers which has support for
+ *     MAC address based access control
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
@@ -562,6 +584,7 @@ struct cfg80211_ap_settings {
        int inactivity_timeout;
        u8 p2p_ctwindow;
        bool p2p_opp_ps;
+       const struct cfg80211_acl_data *acl;
 };
 
 /**
@@ -1796,6 +1819,13 @@ struct cfg80211_gtk_rekey_data {
  *
  * @start_p2p_device: Start the given P2P device.
  * @stop_p2p_device: Stop the given P2P device.
+ *
+ * @set_mac_acl: Sets MAC address control list in AP and P2P GO mode.
+ *     Parameters include ACL policy, an array of MAC address of stations
+ *     and the number of MAC addresses. If there is already a list in driver
+ *     this new list replaces the existing one. Driver has to clear its ACL
+ *     when number of MAC addresses entries is passed as 0. Drivers which
+ *     advertise the support for MAC based ACL have to implement this callback.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2016,6 +2046,9 @@ struct cfg80211_ops {
                                    struct wireless_dev *wdev);
        void    (*stop_p2p_device)(struct wiphy *wiphy,
                                   struct wireless_dev *wdev);
+
+       int     (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
+                              const struct cfg80211_acl_data *params);
 };
 
 /*
@@ -2181,10 +2214,6 @@ struct ieee80211_iface_combination {
        u8 radar_detect_widths;
 };
 
-struct mac_address {
-       u8 addr[ETH_ALEN];
-};
-
 struct ieee80211_txrx_stypes {
        u16 tx, rx;
 };
@@ -2325,6 +2354,9 @@ struct wiphy_wowlan_support {
  * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
  * @ht_capa_mod_mask:  Specify what ht_cap values can be over-ridden.
  *     If null, then none can be over-ridden.
+ *
+ * @max_acl_mac_addrs: Maximum number of MAC addresses that the device
+ *     supports for ACL.
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -2346,6 +2378,8 @@ struct wiphy {
        /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */
        u16 interface_modes;
 
+       u16 max_acl_mac_addrs;
+
        u32 flags, features;
 
        u32 ap_sme_capa;
index 3037f49..5c98d65 100644 (file)
@@ -297,11 +297,9 @@ enum ieee80211_rssi_event {
  *     may filter ARP queries targeted for other addresses than listed here.
  *     The driver must allow ARP queries targeted for all address listed here
  *     to pass through. An empty list implies no ARP queries need to pass.
- * @arp_addr_cnt: Number of addresses currently on the list.
- * @arp_filter_enabled: Enable ARP filtering - if enabled, the hardware may
- *     filter ARP queries based on the @arp_addr_list, if disabled, the
- *     hardware must not perform any ARP filtering. Note, that the filter will
- *     be enabled also in promiscuous mode.
+ * @arp_addr_cnt: Number of addresses currently on the list. Note that this
+ *     may be larger than %IEEE80211_BSS_ARP_ADDR_LIST_LEN (the arp_addr_list
+ *     array size), it's up to the driver what to do in that case.
  * @qos: This is a QoS-enabled BSS.
  * @idle: This interface is idle. There's also a global idle flag in the
  *     hardware config which may be more appropriate depending on what
@@ -338,8 +336,7 @@ struct ieee80211_bss_conf {
        u32 cqm_rssi_hyst;
        struct cfg80211_chan_def chandef;
        __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
-       u8 arp_addr_cnt;
-       bool arp_filter_enabled;
+       int arp_addr_cnt;
        bool qos;
        bool idle;
        bool ps;
@@ -1630,6 +1627,10 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
  * rekeying), it will not include a valid phase 1 key. The valid phase 1 key is
  * provided by update_tkip_key only. The trigger that makes mac80211 call this
  * handler is software decryption with wrap around of iv16.
+ *
+ * The set_default_unicast_key() call updates the default WEP key index
+ * configured to the hardware for WEP encryption type. This is required
+ * for devices that support offload of data packets (e.g. ARP responses).
  */
 
 /**
@@ -2208,6 +2209,10 @@ enum ieee80211_rate_control_changed {
  *     After rekeying was done it should (for example during resume) notify
  *     userspace of the new replay counter using ieee80211_gtk_rekey_notify().
  *
+ * @set_default_unicast_key: Set the default (unicast) key index, useful for
+ *     WEP when the device sends data packets autonomously, e.g. for ARP
+ *     offloading. The index can be 0-3, or -1 for unsetting it.
+ *
  * @hw_scan: Ask the hardware to service the scan request, no need to start
  *     the scan state machine in stack. The scan must honour the channel
  *     configuration done by the regulatory agent in the wiphy's
@@ -2492,6 +2497,9 @@ enum ieee80211_rate_control_changed {
  *     driver's resume function returned 1, as this is just like an "inline"
  *     hardware restart. This callback may sleep.
  *
+ * @ipv6_addr_change: IPv6 address assignment on the given interface changed.
+ *     Currently, this is only called for managed or P2P client interfaces.
+ *     This callback is optional; it must not sleep.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -2539,6 +2547,8 @@ struct ieee80211_ops {
        void (*set_rekey_data)(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               struct cfg80211_gtk_rekey_data *data);
+       void (*set_default_unicast_key)(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif, int idx);
        int (*hw_scan)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       struct cfg80211_scan_request *req);
        void (*cancel_hw_scan)(struct ieee80211_hw *hw,
@@ -2623,6 +2633,7 @@ struct ieee80211_ops {
        int (*set_bitrate_mask)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                                const struct cfg80211_bitrate_mask *mask);
        void (*rssi_callback)(struct ieee80211_hw *hw,
+                             struct ieee80211_vif *vif,
                              enum ieee80211_rssi_event rssi_event);
 
        void (*allow_buffered_frames)(struct ieee80211_hw *hw,
@@ -2665,6 +2676,12 @@ struct ieee80211_ops {
                                     struct ieee80211_chanctx_conf *ctx);
 
        void (*restart_complete)(struct ieee80211_hw *hw);
+
+#if IS_ENABLED(CONFIG_IPV6)
+       void (*ipv6_addr_change)(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct inet6_dev *idev);
+#endif
 };
 
 /**
index e6eeb4b..5b7dbc1 100644 (file)
  *     %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE,
  *     %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS,
  *     %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY,
- *     %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT.
+ *     %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT,
+ *     %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS.
  *     The channel to use can be set on the interface or be given using the
  *     %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width.
  * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP
  * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames
  *     for IBSS or MESH vif.
  *
+ * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control.
+ *     This is to be used with the drivers advertising the support of MAC
+ *     address based access control. List of MAC addresses is passed in
+ *     %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in
+ *     %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it
+ *     is not already done. The new list will replace any existing list. Driver
+ *     will clear its ACL when the list of MAC addresses passed is empty. This
+ *     command is used in AP/P2P GO mode. Driver has to make sure to clear its
+ *     ACL list during %NL80211_CMD_STOP_AP.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -736,6 +747,8 @@ enum nl80211_commands {
 
        NL80211_CMD_SET_MCAST_RATE,
 
+       NL80211_CMD_SET_MAC_ACL,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1313,6 +1326,16 @@ enum nl80211_commands {
  * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode
  *     defined in &enum nl80211_mesh_power_mode.
  *
+ * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy,
+ *     carried in a u32 attribute
+ *
+ * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for
+ *     MAC ACL.
+ *
+ * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum
+ *     number of MAC addresses that a device can support for MAC
+ *     ACL.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1585,6 +1608,12 @@ enum nl80211_attrs {
 
        NL80211_ATTR_LOCAL_MESH_POWER_MODE,
 
+       NL80211_ATTR_ACL_POLICY,
+
+       NL80211_ATTR_MAC_ADDRS,
+
+       NL80211_ATTR_MAC_ACL_MAX,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3248,7 +3277,7 @@ enum nl80211_probe_resp_offload_support_attr {
  * enum nl80211_connect_failed_reason - connection request failed reasons
  * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
  *     handled by the AP is reached.
- * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL.
  */
 enum nl80211_connect_failed_reason {
        NL80211_CONN_FAIL_MAX_CLIENTS,
@@ -3276,4 +3305,22 @@ enum nl80211_scan_flags {
        NL80211_SCAN_FLAG_AP                            = 1<<2,
 };
 
+/**
+ * enum nl80211_acl_policy - access control policy
+ *
+ * Access control policy is applied on a MAC list set by
+ * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to
+ * be used with %NL80211_ATTR_ACL_POLICY.
+ *
+ * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
+ *     listed in ACL, i.e. allow all the stations which are not listed
+ *     in ACL to authenticate.
+ * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed
+ *     in ACL, i.e. deny all the stations which are not listed in ACL.
+ */
+enum nl80211_acl_policy {
+       NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED,
+       NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
+};
+
 #endif /* __LINUX_NL80211_H */
index 2f67d5e..eb0f4b1 100644 (file)
@@ -290,7 +290,7 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
                goto done;
        }
 
-       mgr->state = READ_LOC_AMP_INFO;
+       set_bit(READ_LOC_AMP_INFO, &mgr->state);
        hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
 
 done:
@@ -499,8 +499,16 @@ send_rsp:
        if (hdev)
                hci_dev_put(hdev);
 
-       a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp),
-                 &rsp);
+       /* Reply error now and success after HCI Write Remote AMP Assoc
+          command complete with success status
+        */
+       if (rsp.status != A2MP_STATUS_SUCCESS) {
+               a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident,
+                         sizeof(rsp), &rsp);
+       } else {
+               set_bit(WRITE_REMOTE_AMP_ASSOC, &mgr->state);
+               mgr->ident = hdr->ident;
+       }
 
        skb_pull(skb, le16_to_cpu(hdr->len));
        return 0;
@@ -840,7 +848,7 @@ struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
 
        mutex_lock(&amp_mgr_list_lock);
        list_for_each_entry(mgr, &amp_mgr_list, list) {
-               if (mgr->state == state) {
+               if (test_and_clear_bit(state, &mgr->state)) {
                        amp_mgr_get(mgr);
                        mutex_unlock(&amp_mgr_list_lock);
                        return mgr;
@@ -949,6 +957,32 @@ clean:
        kfree(req);
 }
 
+void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status)
+{
+       struct amp_mgr *mgr;
+       struct a2mp_physlink_rsp rsp;
+       struct hci_conn *hs_hcon;
+
+       mgr = amp_mgr_lookup_by_state(WRITE_REMOTE_AMP_ASSOC);
+       if (!mgr)
+               return;
+
+       hs_hcon = hci_conn_hash_lookup_state(hdev, AMP_LINK, BT_CONNECT);
+       if (!hs_hcon) {
+               rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
+       } else {
+               rsp.remote_id = hs_hcon->remote_id;
+               rsp.status = A2MP_STATUS_SUCCESS;
+       }
+
+       BT_DBG("%s mgr %p hs_hcon %p status %u", hdev->name, mgr, hs_hcon,
+              status);
+
+       rsp.local_id = hdev->id;
+       a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, mgr->ident, sizeof(rsp), &rsp);
+       amp_mgr_put(mgr);
+}
+
 void a2mp_discover_amp(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
index 1b0d92c..d459ed4 100644 (file)
@@ -236,7 +236,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
 
        cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
 
-       mgr->state = READ_LOC_AMP_ASSOC;
+       set_bit(READ_LOC_AMP_ASSOC, &mgr->state);
        hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
 }
 
@@ -250,7 +250,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
        cp.len_so_far = cpu_to_le16(0);
        cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
 
-       mgr->state = READ_LOC_AMP_ASSOC_FINAL;
+       set_bit(READ_LOC_AMP_ASSOC_FINAL, &mgr->state);
 
        /* Read Local AMP Assoc final link information data */
        hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
@@ -317,7 +317,9 @@ void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle)
        if (!hcon)
                return;
 
-       amp_write_rem_assoc_frag(hdev, hcon);
+       /* Send A2MP create phylink rsp when all fragments are written */
+       if (amp_write_rem_assoc_frag(hdev, hcon))
+               a2mp_send_create_phy_link_rsp(hdev, 0);
 }
 
 void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle)
@@ -403,26 +405,20 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
 
 void amp_create_logical_link(struct l2cap_chan *chan)
 {
+       struct hci_conn *hs_hcon = chan->hs_hcon;
        struct hci_cp_create_accept_logical_link cp;
-       struct hci_conn *hcon;
        struct hci_dev *hdev;
 
-       BT_DBG("chan %p", chan);
+       BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst);
 
-       if (!chan->hs_hcon)
+       if (!hs_hcon)
                return;
 
        hdev = hci_dev_hold(chan->hs_hcon->hdev);
        if (!hdev)
                return;
 
-       BT_DBG("chan %p dst %pMR", chan, chan->conn->dst);
-
-       hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst);
-       if (!hcon)
-               goto done;
-
-       cp.phy_handle = hcon->handle;
+       cp.phy_handle = hs_hcon->handle;
 
        cp.tx_flow_spec.id = chan->local_id;
        cp.tx_flow_spec.stype = chan->local_stype;
@@ -438,14 +434,13 @@ void amp_create_logical_link(struct l2cap_chan *chan)
        cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat);
        cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to);
 
-       if (hcon->out)
+       if (hs_hcon->out)
                hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
                             &cp);
        else
                hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
                             &cp);
 
-done:
        hci_dev_put(hdev);
 }
 
index a5b6397..e430b1a 100644 (file)
@@ -33,7 +33,6 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/l2cap.h>
 
 #include "bnep.h"
 
index 0f78e34..22e77a7 100644 (file)
@@ -1146,7 +1146,8 @@ static void hci_power_on(struct work_struct *work)
                return;
 
        if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
-               schedule_delayed_work(&hdev->power_off, HCI_AUTO_OFF_TIMEOUT);
+               queue_delayed_work(hdev->req_workqueue, &hdev->power_off,
+                                  HCI_AUTO_OFF_TIMEOUT);
 
        if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
                mgmt_index_added(hdev);
@@ -1182,14 +1183,10 @@ static void hci_discov_off(struct work_struct *work)
 
 int hci_uuids_clear(struct hci_dev *hdev)
 {
-       struct list_head *p, *n;
-
-       list_for_each_safe(p, n, &hdev->uuids) {
-               struct bt_uuid *uuid;
+       struct bt_uuid *uuid, *tmp;
 
-               uuid = list_entry(p, struct bt_uuid, list);
-
-               list_del(p);
+       list_for_each_entry_safe(uuid, tmp, &hdev->uuids, list) {
+               list_del(&uuid->list);
                kfree(uuid);
        }
 
@@ -1621,8 +1618,8 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
        if (err < 0)
                return err;
 
-       schedule_delayed_work(&hdev->le_scan_disable,
-                             msecs_to_jiffies(timeout));
+       queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
+                          msecs_to_jiffies(timeout));
 
        return 0;
 }
@@ -1799,6 +1796,15 @@ int hci_register_dev(struct hci_dev *hdev)
                goto err;
        }
 
+       hdev->req_workqueue = alloc_workqueue(hdev->name,
+                                             WQ_HIGHPRI | WQ_UNBOUND |
+                                             WQ_MEM_RECLAIM, 1);
+       if (!hdev->req_workqueue) {
+               destroy_workqueue(hdev->workqueue);
+               error = -ENOMEM;
+               goto err;
+       }
+
        error = hci_add_sysfs(hdev);
        if (error < 0)
                goto err_wqueue;
@@ -1821,12 +1827,13 @@ int hci_register_dev(struct hci_dev *hdev)
        hci_notify(hdev, HCI_DEV_REG);
        hci_dev_hold(hdev);
 
-       schedule_work(&hdev->power_on);
+       queue_work(hdev->req_workqueue, &hdev->power_on);
 
        return id;
 
 err_wqueue:
        destroy_workqueue(hdev->workqueue);
+       destroy_workqueue(hdev->req_workqueue);
 err:
        ida_simple_remove(&hci_index_ida, hdev->id);
        write_lock(&hci_dev_list_lock);
@@ -1880,6 +1887,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
        hci_del_sysfs(hdev);
 
        destroy_workqueue(hdev->workqueue);
+       destroy_workqueue(hdev->req_workqueue);
 
        hci_dev_lock(hdev);
        hci_blacklist_clear(hdev);
index 81b4448..477726a 100644 (file)
@@ -609,8 +609,17 @@ static void le_setup(struct hci_dev *hdev)
        /* Read LE Buffer Size */
        hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
 
+       /* Read LE Local Supported Features */
+       hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL);
+
        /* Read LE Advertising Channel TX Power */
        hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
+
+       /* Read LE White List Size */
+       hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL);
+
+       /* Read LE Supported States */
+       hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
 }
 
 static void hci_setup(struct hci_dev *hdev)
@@ -1090,6 +1099,19 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
        hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
 }
 
+static void hci_cc_le_read_local_features(struct hci_dev *hdev,
+                                         struct sk_buff *skb)
+{
+       struct hci_rp_le_read_local_features *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (!rp->status)
+               memcpy(hdev->le_features, rp->features, 8);
+
+       hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status);
+}
+
 static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
                                        struct sk_buff *skb)
 {
@@ -1290,6 +1312,19 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
        }
 }
 
+static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
+                                          struct sk_buff *skb)
+{
+       struct hci_rp_le_read_white_list_size *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x size %u", hdev->name, rp->status, rp->size);
+
+       if (!rp->status)
+               hdev->le_white_list_size = rp->size;
+
+       hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status);
+}
+
 static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
@@ -1314,6 +1349,19 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
        hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
 }
 
+static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
+                                           struct sk_buff *skb)
+{
+       struct hci_rp_le_read_supported_states *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       if (!rp->status)
+               memcpy(hdev->le_states, rp->le_states, 8);
+
+       hci_req_complete(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, rp->status);
+}
+
 static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
                                           struct sk_buff *skb)
 {
@@ -2628,6 +2676,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_le_read_buffer_size(hdev, skb);
                break;
 
+       case HCI_OP_LE_READ_LOCAL_FEATURES:
+               hci_cc_le_read_local_features(hdev, skb);
+               break;
+
        case HCI_OP_LE_READ_ADV_TX_POWER:
                hci_cc_le_read_adv_tx_power(hdev, skb);
                break;
@@ -2664,6 +2716,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_le_set_scan_enable(hdev, skb);
                break;
 
+       case HCI_OP_LE_READ_WHITE_LIST_SIZE:
+               hci_cc_le_read_white_list_size(hdev, skb);
+               break;
+
        case HCI_OP_LE_LTK_REPLY:
                hci_cc_le_ltk_reply(hdev, skb);
                break;
@@ -2672,6 +2728,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_le_ltk_neg_reply(hdev, skb);
                break;
 
+       case HCI_OP_LE_READ_SUPPORTED_STATES:
+               hci_cc_le_read_supported_states(hdev, skb);
+               break;
+
        case HCI_OP_WRITE_LE_HOST_SUPPORTED:
                hci_cc_write_le_host_supported(hdev, skb);
                break;
@@ -3928,8 +3988,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
        void *ptr = &skb->data[1];
        s8 rssi;
 
-       hci_dev_lock(hdev);
-
        while (num_reports--) {
                struct hci_ev_le_advertising_info *ev = ptr;
 
@@ -3939,8 +3997,6 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                ptr += sizeof(*ev) + ev->length + 1;
        }
-
-       hci_dev_unlock(hdev);
 }
 
 static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
index 55cceee..23b4e24 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <linux/debugfs.h>
 #include <linux/module.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -461,19 +462,18 @@ static const struct file_operations blacklist_fops = {
 
 static void print_bt_uuid(struct seq_file *f, u8 *uuid)
 {
-       __be32 data0, data4;
-       __be16 data1, data2, data3, data5;
+       u32 data0, data5;
+       u16 data1, data2, data3, data4;
 
-       memcpy(&data0, &uuid[0], 4);
-       memcpy(&data1, &uuid[4], 2);
-       memcpy(&data2, &uuid[6], 2);
-       memcpy(&data3, &uuid[8], 2);
-       memcpy(&data4, &uuid[10], 4);
-       memcpy(&data5, &uuid[14], 2);
+       data5 = get_unaligned_le32(uuid);
+       data4 = get_unaligned_le16(uuid + 4);
+       data3 = get_unaligned_le16(uuid + 6);
+       data2 = get_unaligned_le16(uuid + 8);
+       data1 = get_unaligned_le16(uuid + 10);
+       data0 = get_unaligned_le32(uuid + 12);
 
-       seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n",
-                  ntohl(data0), ntohs(data1), ntohs(data2), ntohs(data3),
-                  ntohl(data4), ntohs(data5));
+       seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
+                  data0, data1, data2, data3, data4, data5);
 }
 
 static int uuids_show(struct seq_file *f, void *p)
index 22e6583..7c7e932 100644 (file)
@@ -1527,17 +1527,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
        BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
 
        switch (hcon->type) {
-       case AMP_LINK:
-               conn->mtu = hcon->hdev->block_mtu;
-               break;
-
        case LE_LINK:
                if (hcon->hdev->le_mtu) {
                        conn->mtu = hcon->hdev->le_mtu;
                        break;
                }
                /* fall through */
-
        default:
                conn->mtu = hcon->hdev->acl_mtu;
                break;
index f559b96..39395c7 100644 (file)
@@ -35,7 +35,7 @@
 bool enable_hs;
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  2
+#define MGMT_REVISION  3
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -435,35 +435,117 @@ static u32 get_current_settings(struct hci_dev *hdev)
 
 #define PNP_INFO_SVCLASS_ID            0x1200
 
-static u8 bluetooth_base_uuid[] = {
-                       0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
-                       0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
+static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+       u8 *ptr = data, *uuids_start = NULL;
+       struct bt_uuid *uuid;
+
+       if (len < 4)
+               return ptr;
+
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               u16 uuid16;
+
+               if (uuid->size != 16)
+                       continue;
+
+               uuid16 = get_unaligned_le16(&uuid->uuid[12]);
+               if (uuid16 < 0x1100)
+                       continue;
+
+               if (uuid16 == PNP_INFO_SVCLASS_ID)
+                       continue;
 
-static u16 get_uuid16(u8 *uuid128)
+               if (!uuids_start) {
+                       uuids_start = ptr;
+                       uuids_start[0] = 1;
+                       uuids_start[1] = EIR_UUID16_ALL;
+                       ptr += 2;
+               }
+
+               /* Stop if not enough space to put next UUID */
+               if ((ptr - data) + sizeof(u16) > len) {
+                       uuids_start[1] = EIR_UUID16_SOME;
+                       break;
+               }
+
+               *ptr++ = (uuid16 & 0x00ff);
+               *ptr++ = (uuid16 & 0xff00) >> 8;
+               uuids_start[0] += sizeof(uuid16);
+       }
+
+       return ptr;
+}
+
+static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
 {
-       u32 val;
-       int i;
+       u8 *ptr = data, *uuids_start = NULL;
+       struct bt_uuid *uuid;
+
+       if (len < 6)
+               return ptr;
 
-       for (i = 0; i < 12; i++) {
-               if (bluetooth_base_uuid[i] != uuid128[i])
-                       return 0;
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               if (uuid->size != 32)
+                       continue;
+
+               if (!uuids_start) {
+                       uuids_start = ptr;
+                       uuids_start[0] = 1;
+                       uuids_start[1] = EIR_UUID32_ALL;
+                       ptr += 2;
+               }
+
+               /* Stop if not enough space to put next UUID */
+               if ((ptr - data) + sizeof(u32) > len) {
+                       uuids_start[1] = EIR_UUID32_SOME;
+                       break;
+               }
+
+               memcpy(ptr, &uuid->uuid[12], sizeof(u32));
+               ptr += sizeof(u32);
+               uuids_start[0] += sizeof(u32);
        }
 
-       val = get_unaligned_le32(&uuid128[12]);
-       if (val > 0xffff)
-               return 0;
+       return ptr;
+}
+
+static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
+{
+       u8 *ptr = data, *uuids_start = NULL;
+       struct bt_uuid *uuid;
+
+       if (len < 18)
+               return ptr;
 
-       return (u16) val;
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               if (uuid->size != 128)
+                       continue;
+
+               if (!uuids_start) {
+                       uuids_start = ptr;
+                       uuids_start[0] = 1;
+                       uuids_start[1] = EIR_UUID128_ALL;
+                       ptr += 2;
+               }
+
+               /* Stop if not enough space to put next UUID */
+               if ((ptr - data) + 16 > len) {
+                       uuids_start[1] = EIR_UUID128_SOME;
+                       break;
+               }
+
+               memcpy(ptr, uuid->uuid, 16);
+               ptr += 16;
+               uuids_start[0] += 16;
+       }
+
+       return ptr;
 }
 
 static void create_eir(struct hci_dev *hdev, u8 *data)
 {
        u8 *ptr = data;
-       u16 eir_len = 0;
-       u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
-       int i, truncated = 0;
-       struct bt_uuid *uuid;
        size_t name_len;
 
        name_len = strlen(hdev->dev_name);
@@ -481,7 +563,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
 
                memcpy(ptr + 2, hdev->dev_name, name_len);
 
-               eir_len += (name_len + 2);
                ptr += (name_len + 2);
        }
 
@@ -490,7 +571,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
                ptr[1] = EIR_TX_POWER;
                ptr[2] = (u8) hdev->inq_tx_power;
 
-               eir_len += 3;
                ptr += 3;
        }
 
@@ -503,60 +583,12 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
                put_unaligned_le16(hdev->devid_product, ptr + 6);
                put_unaligned_le16(hdev->devid_version, ptr + 8);
 
-               eir_len += 10;
                ptr += 10;
        }
 
-       memset(uuid16_list, 0, sizeof(uuid16_list));
-
-       /* Group all UUID16 types */
-       list_for_each_entry(uuid, &hdev->uuids, list) {
-               u16 uuid16;
-
-               uuid16 = get_uuid16(uuid->uuid);
-               if (uuid16 == 0)
-                       return;
-
-               if (uuid16 < 0x1100)
-                       continue;
-
-               if (uuid16 == PNP_INFO_SVCLASS_ID)
-                       continue;
-
-               /* Stop if not enough space to put next UUID */
-               if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
-                       truncated = 1;
-                       break;
-               }
-
-               /* Check for duplicates */
-               for (i = 0; uuid16_list[i] != 0; i++)
-                       if (uuid16_list[i] == uuid16)
-                               break;
-
-               if (uuid16_list[i] == 0) {
-                       uuid16_list[i] = uuid16;
-                       eir_len += sizeof(u16);
-               }
-       }
-
-       if (uuid16_list[0] != 0) {
-               u8 *length = ptr;
-
-               /* EIR Data type */
-               ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
-
-               ptr += 2;
-               eir_len += 2;
-
-               for (i = 0; uuid16_list[i] != 0; i++) {
-                       *ptr++ = (uuid16_list[i] & 0x00ff);
-                       *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
-               }
-
-               /* EIR Data length */
-               *length = (i * sizeof(u16)) + 1;
-       }
+       ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+       ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
+       ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
 }
 
 static int update_eir(struct hci_dev *hdev)
@@ -728,13 +760,9 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
                                            void *data),
                                 void *data)
 {
-       struct list_head *p, *n;
-
-       list_for_each_safe(p, n, &hdev->mgmt_pending) {
-               struct pending_cmd *cmd;
-
-               cmd = list_entry(p, struct pending_cmd, list);
+       struct pending_cmd *cmd, *tmp;
 
+       list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
                if (opcode > 0 && cmd->opcode != opcode)
                        continue;
 
@@ -777,14 +805,19 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("request for %s", hdev->name);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        hci_dev_lock(hdev);
 
        if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
                cancel_delayed_work(&hdev->power_off);
 
                if (cp->val) {
-                       err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
-                       mgmt_powered(hdev, 1);
+                       mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev,
+                                        data, len);
+                       err = mgmt_powered(hdev, 1);
                        goto failed;
                }
        }
@@ -807,9 +840,9 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
        if (cp->val)
-               schedule_work(&hdev->power_on);
+               queue_work(hdev->req_workqueue, &hdev->power_on);
        else
-               schedule_work(&hdev->power_off.work);
+               queue_work(hdev->req_workqueue, &hdev->power_off.work);
 
        err = 0;
 
@@ -872,6 +905,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
                                 MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        timeout = __le16_to_cpu(cp->timeout);
        if (!cp->val && timeout > 0)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
@@ -971,6 +1008,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
@@ -1041,6 +1082,10 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("request for %s", hdev->name);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PAIRABLE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        hci_dev_lock(hdev);
 
        if (cp->val)
@@ -1073,6 +1118,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
@@ -1133,13 +1182,15 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        BT_DBG("request for %s", hdev->name);
 
-       hci_dev_lock(hdev);
+       if (!lmp_ssp_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
+                                 MGMT_STATUS_NOT_SUPPORTED);
 
-       if (!lmp_ssp_capable(hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
-                                MGMT_STATUS_NOT_SUPPORTED);
-               goto failed;
-       }
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
 
        val = !!cp->val;
 
@@ -1199,6 +1250,10 @@ static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        if (cp->val)
                set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
        else
@@ -1217,13 +1272,15 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        BT_DBG("request for %s", hdev->name);
 
-       hci_dev_lock(hdev);
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+                                 MGMT_STATUS_NOT_SUPPORTED);
 
-       if (!lmp_le_capable(hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
-                                MGMT_STATUS_NOT_SUPPORTED);
-               goto unlock;
-       }
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
 
        val = !!cp->val;
        enabled = lmp_host_le_capable(hdev);
@@ -1275,6 +1332,25 @@ unlock:
        return err;
 }
 
+static const u8 bluetooth_base_uuid[] = {
+                       0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+                       0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static u8 get_uuid_size(const u8 *uuid)
+{
+       u32 val;
+
+       if (memcmp(uuid, bluetooth_base_uuid, 12))
+               return 128;
+
+       val = get_unaligned_le32(&uuid[12]);
+       if (val > 0xffff)
+               return 32;
+
+       return 16;
+}
+
 static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 {
        struct mgmt_cp_add_uuid *cp = data;
@@ -1300,8 +1376,9 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        memcpy(uuid->uuid, cp->uuid, 16);
        uuid->svc_hint = cp->svc_hint;
+       uuid->size = get_uuid_size(cp->uuid);
 
-       list_add(&uuid->list, &hdev->uuids);
+       list_add_tail(&uuid->list, &hdev->uuids);
 
        err = update_class(hdev);
        if (err < 0)
@@ -1332,7 +1409,8 @@ static bool enable_service_cache(struct hci_dev *hdev)
                return false;
 
        if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
-               schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT);
+               queue_delayed_work(hdev->workqueue, &hdev->service_cache,
+                                  CACHE_TIMEOUT);
                return true;
        }
 
@@ -1344,7 +1422,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
 {
        struct mgmt_cp_remove_uuid *cp = data;
        struct pending_cmd *cmd;
-       struct list_head *p, *n;
+       struct bt_uuid *match, *tmp;
        u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int err, found;
 
@@ -1372,9 +1450,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
 
        found = 0;
 
-       list_for_each_safe(p, n, &hdev->uuids) {
-               struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
-
+       list_for_each_entry_safe(match, tmp, &hdev->uuids, list) {
                if (memcmp(match->uuid, cp->uuid, 16) != 0)
                        continue;
 
@@ -1422,13 +1498,19 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("request for %s", hdev->name);
 
-       hci_dev_lock(hdev);
+       if (!lmp_bredr_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
 
-       if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
-                                MGMT_STATUS_BUSY);
-               goto unlock;
-       }
+       if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+                                 MGMT_STATUS_BUSY);
+
+       if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
 
        hdev->major_class = cp->major;
        hdev->minor_class = cp->minor;
@@ -1483,9 +1565,21 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
                                  MGMT_STATUS_INVALID_PARAMS);
        }
 
+       if (cp->debug_keys != 0x00 && cp->debug_keys != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys,
               key_count);
 
+       for (i = 0; i < key_count; i++) {
+               struct mgmt_link_key_info *key = &cp->keys[i];
+
+               if (key->addr.type != BDADDR_BREDR)
+                       return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+                                         MGMT_STATUS_INVALID_PARAMS);
+       }
+
        hci_dev_lock(hdev);
 
        hci_link_keys_clear(hdev);
@@ -1533,12 +1627,22 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        struct hci_conn *conn;
        int err;
 
-       hci_dev_lock(hdev);
-
        memset(&rp, 0, sizeof(rp));
        bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
        rp.addr.type = cp->addr.type;
 
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
+       if (cp->disconnect != 0x00 && cp->disconnect != 0x01)
+               return cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
+       hci_dev_lock(hdev);
+
        if (!hdev_is_powered(hdev)) {
                err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
                                   MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
@@ -1596,6 +1700,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
                      u16 len)
 {
        struct mgmt_cp_disconnect *cp = data;
+       struct mgmt_rp_disconnect rp;
        struct hci_cp_disconnect dc;
        struct pending_cmd *cmd;
        struct hci_conn *conn;
@@ -1603,17 +1708,26 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("");
 
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
        hci_dev_lock(hdev);
 
        if (!test_bit(HCI_UP, &hdev->flags)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
-                                MGMT_STATUS_NOT_POWERED);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+                                  MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
                goto failed;
        }
 
        if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
-                                MGMT_STATUS_BUSY);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+                                  MGMT_STATUS_BUSY, &rp, sizeof(rp));
                goto failed;
        }
 
@@ -1624,8 +1738,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
                conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
 
        if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
-                                MGMT_STATUS_NOT_CONNECTED);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT,
+                                  MGMT_STATUS_NOT_CONNECTED, &rp, sizeof(rp));
                goto failed;
        }
 
@@ -1903,11 +2017,20 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("");
 
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
        hci_dev_lock(hdev);
 
        if (!hdev_is_powered(hdev)) {
-               err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
-                                MGMT_STATUS_NOT_POWERED);
+               err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
+                                  MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
                goto unlock;
        }
 
@@ -1924,10 +2047,6 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr,
                                   cp->addr.type, sec_level, auth_type);
 
-       memset(&rp, 0, sizeof(rp));
-       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
-       rp.addr.type = cp->addr.type;
-
        if (IS_ERR(conn)) {
                int status;
 
@@ -2254,24 +2373,16 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
 
        hci_dev_lock(hdev);
 
-       if (!hdev_is_powered(hdev)) {
-               err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
-                                  MGMT_STATUS_NOT_POWERED, &cp->addr,
-                                  sizeof(cp->addr));
-               goto unlock;
-       }
-
        err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
                                      cp->randomizer);
        if (err < 0)
                status = MGMT_STATUS_FAILED;
        else
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
                           &cp->addr, sizeof(cp->addr));
 
-unlock:
        hci_dev_unlock(hdev);
        return err;
 }
@@ -2287,24 +2398,15 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
 
        hci_dev_lock(hdev);
 
-       if (!hdev_is_powered(hdev)) {
-               err = cmd_complete(sk, hdev->id,
-                                  MGMT_OP_REMOVE_REMOTE_OOB_DATA,
-                                  MGMT_STATUS_NOT_POWERED, &cp->addr,
-                                  sizeof(cp->addr));
-               goto unlock;
-       }
-
        err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
        if (err < 0)
                status = MGMT_STATUS_INVALID_PARAMS;
        else
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
                           status, &cp->addr, sizeof(cp->addr));
 
-unlock:
        hci_dev_unlock(hdev);
        return err;
 }
@@ -2365,31 +2467,45 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
 
        switch (hdev->discovery.type) {
        case DISCOV_TYPE_BREDR:
-               if (lmp_bredr_capable(hdev))
-                       err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
-               else
-                       err = -ENOTSUPP;
+               if (!lmp_bredr_capable(hdev)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_NOT_SUPPORTED);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
                break;
 
        case DISCOV_TYPE_LE:
-               if (lmp_host_le_capable(hdev))
-                       err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
-                                         LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
-               else
-                       err = -ENOTSUPP;
+               if (!lmp_host_le_capable(hdev)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_NOT_SUPPORTED);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
+                                 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
                break;
 
        case DISCOV_TYPE_INTERLEAVED:
-               if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev))
-                       err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
-                                         LE_SCAN_WIN,
-                                         LE_SCAN_TIMEOUT_BREDR_LE);
-               else
-                       err = -ENOTSUPP;
+               if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) {
+                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                        MGMT_STATUS_NOT_SUPPORTED);
+                       mgmt_pending_remove(cmd);
+                       goto failed;
+               }
+
+               err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, LE_SCAN_WIN,
+                                 LE_SCAN_TIMEOUT_BREDR_LE);
                break;
 
        default:
-               err = -EINVAL;
+               err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+                                MGMT_STATUS_INVALID_PARAMS);
+               mgmt_pending_remove(cmd);
+               goto failed;
        }
 
        if (err < 0)
@@ -2510,7 +2626,8 @@ static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
                hci_inquiry_cache_update_resolve(hdev, e);
        }
 
-       err = 0;
+       err = cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 0, &cp->addr,
+                          sizeof(cp->addr));
 
 failed:
        hci_dev_unlock(hdev);
@@ -2526,13 +2643,18 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("%s", hdev->name);
 
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &cp->addr, sizeof(cp->addr));
+
        hci_dev_lock(hdev);
 
        err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
        if (err < 0)
                status = MGMT_STATUS_FAILED;
        else
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
                           &cp->addr, sizeof(cp->addr));
@@ -2551,13 +2673,18 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("%s", hdev->name);
 
+       if (!bdaddr_type_is_valid(cp->addr.type))
+               return cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &cp->addr, sizeof(cp->addr));
+
        hci_dev_lock(hdev);
 
        err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
        if (err < 0)
                status = MGMT_STATUS_INVALID_PARAMS;
        else
-               status = 0;
+               status = MGMT_STATUS_SUCCESS;
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
                           &cp->addr, sizeof(cp->addr));
@@ -2612,6 +2739,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
                return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
        if (!hdev_is_powered(hdev))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
                                  MGMT_STATUS_NOT_POWERED);
@@ -2659,12 +2790,23 @@ done:
        return err;
 }
 
+static bool ltk_is_valid(struct mgmt_ltk_info *key)
+{
+       if (key->authenticated != 0x00 && key->authenticated != 0x01)
+               return false;
+       if (key->master != 0x00 && key->master != 0x01)
+               return false;
+       if (!bdaddr_type_is_le(key->addr.type))
+               return false;
+       return true;
+}
+
 static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                               void *cp_data, u16 len)
 {
        struct mgmt_cp_load_long_term_keys *cp = cp_data;
        u16 key_count, expected_len;
-       int i;
+       int i, err;
 
        key_count = __le16_to_cpu(cp->key_count);
 
@@ -2674,11 +2816,20 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                BT_ERR("load_keys: expected %u bytes, got %u bytes",
                       len, expected_len);
                return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
-                                 EINVAL);
+                                 MGMT_STATUS_INVALID_PARAMS);
        }
 
        BT_DBG("%s key_count %u", hdev->name, key_count);
 
+       for (i = 0; i < key_count; i++) {
+               struct mgmt_ltk_info *key = &cp->keys[i];
+
+               if (!ltk_is_valid(key))
+                       return cmd_status(sk, hdev->id,
+                                         MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                         MGMT_STATUS_INVALID_PARAMS);
+       }
+
        hci_dev_lock(hdev);
 
        hci_smp_ltks_clear(hdev);
@@ -2698,9 +2849,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                            key->enc_size, key->ediv, key->rand);
        }
 
+       err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
+                          NULL, 0);
+
        hci_dev_unlock(hdev);
 
-       return 0;
+       return err;
 }
 
 static const struct mgmt_handler {
@@ -2915,6 +3069,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
        mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
 
        if (powered) {
+               u8 link_sec;
+
                if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
                    !lmp_host_ssp_capable(hdev)) {
                        u8 ssp = 1;
@@ -2938,6 +3094,11 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
                                             sizeof(cp), &cp);
                }
 
+               link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
+               if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
+                       hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE,
+                                    sizeof(link_sec), &link_sec);
+
                if (lmp_bredr_capable(hdev)) {
                        set_bredr_scan(hdev);
                        update_class(hdev);
@@ -2946,7 +3107,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
                }
        } else {
                u8 status = MGMT_STATUS_NOT_POWERED;
+               u8 zero_cod[] = { 0, 0, 0 };
+
                mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+
+               if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
+                       mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
+                                  zero_cod, sizeof(zero_cod), NULL);
        }
 
        err = new_settings(hdev, match.sk);
index 57f250c..b5178d6 100644 (file)
@@ -900,8 +900,6 @@ static void sco_conn_ready(struct sco_conn *conn)
 
        BT_DBG("conn %p", conn);
 
-       sco_conn_lock(conn);
-
        if (sk) {
                sco_sock_clear_timer(sk);
                bh_lock_sock(sk);
@@ -909,9 +907,13 @@ static void sco_conn_ready(struct sco_conn *conn)
                sk->sk_state_change(sk);
                bh_unlock_sock(sk);
        } else {
+               sco_conn_lock(conn);
+
                parent = sco_get_sock_listen(conn->src);
-               if (!parent)
-                       goto done;
+               if (!parent) {
+                       sco_conn_unlock(conn);
+                       return;
+               }
 
                bh_lock_sock(parent);
 
@@ -919,7 +921,8 @@ static void sco_conn_ready(struct sco_conn *conn)
                                    BTPROTO_SCO, GFP_ATOMIC);
                if (!sk) {
                        bh_unlock_sock(parent);
-                       goto done;
+                       sco_conn_unlock(conn);
+                       return;
                }
 
                sco_sock_init(sk, parent);
@@ -939,10 +942,9 @@ static void sco_conn_ready(struct sco_conn *conn)
                parent->sk_data_ready(parent, 1);
 
                bh_unlock_sock(parent);
-       }
 
-done:
-       sco_conn_unlock(conn);
+               sco_conn_unlock(conn);
+       }
 }
 
 /* ----- SCO interface with lower layer (HCI) ----- */
index 808338a..31bf258 100644 (file)
@@ -83,8 +83,8 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
        if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
                             &sta->sta, tid, NULL, 0))
                sdata_info(sta->sdata,
-                          "HW problem - can not stop rx aggregation for tid %d\n",
-                          tid);
+                          "HW problem - can not stop rx aggregation for %pM tid %d\n",
+                          sta->sta.addr, tid);
 
        /* check if this is a self generated aggregation halt */
        if (initiator == WLAN_BACK_RECIPIENT && tx)
@@ -159,7 +159,8 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
        }
        rcu_read_unlock();
 
-       ht_dbg(sta->sdata, "rx session timer expired on tid %d\n", (u16)*ptid);
+       ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n",
+              sta->sta.addr, (u16)*ptid);
 
        set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired);
        ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
@@ -247,7 +248,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
        status = WLAN_STATUS_REQUEST_DECLINED;
 
        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
-               ht_dbg(sta->sdata, "Suspend in progress - Denying ADDBA request\n");
+               ht_dbg(sta->sdata,
+                      "Suspend in progress - Denying ADDBA request (%pM tid %d)\n",
+                      sta->sta.addr, tid);
                goto end_no_lock;
        }
 
@@ -317,7 +320,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
 
        ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
                               &sta->sta, tid, &start_seq_num, 0);
-       ht_dbg(sta->sdata, "Rx A-MPDU request on tid %d result %d\n", tid, ret);
+       ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
+              sta->sta.addr, tid, ret);
        if (ret) {
                kfree(tid_agg_rx->reorder_buf);
                kfree(tid_agg_rx->reorder_time);
index 2f0ccbc..13b7683 100644 (file)
@@ -296,7 +296,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                                       IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
                                       &sta->sta, tid, NULL, 0);
                WARN_ON_ONCE(ret);
-               goto remove_tid_tx;
+               return 0;
        }
 
        if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
@@ -354,12 +354,15 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                 */
        }
 
-       if (reason == AGG_STOP_DESTROY_STA) {
- remove_tid_tx:
-               spin_lock_bh(&sta->lock);
-               ieee80211_remove_tid_tx(sta, tid);
-               spin_unlock_bh(&sta->lock);
-       }
+       /*
+        * In the case of AGG_STOP_DESTROY_STA, the driver won't
+        * necessarily call ieee80211_stop_tx_ba_cb(), so this may
+        * seem like we can leave the tid_tx data pending forever.
+        * This is true, in a way, but "forever" is only until the
+        * station struct is actually destroyed. In the meantime,
+        * leaving it around ensures that we don't transmit packets
+        * to the driver on this TID which might confuse it.
+        */
 
        return 0;
 }
@@ -387,12 +390,13 @@ static void sta_addba_resp_timer_expired(unsigned long data)
            test_bit(HT_AGG_STATE_RESPONSE_RECEIVED, &tid_tx->state)) {
                rcu_read_unlock();
                ht_dbg(sta->sdata,
-                      "timer expired on tid %d but we are not (or no longer) expecting addBA response there\n",
-                      tid);
+                      "timer expired on %pM tid %d but we are not (or no longer) expecting addBA response there\n",
+                      sta->sta.addr, tid);
                return;
        }
 
-       ht_dbg(sta->sdata, "addBA response timer expired on tid %d\n", tid);
+       ht_dbg(sta->sdata, "addBA response timer expired on %pM tid %d\n",
+              sta->sta.addr, tid);
 
        ieee80211_stop_tx_ba_session(&sta->sta, tid);
        rcu_read_unlock();
@@ -429,7 +433,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
                               &sta->sta, tid, &start_seq_num, 0);
        if (ret) {
                ht_dbg(sdata,
-                      "BA request denied - HW unavailable for tid %d\n", tid);
+                      "BA request denied - HW unavailable for %pM tid %d\n",
+                      sta->sta.addr, tid);
                spin_lock_bh(&sta->lock);
                ieee80211_agg_splice_packets(sdata, tid_tx, tid);
                ieee80211_assign_tid_tx(sta, tid, NULL);
@@ -442,7 +447,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
 
        /* activate the timer for the recipient's addBA response */
        mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL);
-       ht_dbg(sdata, "activated addBA response timer on tid %d\n", tid);
+       ht_dbg(sdata, "activated addBA response timer on %pM tid %d\n",
+              sta->sta.addr, tid);
 
        spin_lock_bh(&sta->lock);
        sta->ampdu_mlme.last_addba_req_time[tid] = jiffies;
@@ -489,7 +495,8 @@ static void sta_tx_agg_session_timer_expired(unsigned long data)
 
        rcu_read_unlock();
 
-       ht_dbg(sta->sdata, "tx session timer expired on tid %d\n", (u16)*ptid);
+       ht_dbg(sta->sdata, "tx session timer expired on %pM tid %d\n",
+              sta->sta.addr, (u16)*ptid);
 
        ieee80211_stop_tx_ba_session(&sta->sta, *ptid);
 }
@@ -525,7 +532,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
 
        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
                ht_dbg(sdata,
-                      "BA sessions blocked - Denying BA session request\n");
+                      "BA sessions blocked - Denying BA session request %pM tid %d\n",
+                      sta->sta.addr, tid);
                return -EINVAL;
        }
 
@@ -566,8 +574,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
            time_before(jiffies, sta->ampdu_mlme.last_addba_req_time[tid] +
                        HT_AGG_RETRIES_PERIOD)) {
                ht_dbg(sdata,
-                      "BA request denied - waiting a grace period after %d failed requests on tid %u\n",
-                      sta->ampdu_mlme.addba_req_num[tid], tid);
+                      "BA request denied - waiting a grace period after %d failed requests on %pM tid %u\n",
+                      sta->ampdu_mlme.addba_req_num[tid], sta->sta.addr, tid);
                ret = -EBUSY;
                goto err_unlock_sta;
        }
@@ -576,8 +584,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
        /* check if the TID is not in aggregation flow already */
        if (tid_tx || sta->ampdu_mlme.tid_start_tx[tid]) {
                ht_dbg(sdata,
-                      "BA request denied - session is not idle on tid %u\n",
-                      tid);
+                      "BA request denied - session is not idle on %pM tid %u\n",
+                      sta->sta.addr, tid);
                ret = -EAGAIN;
                goto err_unlock_sta;
        }
@@ -632,7 +640,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
 
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
 
-       ht_dbg(sta->sdata, "Aggregation is on for tid %d\n", tid);
+       ht_dbg(sta->sdata, "Aggregation is on for %pM tid %d\n",
+              sta->sta.addr, tid);
 
        drv_ampdu_action(local, sta->sdata,
                         IEEE80211_AMPDU_TX_OPERATIONAL,
@@ -802,7 +811,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
 
        if (!tid_tx || !test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
-               ht_dbg(sdata, "unexpected callback to A-MPDU stop\n");
+               ht_dbg(sdata,
+                      "unexpected callback to A-MPDU stop for %pM tid %d\n",
+                      sta->sta.addr, tid);
                goto unlock_sta;
        }
 
@@ -861,13 +872,15 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
                goto out;
 
        if (mgmt->u.action.u.addba_resp.dialog_token != tid_tx->dialog_token) {
-               ht_dbg(sta->sdata, "wrong addBA response token, tid %d\n", tid);
+               ht_dbg(sta->sdata, "wrong addBA response token, %pM tid %d\n",
+                      sta->sta.addr, tid);
                goto out;
        }
 
        del_timer_sync(&tid_tx->addba_resp_timer);
 
-       ht_dbg(sta->sdata, "switched off addBA timer for tid %d\n", tid);
+       ht_dbg(sta->sdata, "switched off addBA timer for %pM tid %d\n",
+              sta->sta.addr, tid);
 
        /*
         * addba_resp_timer may have fired before we got here, and
@@ -877,8 +890,8 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
        if (test_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state) ||
            test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
                ht_dbg(sta->sdata,
-                      "got addBA resp for tid %d but we already gave up\n",
-                      tid);
+                      "got addBA resp for %pM tid %d but we already gave up\n",
+                      sta->sta.addr, tid);
                goto out;
        }
 
index 0c07f94..434b3c4 100644 (file)
@@ -569,7 +569,8 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local,
        check_sdata_in_driver(sdata);
 
        WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
-               sdata->vif.type != NL80211_IFTYPE_ADHOC);
+               (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                sdata->vif.type != NL80211_IFTYPE_MESH_POINT));
 
        trace_drv_sta_rc_update(local, sdata, sta, changed);
        if (local->ops->sta_rc_update)
@@ -845,11 +846,12 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local,
 }
 
 static inline void drv_rssi_callback(struct ieee80211_local *local,
+                                    struct ieee80211_sub_if_data *sdata,
                                     const enum ieee80211_rssi_event event)
 {
-       trace_drv_rssi_callback(local, event);
+       trace_drv_rssi_callback(local, sdata, event);
        if (local->ops->rssi_callback)
-               local->ops->rssi_callback(&local->hw, event);
+               local->ops->rssi_callback(&local->hw, &sdata->vif, event);
        trace_drv_return_void(local);
 }
 
@@ -1020,4 +1022,32 @@ static inline void drv_restart_complete(struct ieee80211_local *local)
        trace_drv_return_void(local);
 }
 
+static inline void
+drv_set_default_unicast_key(struct ieee80211_local *local,
+                           struct ieee80211_sub_if_data *sdata,
+                           int key_idx)
+{
+       check_sdata_in_driver(sdata);
+
+       WARN_ON_ONCE(key_idx < -1 || key_idx > 3);
+
+       trace_drv_set_default_unicast_key(local, sdata, key_idx);
+       if (local->ops->set_default_unicast_key)
+               local->ops->set_default_unicast_key(&local->hw, &sdata->vif,
+                                                   key_idx);
+       trace_drv_return_void(local);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
+                                       struct ieee80211_sub_if_data *sdata,
+                                       struct inet6_dev *idev)
+{
+       trace_drv_ipv6_addr_change(local, sdata);
+       if (local->ops->ipv6_addr_change)
+               local->ops->ipv6_addr_change(&local->hw, &sdata->vif, idev);
+       trace_drv_return_void(local);
+}
+#endif
+
 #endif /* __MAC80211_DRIVER_OPS */
index 63f0430..5fba867 100644 (file)
@@ -747,8 +747,6 @@ struct ieee80211_sub_if_data {
        struct work_struct work;
        struct sk_buff_head skb_queue;
 
-       bool arp_filter_state;
-
        u8 needed_rx_chains;
        enum ieee80211_smps_mode smps_mode;
 
@@ -1129,6 +1127,7 @@ struct ieee80211_local {
        struct timer_list dynamic_ps_timer;
        struct notifier_block network_latency_notifier;
        struct notifier_block ifa_notifier;
+       struct notifier_block ifa6_notifier;
 
        /*
         * The dynamic ps timeout configured from user space via WEXT -
index 06fac29..0a36dc6 100644 (file)
@@ -1574,9 +1574,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        /* initialise type-independent data */
        sdata->wdev.wiphy = local->hw.wiphy;
        sdata->local = local;
-#ifdef CONFIG_INET
-       sdata->arp_filter_state = true;
-#endif
 
        for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
                skb_queue_head_init(&sdata->fragments[i].skb_list);
index 619c5d6..ef252eb 100644 (file)
@@ -204,8 +204,11 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
        if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
                key = key_mtx_dereference(sdata->local, sdata->keys[idx]);
 
-       if (uni)
+       if (uni) {
                rcu_assign_pointer(sdata->default_unicast_key, key);
+               drv_set_default_unicast_key(sdata->local, sdata, idx);
+       }
+
        if (multi)
                rcu_assign_pointer(sdata->default_multicast_key, key);
 
index 39cfe8f..2bdd454 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/inetdevice.h>
 #include <net/net_namespace.h>
 #include <net/cfg80211.h>
+#include <net/addrconf.h>
 
 #include "ieee80211_i.h"
 #include "driver-ops.h"
@@ -349,27 +350,19 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
 
        /* Copy the addresses to the bss_conf list */
        ifa = idev->ifa_list;
-       while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) {
-               bss_conf->arp_addr_list[c] = ifa->ifa_address;
+       while (ifa) {
+               if (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN)
+                       bss_conf->arp_addr_list[c] = ifa->ifa_address;
                ifa = ifa->ifa_next;
                c++;
        }
 
-       /* If not all addresses fit the list, disable filtering */
-       if (ifa) {
-               sdata->arp_filter_state = false;
-               c = 0;
-       } else {
-               sdata->arp_filter_state = true;
-       }
        bss_conf->arp_addr_cnt = c;
 
        /* Configure driver only if associated (which also implies it is up) */
-       if (ifmgd->associated) {
-               bss_conf->arp_filter_enabled = sdata->arp_filter_state;
+       if (ifmgd->associated)
                ieee80211_bss_info_change_notify(sdata,
                                                 BSS_CHANGED_ARP_FILTER);
-       }
 
        mutex_unlock(&ifmgd->mtx);
 
@@ -377,6 +370,37 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
 }
 #endif
 
+#if IS_ENABLED(CONFIG_IPV6)
+static int ieee80211_ifa6_changed(struct notifier_block *nb,
+                                 unsigned long data, void *arg)
+{
+       struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)arg;
+       struct inet6_dev *idev = ifa->idev;
+       struct net_device *ndev = ifa->idev->dev;
+       struct ieee80211_local *local =
+               container_of(nb, struct ieee80211_local, ifa6_notifier);
+       struct wireless_dev *wdev = ndev->ieee80211_ptr;
+       struct ieee80211_sub_if_data *sdata;
+
+       /* Make sure it's our interface that got changed */
+       if (!wdev || wdev->wiphy != local->hw.wiphy)
+               return NOTIFY_DONE;
+
+       sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
+
+       /*
+        * For now only support station mode. This is mostly because
+        * doing AP would have to handle AP_VLAN in some way ...
+        */
+       if (sdata->vif.type != NL80211_IFTYPE_STATION)
+               return NOTIFY_DONE;
+
+       drv_ipv6_addr_change(local, sdata, idev);
+
+       return NOTIFY_DONE;
+}
+#endif
+
 static int ieee80211_napi_poll(struct napi_struct *napi, int budget)
 {
        struct ieee80211_local *local =
@@ -985,12 +1009,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                goto fail_ifa;
 #endif
 
+#if IS_ENABLED(CONFIG_IPV6)
+       local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
+       result = register_inet6addr_notifier(&local->ifa6_notifier);
+       if (result)
+               goto fail_ifa6;
+#endif
+
        netif_napi_add(&local->napi_dev, &local->napi, ieee80211_napi_poll,
                        local->hw.napi_weight);
 
        return 0;
 
+#if IS_ENABLED(CONFIG_IPV6)
+ fail_ifa6:
 #ifdef CONFIG_INET
+       unregister_inetaddr_notifier(&local->ifa_notifier);
+#endif
+#endif
+#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
  fail_ifa:
        pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
                               &local->network_latency_notifier);
@@ -1026,6 +1063,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 #ifdef CONFIG_INET
        unregister_inetaddr_notifier(&local->ifa_notifier);
 #endif
+#if IS_ENABLED(CONFIG_IPV6)
+       unregister_inet6addr_notifier(&local->ifa6_notifier);
+#endif
 
        rtnl_lock();
 
index 9e04166..81e6126 100644 (file)
@@ -55,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
        sta->plink_retries = 0;
 }
 
-/*
- * Allocate mesh sta entry and insert into station table
- */
-static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
-                                        u8 *hw_addr)
-{
-       struct sta_info *sta;
-
-       if (sdata->local->num_sta >= MESH_MAX_PLINKS)
-               return NULL;
-
-       sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
-       if (!sta)
-               return NULL;
-
-       sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
-       sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
-       sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
-
-       set_sta_flag(sta, WLAN_STA_WME);
-
-       return sta;
-}
-
 /**
  * mesh_set_ht_prot_mode - set correct HT protection mode
  *
@@ -309,53 +285,27 @@ free:
        return err;
 }
 
-/**
- * mesh_peer_init - initialize new mesh peer and return resulting sta_info
- *
- * @sdata: local meshif
- * @addr: peer's address
- * @elems: IEs from beacon or mesh peering frame
- *
- * call under RCU
- */
-static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
-                                      u8 *addr,
-                                      struct ieee802_11_elems *elems)
+static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
+                              struct sta_info *sta,
+                              struct ieee802_11_elems *elems, bool insert)
 {
        struct ieee80211_local *local = sdata->local;
        enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_supported_band *sband;
-       u32 rates, basic_rates = 0;
-       struct sta_info *sta;
-       bool insert = false;
+       u32 rates, basic_rates = 0, changed = 0;
 
        sband = local->hw.wiphy->bands[band];
        rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates);
 
-       sta = sta_info_get(sdata, addr);
-       if (!sta) {
-               /* Userspace handles peer allocation when security is enabled */
-               if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
-                       cfg80211_notify_new_peer_candidate(sdata->dev, addr,
-                                                          elems->ie_start,
-                                                          elems->total_len,
-                                                          GFP_ATOMIC);
-                       return NULL;
-               }
-
-               sta = mesh_plink_alloc(sdata, addr);
-               if (!sta)
-                       return NULL;
-               insert = true;
-       }
-
        spin_lock_bh(&sta->lock);
        sta->last_rx = jiffies;
-       if (sta->plink_state == NL80211_PLINK_ESTAB) {
-               spin_unlock_bh(&sta->lock);
-               return sta;
-       }
 
+       /* rates and capabilities don't change during peering */
+       if (sta->plink_state == NL80211_PLINK_ESTAB)
+               goto out;
+
+       if (sta->sta.supp_rates[band] != rates)
+               changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
        sta->sta.supp_rates[band] = rates;
        if (elems->ht_cap_elem &&
            sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
@@ -374,27 +324,115 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
                                            ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
                ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
                                             elems->ht_operation, &chandef);
+               if (sta->ch_width != chandef.width)
+                       changed |= IEEE80211_RC_BW_CHANGED;
                sta->ch_width = chandef.width;
        }
 
        if (insert)
                rate_control_rate_init(sta);
+       else
+               rate_control_rate_update(local, sband, sta, changed);
+out:
        spin_unlock_bh(&sta->lock);
+}
+
+static struct sta_info *
+__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
+{
+       struct sta_info *sta;
 
-       if (insert && sta_info_insert(sta))
+       if (sdata->local->num_sta >= MESH_MAX_PLINKS)
                return NULL;
 
+       sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
+       if (!sta)
+               return NULL;
+
+       sta->plink_state = NL80211_PLINK_LISTEN;
+       init_timer(&sta->plink_timer);
+
+       sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
+       sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
+       sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
+
+       set_sta_flag(sta, WLAN_STA_WME);
+
+       return sta;
+}
+
+static struct sta_info *
+mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
+                   struct ieee802_11_elems *elems)
+{
+       struct sta_info *sta = NULL;
+
+       /* Userspace handles peer allocation when security is enabled */
+       if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
+               cfg80211_notify_new_peer_candidate(sdata->dev, addr,
+                                                  elems->ie_start,
+                                                  elems->total_len,
+                                                  GFP_KERNEL);
+       else
+               sta = __mesh_sta_info_alloc(sdata, addr);
+
+       return sta;
+}
+
+/*
+ * mesh_sta_info_get - return mesh sta info entry for @addr.
+ *
+ * @sdata: local meshif
+ * @addr: peer's address
+ * @elems: IEs from beacon or mesh peering frame.
+ *
+ * Return existing or newly allocated sta_info under RCU read lock.
+ * (re)initialize with given IEs.
+ */
+static struct sta_info *
+mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
+                 u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU)
+{
+       struct sta_info *sta = NULL;
+
+       rcu_read_lock();
+       sta = sta_info_get(sdata, addr);
+       if (sta) {
+               mesh_sta_info_init(sdata, sta, elems, false);
+       } else {
+               rcu_read_unlock();
+               /* can't run atomic */
+               sta = mesh_sta_info_alloc(sdata, addr, elems);
+               if (!sta) {
+                       rcu_read_lock();
+                       return NULL;
+               }
+
+               mesh_sta_info_init(sdata, sta, elems, true);
+
+               if (sta_info_insert_rcu(sta))
+                       return NULL;
+       }
+
        return sta;
 }
 
+/*
+ * mesh_neighbour_update - update or initialize new mesh neighbor.
+ *
+ * @sdata: local meshif
+ * @addr: peer's address
+ * @elems: IEs from beacon or mesh peering frame
+ *
+ * Initiates peering if appropriate.
+ */
 void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
                           u8 *hw_addr,
                           struct ieee802_11_elems *elems)
 {
        struct sta_info *sta;
 
-       rcu_read_lock();
-       sta = mesh_peer_init(sdata, hw_addr, elems);
+       sta = mesh_sta_info_get(sdata, hw_addr, elems);
        if (!sta)
                goto out;
 
@@ -632,6 +670,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
            (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
                memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);
 
+       /* WARNING: Only for sta pointer, is dropped & re-acquired */
        rcu_read_lock();
 
        sta = sta_info_get(sdata, mgmt->sa);
@@ -735,8 +774,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
        }
 
        if (event == OPN_ACPT) {
+               rcu_read_unlock();
                /* allocate sta entry if necessary and update info */
-               sta = mesh_peer_init(sdata, mgmt->sa, &elems);
+               sta = mesh_sta_info_get(sdata, mgmt->sa, &elems);
                if (!sta) {
                        mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
                        rcu_read_unlock();
index e930175..5913fb9 100644 (file)
@@ -1465,10 +1465,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                bss_info_changed |= BSS_CHANGED_CQM;
 
        /* Enable ARP filtering */
-       if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) {
-               bss_conf->arp_filter_enabled = sdata->arp_filter_state;
+       if (bss_conf->arp_addr_cnt)
                bss_info_changed |= BSS_CHANGED_ARP_FILTER;
-       }
 
        ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
@@ -1489,7 +1487,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
-       struct sta_info *sta;
        u32 changed = 0;
 
        ASSERT_MGD_MTX(ifmgd);
@@ -1521,14 +1518,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        netif_tx_stop_all_queues(sdata->dev);
        netif_carrier_off(sdata->dev);
 
-       mutex_lock(&local->sta_mtx);
-       sta = sta_info_get(sdata, ifmgd->bssid);
-       if (sta) {
-               set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-               ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
-       }
-       mutex_unlock(&local->sta_mtx);
-
        /*
         * if we want to get out of ps before disassoc (why?) we have
         * to do it before sending disassoc, as otherwise the null-packet
@@ -1582,10 +1571,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        cancel_work_sync(&local->dynamic_ps_enable_work);
 
        /* Disable ARP filtering */
-       if (sdata->vif.bss_conf.arp_filter_enabled) {
-               sdata->vif.bss_conf.arp_filter_enabled = false;
+       if (sdata->vif.bss_conf.arp_addr_cnt)
                changed |= BSS_CHANGED_ARP_FILTER;
-       }
 
        sdata->vif.bss_conf.qos = false;
        changed |= BSS_CHANGED_QOS;
@@ -2608,12 +2595,12 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                if (sig > ifmgd->rssi_max_thold &&
                    (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
                        ifmgd->last_ave_beacon_signal = sig;
-                       drv_rssi_callback(local, RSSI_EVENT_HIGH);
+                       drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH);
                } else if (sig < ifmgd->rssi_min_thold &&
                           (last_sig >= ifmgd->rssi_max_thold ||
                           last_sig == 0)) {
                        ifmgd->last_ave_beacon_signal = sig;
-                       drv_rssi_callback(local, RSSI_EVENT_LOW);
+                       drv_rssi_callback(local, sdata, RSSI_EVENT_LOW);
                }
        }
 
@@ -3169,23 +3156,22 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
-       if (!ifmgd->associated)
+       mutex_lock(&ifmgd->mtx);
+       if (!ifmgd->associated) {
+               mutex_unlock(&ifmgd->mtx);
                return;
+       }
 
        if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
                sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
-               mutex_lock(&ifmgd->mtx);
-               if (ifmgd->associated) {
-                       mlme_dbg(sdata,
-                                "driver requested disconnect after resume\n");
-                       ieee80211_sta_connection_lost(sdata,
-                               ifmgd->associated->bssid,
-                               WLAN_REASON_UNSPECIFIED);
-                       mutex_unlock(&ifmgd->mtx);
-                       return;
-               }
+               mlme_dbg(sdata, "driver requested disconnect after resume\n");
+               ieee80211_sta_connection_lost(sdata,
+                                             ifmgd->associated->bssid,
+                                             WLAN_REASON_UNSPECIFIED);
                mutex_unlock(&ifmgd->mtx);
+               return;
        }
+       mutex_unlock(&ifmgd->mtx);
 
        if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
                add_timer(&ifmgd->timer);
index 9d864ed..227233c 100644 (file)
@@ -380,11 +380,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 
-#ifdef CONFIG_MAC80211_MESH
-       sta->plink_state = NL80211_PLINK_LISTEN;
-       init_timer(&sta->plink_timer);
-#endif
-
        return sta;
 }
 
index 41861b9..6ca53d6 100644 (file)
@@ -347,8 +347,11 @@ TRACE_EVENT(drv_bss_info_changed,
                __field(s32, cqm_rssi_hyst);
                __field(u32, channel_width);
                __field(u32, channel_cfreq1);
-               __dynamic_array(u32, arp_addr_list, info->arp_addr_cnt);
-               __field(bool, arp_filter_enabled);
+               __dynamic_array(u32, arp_addr_list,
+                               info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ?
+                                       IEEE80211_BSS_ARP_ADDR_LIST_LEN :
+                                       info->arp_addr_cnt);
+               __field(int, arp_addr_cnt);
                __field(bool, qos);
                __field(bool, idle);
                __field(bool, ps);
@@ -384,9 +387,11 @@ TRACE_EVENT(drv_bss_info_changed,
                __entry->cqm_rssi_hyst = info->cqm_rssi_hyst;
                __entry->channel_width = info->chandef.width;
                __entry->channel_cfreq1 = info->chandef.center_freq1;
+               __entry->arp_addr_cnt = info->arp_addr_cnt;
                memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list,
-                      sizeof(u32) * info->arp_addr_cnt);
-               __entry->arp_filter_enabled = info->arp_filter_enabled;
+                      sizeof(u32) * (info->arp_addr_cnt > IEEE80211_BSS_ARP_ADDR_LIST_LEN ?
+                                       IEEE80211_BSS_ARP_ADDR_LIST_LEN :
+                                       info->arp_addr_cnt));
                __entry->qos = info->qos;
                __entry->idle = info->idle;
                __entry->ps = info->ps;
@@ -1184,23 +1189,26 @@ TRACE_EVENT(drv_set_rekey_data,
 
 TRACE_EVENT(drv_rssi_callback,
        TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
                 enum ieee80211_rssi_event rssi_event),
 
-       TP_ARGS(local, rssi_event),
+       TP_ARGS(local, sdata, rssi_event),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
+               VIF_ENTRY
                __field(u32, rssi_event)
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
+               VIF_ASSIGN;
                __entry->rssi_event = rssi_event;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT " rssi_event:%d",
-               LOCAL_PR_ARG, __entry->rssi_event
+               LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d",
+               LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event
        )
 );
 
@@ -1432,6 +1440,14 @@ DEFINE_EVENT(local_only_evt, drv_restart_complete,
        TP_ARGS(local)
 );
 
+#if IS_ENABLED(CONFIG_IPV6)
+DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+       TP_ARGS(local, sdata)
+);
+#endif
+
 /*
  * Tracing for API calls that drivers call.
  */
@@ -1821,6 +1837,29 @@ TRACE_EVENT(stop_queue,
        )
 );
 
+TRACE_EVENT(drv_set_default_unicast_key,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                int key_idx),
+
+       TP_ARGS(local, sdata, key_idx),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(int, key_idx)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->key_idx = key_idx;
+       ),
+
+       TP_printk(LOCAL_PR_FMT VIF_PR_FMT " key_idx:%d",
+                 LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx)
+);
+
 #ifdef CONFIG_MAC80211_MESSAGE_TRACING
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mac80211_msg
index f32d681..a2cb6a3 100644 (file)
@@ -1787,16 +1787,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                        break;
                /* fall through */
        case NL80211_IFTYPE_AP:
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+               if (!chanctx_conf)
+                       goto fail_rcu;
                fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
                /* DA BSSID SA */
                memcpy(hdr.addr1, skb->data, ETH_ALEN);
                memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
                memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
                hdrlen = 24;
-               if (sdata->vif.type == NL80211_IFTYPE_AP)
-                       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-               if (!chanctx_conf)
-                       goto fail_rcu;
                band = chanctx_conf->def.chan->band;
                break;
        case NL80211_IFTYPE_WDS:
index 9245729..40dbe37 100644 (file)
@@ -478,6 +478,11 @@ int wiphy_register(struct wiphy *wiphy)
                           ETH_ALEN)))
                return -EINVAL;
 
+       if (WARN_ON(wiphy->max_acl_mac_addrs &&
+                   (!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME) ||
+                    !rdev->ops->set_mac_acl)))
+               return -EINVAL;
+
        if (wiphy->addresses)
                memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
 
index 33de803..b5978ab 100644 (file)
@@ -365,6 +365,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
        [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
        [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
+       [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
+       [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
 };
 
 /* policy for the key attributes */
@@ -1268,6 +1270,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
                    dev->wiphy.ht_capa_mod_mask))
                goto nla_put_failure;
 
+       if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+           dev->wiphy.max_acl_mac_addrs &&
+           nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
+                       dev->wiphy.max_acl_mac_addrs))
+               goto nla_put_failure;
+
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -2491,6 +2499,97 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+/* This function returns an error or the number of nested attributes */
+static int validate_acl_mac_addrs(struct nlattr *nl_attr)
+{
+       struct nlattr *attr;
+       int n_entries = 0, tmp;
+
+       nla_for_each_nested(attr, nl_attr, tmp) {
+               if (nla_len(attr) != ETH_ALEN)
+                       return -EINVAL;
+
+               n_entries++;
+       }
+
+       return n_entries;
+}
+
+/*
+ * This function parses ACL information and allocates memory for ACL data.
+ * On successful return, the calling function is responsible to free the
+ * ACL buffer returned by this function.
+ */
+static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
+                                               struct genl_info *info)
+{
+       enum nl80211_acl_policy acl_policy;
+       struct nlattr *attr;
+       struct cfg80211_acl_data *acl;
+       int i = 0, n_entries, tmp;
+
+       if (!wiphy->max_acl_mac_addrs)
+               return ERR_PTR(-EOPNOTSUPP);
+
+       if (!info->attrs[NL80211_ATTR_ACL_POLICY])
+               return ERR_PTR(-EINVAL);
+
+       acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
+       if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
+           acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
+               return ERR_PTR(-EINVAL);
+
+       if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
+               return ERR_PTR(-EINVAL);
+
+       n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
+       if (n_entries < 0)
+               return ERR_PTR(n_entries);
+
+       if (n_entries > wiphy->max_acl_mac_addrs)
+               return ERR_PTR(-ENOTSUPP);
+
+       acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
+                     GFP_KERNEL);
+       if (!acl)
+               return ERR_PTR(-ENOMEM);
+
+       nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
+               memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
+               i++;
+       }
+
+       acl->n_acl_entries = n_entries;
+       acl->acl_policy = acl_policy;
+
+       return acl;
+}
+
+static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct cfg80211_acl_data *acl;
+       int err;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+
+       if (!dev->ieee80211_ptr->beacon_interval)
+               return -EINVAL;
+
+       acl = parse_acl_data(&rdev->wiphy, info);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+
+       err = rdev_set_mac_acl(rdev, dev, acl);
+
+       kfree(acl);
+
+       return err;
+}
+
 static int nl80211_parse_beacon(struct genl_info *info,
                                struct cfg80211_beacon_data *bcn)
 {
@@ -2734,6 +2833,12 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
+       if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
+               params.acl = parse_acl_data(&rdev->wiphy, info);
+               if (IS_ERR(params.acl))
+                       return PTR_ERR(params.acl);
+       }
+
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
                wdev->preset_chandef = params.chandef;
@@ -2742,6 +2847,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                wdev->ssid_len = params.ssid_len;
                memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
        }
+
+       kfree(params.acl);
+
        return err;
 }
 
@@ -7876,6 +7984,14 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_SET_MAC_ACL,
+               .doit = nl80211_set_mac_acl,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
index 6c0c819..422d382 100644 (file)
@@ -875,4 +875,16 @@ static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev,
        rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
        trace_rdev_return_void(&rdev->wiphy);
 }                                      
+
+static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
+                                  struct net_device *dev,
+                                  struct cfg80211_acl_data *params)
+{
+       int ret;
+
+       trace_rdev_set_mac_acl(&rdev->wiphy, dev, params);
+       ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
 #endif /* __CFG80211_RDEV_OPS */
index 2134576..8bc5531 100644 (file)
@@ -1767,6 +1767,24 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device,
        TP_ARGS(wiphy, wdev)
 );
 
+TRACE_EVENT(rdev_set_mac_acl,
+       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+                struct cfg80211_acl_data *params),
+       TP_ARGS(wiphy, netdev, params),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               NETDEV_ENTRY
+               __field(u32, acl_policy)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WIPHY_ASSIGN;
+               __entry->acl_policy = params->acl_policy;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", acl policy: %d",
+                 WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->acl_policy)
+);
+
 /*************************************************************
  *          cfg80211 exported functions traces              *
  *************************************************************/
index 1c2795d..d7873c7 100644 (file)
@@ -1212,7 +1212,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_WDS:
-               radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
+               radar_required = !!(chan &&
+                                   (chan->flags & IEEE80211_CHAN_RADAR));
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION: