aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2013-02-08 15:18:40 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-08 15:18:40 -0500
commitbfb235d78387c4fd38e296ed7bd9a4f2b0a5b2b9 (patch)
tree209d4fd6fb00e660c76ca8ac5d4caed59dbb9957
parentMerge branch 'tg3' (diff)
parentMerge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem (diff)
downloadlinux-dev-bfb235d78387c4fd38e296ed7bd9a4f2b0a5b2b9.tar.xz
linux-dev-bfb235d78387c4fd38e296ed7bd9a4f2b0a5b2b9.zip
Merge branch 'wireless'
John W. Linville says: ==================== Please accept this pull request intended for the 3.9 stream! Included are a mac80211 pull, an iwlwifi pull (actually two -- one was a fast-forward), a wl12xx pull, and a couple of Bluetooth pulls. On mac80211, Johannes says: "I've included * AKM definitions from Bing, * mesh fixes from Thomas, including a fix from him for me breaking his patch while applying, * channel check fix from Simon, * an old patch from Yoni Divinsky who doesn't even work for TI any more, to configure the WEP TX key for ARP offload etc. * MAC ACL API from Vasanth * a fix for the infamous chanctx_conf warning from Arnd * from myself, a fix for my previous aggregation changes, some cleanup and some improvements and fixes for WoWLAN" On iwlwifi, Johannes says: "Two small changes for iwlwifi-next, one to update all our Copyright notices and one to provide the RX page order." And also: "So what I have here is some cleanups, preparations and the new MVM (multi-virtual MAC) driver itself and (this is new) some work on the transport API as well as a message flooding fix." On wl12xx, Luca says: "Lots of bugfixes and improvements in our TI wireless drivers, including support for multi-channel. Intended for 3.9." On Bluetooth, Gustavo says: "This is my first pull request to 3.9. The biggest changes here are from Johan Hedberg who made a lot of fixes in the Management interface. The issues arose due to a new test tool we wrote and the usage of the Management interface as default in BlueZ 5. The rest of the patches are more clean ups and small fixes." And also: "Here goes another batch intended for 3.9, the majority of the patch here are from Johan who is fixing many issues in the management interface that have appeared lately. The rest of the patches are just small improvements, fixes and clean ups." Along with those are the usual variety of updates/enhancements to the mwl8k, mwifiex, ath9k, rtlwifi, and rt2x00 drivers as well as a few updates for the ssb and bcma busses. I don't think there are any big headliners there. Please let me know if there are problems! ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/bcma/bcma_private.h1
-rw-r--r--drivers/bcma/driver_chipcommon_nflash.c4
-rw-r--r--drivers/bcma/driver_chipcommon_sflash.c4
-rw-r--r--drivers/bcma/driver_gpio.c11
-rw-r--r--drivers/bcma/driver_mips.c38
-rw-r--r--drivers/bcma/main.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig1
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h17
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c193
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c23
-rw-r--r--drivers/net/wireless/ath/ath9k/mci.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c80
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c9
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c55
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c63
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c31
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c107
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h22
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c107
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c5
-rw-r--r--drivers/net/wireless/iwlegacy/3945-mac.c39
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c57
-rw-r--r--drivers/net/wireless/iwlegacy/4965.c3
-rw-r--r--drivers/net/wireless/iwlegacy/commands.h3
-rw-r--r--drivers/net/wireless/iwlegacy/common.h1
-rw-r--r--drivers/net/wireless/iwlwifi/Kconfig14
-rw-r--r--drivers/net/wireless/iwlwifi/Makefile3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/agn.h4
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/calib.c4
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/calib.h4
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/commands.h4
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/debugfs.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/dev.h2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/devices.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/led.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/led.h2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/lib.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c9
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/main.c42
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/power.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/power.h2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.c7
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.h2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rxon.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/scan.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/sta.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/testmode.c4
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tt.c8
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tt.h2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/tx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/ucode.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-hw.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-config.h5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-csr.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-debug.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-devtrace.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c27
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.h6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c18
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h13
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-read.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-read.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fh.h5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-file.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h20
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.c93
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-io.h15
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-modparams.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-notif-wait.c4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-notif-wait.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.c346
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.h80
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-op-mode.h6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-phy-db.c514
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-phy-db.h82
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h7
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-test.c21
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-test.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-testmode.h4
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h52
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile10
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/binding.c197
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c841
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c378
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h282
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h369
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h140
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h312
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h561
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h380
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h580
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h949
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c644
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/led.c134
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c951
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c1310
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h500
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/nvm.c311
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c679
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c292
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power.c207
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/quota.c178
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.c3096
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.h393
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c355
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/scan.c437
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c1211
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h368
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.c569
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.h214
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c916
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c472
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/1000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/2000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/5000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/6000.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/7000.c111
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/cfg.h6
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/drv.c10
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h6
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/rx.c3
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c95
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c2
-rw-r--r--drivers/net/wireless/mwifiex/11n.c39
-rw-r--r--drivers/net/wireless/mwifiex/11n.h2
-rw-r--r--drivers/net/wireless/mwifiex/README1
-rw-r--r--drivers/net/wireless/mwifiex/debugfs.c2
-rw-r--r--drivers/net/wireless/mwifiex/init.c1
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h1
-rw-r--r--drivers/net/wireless/mwifiex/join.c6
-rw-r--r--drivers/net/wireless/mwifiex/main.h1
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c6
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c10
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c5
-rw-r--r--drivers/net/wireless/mwifiex/usb.c4
-rw-r--r--drivers/net/wireless/mwifiex/util.c2
-rw-r--r--drivers/net/wireless/mwl8k.c167
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c1
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c20
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h6
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c7
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c6
-rw-r--r--drivers/net/wireless/rtlwifi/rc.c15
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c3
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/sw.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/dm.c4
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/fw.c8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/hw.c8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/phy.c20
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/trx.c2
-rw-r--r--drivers/net/wireless/rtlwifi/usb.c4
-rw-r--r--drivers/net/wireless/rtlwifi/wifi.h2
-rw-r--r--drivers/net/wireless/ti/wl1251/Kconfig2
-rw-r--r--drivers/net/wireless/ti/wl12xx/Makefile2
-rw-r--r--drivers/net/wireless/ti/wl12xx/cmd.c37
-rw-r--r--drivers/net/wireless/ti/wl12xx/cmd.h20
-rw-r--r--drivers/net/wireless/ti/wl12xx/event.c116
-rw-r--r--drivers/net/wireless/ti/wl12xx/event.h111
-rw-r--r--drivers/net/wireless/ti/wl12xx/main.c192
-rw-r--r--drivers/net/wireless/ti/wl12xx/scan.c501
-rw-r--r--drivers/net/wireless/ti/wl12xx/scan.h140
-rw-r--r--drivers/net/wireless/ti/wl12xx/wl12xx.h40
-rw-r--r--drivers/net/wireless/ti/wl18xx/Makefile2
-rw-r--r--drivers/net/wireless/ti/wl18xx/acx.c87
-rw-r--r--drivers/net/wireless/ti/wl18xx/acx.h55
-rw-r--r--drivers/net/wireless/ti/wl18xx/cmd.c80
-rw-r--r--drivers/net/wireless/ti/wl18xx/cmd.h52
-rw-r--r--drivers/net/wireless/ti/wl18xx/conf.h21
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.c111
-rw-r--r--drivers/net/wireless/ti/wl18xx/event.h77
-rw-r--r--drivers/net/wireless/ti/wl18xx/main.c267
-rw-r--r--drivers/net/wireless/ti/wl18xx/scan.c326
-rw-r--r--drivers/net/wireless/ti/wl18xx/scan.h127
-rw-r--r--drivers/net/wireless/ti/wl18xx/tx.c54
-rw-r--r--drivers/net/wireless/ti/wl18xx/wl18xx.h50
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.c15
-rw-r--r--drivers/net/wireless/ti/wlcore/acx.h1
-rw-r--r--drivers/net/wireless/ti/wlcore/boot.c77
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.c419
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.h81
-rw-r--r--drivers/net/wireless/ti/wlcore/conf.h110
-rw-r--r--drivers/net/wireless/ti/wlcore/debugfs.c10
-rw-r--r--drivers/net/wireless/ti/wlcore/event.c326
-rw-r--r--drivers/net/wireless/ti/wlcore/event.h99
-rw-r--r--drivers/net/wireless/ti/wlcore/hw_ops.h41
-rw-r--r--drivers/net/wireless/ti/wlcore/init.c19
-rw-r--r--drivers/net/wireless/ti/wlcore/io.h12
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c1572
-rw-r--r--drivers/net/wireless/ti/wlcore/ps.c11
-rw-r--r--drivers/net/wireless/ti/wlcore/rx.c33
-rw-r--r--drivers/net/wireless/ti/wlcore/rx.h3
-rw-r--r--drivers/net/wireless/ti/wlcore/scan.c696
-rw-r--r--drivers/net/wireless/ti/wlcore/scan.h144
-rw-r--r--drivers/net/wireless/ti/wlcore/sdio.c3
-rw-r--r--drivers/net/wireless/ti/wlcore/spi.c5
-rw-r--r--drivers/net/wireless/ti/wlcore/tx.c298
-rw-r--r--drivers/net/wireless/ti/wlcore/tx.h35
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore.h118
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore_i.h49
-rw-r--r--drivers/ssb/driver_gpio.c22
-rw-r--r--drivers/ssb/driver_mipscore.c48
-rw-r--r--drivers/ssb/main.c8
-rw-r--r--drivers/ssb/ssb_private.h4
-rw-r--r--include/linux/bcma/bcma_driver_chipcommon.h2
-rw-r--r--include/linux/bcma/bcma_driver_mips.h9
-rw-r--r--include/linux/ieee80211.h5
-rw-r--r--include/linux/ssb/ssb_driver_mips.h5
-rw-r--r--include/net/bluetooth/a2mp.h4
-rw-r--r--include/net/bluetooth/bluetooth.h23
-rw-r--r--include/net/bluetooth/hci.h18
-rw-r--r--include/net/bluetooth/hci_core.h5
-rw-r--r--include/net/bluetooth/l2cap.h1
-rw-r--r--include/net/cfg80211.h42
-rw-r--r--include/net/mac80211.h31
-rw-r--r--include/uapi/linux/nl80211.h51
-rw-r--r--net/bluetooth/a2mp.c42
-rw-r--r--net/bluetooth/amp.c25
-rw-r--r--net/bluetooth/bnep/core.c1
-rw-r--r--net/bluetooth/hci_core.c30
-rw-r--r--net/bluetooth/hci_event.c64
-rw-r--r--net/bluetooth/hci_sysfs.c22
-rw-r--r--net/bluetooth/l2cap_core.c5
-rw-r--r--net/bluetooth/mgmt.c489
-rw-r--r--net/bluetooth/sco.c18
-rw-r--r--net/mac80211/agg-rx.c14
-rw-r--r--net/mac80211/agg-tx.c61
-rw-r--r--net/mac80211/driver-ops.h36
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/iface.c3
-rw-r--r--net/mac80211/key.c5
-rw-r--r--net/mac80211/main.c64
-rw-r--r--net/mac80211/mesh_plink.c168
-rw-r--r--net/mac80211/mlme.c42
-rw-r--r--net/mac80211/sta_info.c5
-rw-r--r--net/mac80211/trace.h53
-rw-r--r--net/mac80211/tx.c8
-rw-r--r--net/wireless/core.c5
-rw-r--r--net/wireless/nl80211.c116
-rw-r--r--net/wireless/rdev-ops.h12
-rw-r--r--net/wireless/trace.h18
-rw-r--r--net/wireless/util.c3
249 files changed, 26293 insertions, 3180 deletions
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 04f7c86ea3d8..6f377bd1a6d6 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -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 */
diff --git a/drivers/bcma/driver_chipcommon_nflash.c b/drivers/bcma/driver_chipcommon_nflash.c
index 1f0b83e18f68..d4f699aef8c4 100644
--- a/drivers/bcma/driver_chipcommon_nflash.c
+++ b/drivers/bcma/driver_chipcommon_nflash.c
@@ -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,
diff --git a/drivers/bcma/driver_chipcommon_sflash.c b/drivers/bcma/driver_chipcommon_sflash.c
index 1e694db4532d..e6ed4fe5dced 100644
--- a/drivers/bcma/driver_chipcommon_sflash.c
+++ b/drivers/bcma/driver_chipcommon_sflash.c
@@ -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,
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
index 9a6f585da2d9..0b5df538dfd9 100644
--- a/drivers/bcma/driver_gpio.c
+++ b/drivers/bcma/driver_gpio.c
@@ -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
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
index 9fe86ee16c66..9a7f0e3ab5a3 100644
--- a/drivers/bcma/driver_mips.c
+++ b/drivers/bcma/driver_mips.c
@@ -14,11 +14,33 @@
#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");
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index ff8528925322..6eda7ef0682f 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -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);
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index 7647ed6b73d7..17507dc8a1e7 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -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.
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index b2d6c18d1678..97c90b21e1cb 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -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);
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 6c5d313ebcb7..3714b971d18e 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -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,
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index a22c0d780700..410d6d8f1aa7 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -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 {
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 4b1abc7da98c..af932c9444de 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -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;
}
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index b42be910a83d..811007ec07a7 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -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;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 4b72b660f180..5432f1247e2e 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -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;
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c
index d2074334ec9b..815bee21c19a 100644
--- a/drivers/net/wireless/ath/ath9k/mci.c
+++ b/drivers/net/wireless/ath/ath9k/mci.c
@@ -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);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index d7c129bb571b..2d0fd17a1917 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -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:
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 116f4e807ae1..002851fceb2f 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -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);
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 38049da71049..dc97e7b2609c 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -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;
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 95fcd361322b..761c389586d4 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -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 */
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 3068b5cb53a7..8ce2e33dce20 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -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;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 0fc83edd6bad..81c35c6e3832 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -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);
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index f29c294413cf..64b971fdc3cc 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -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;
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 9bcfffa4006c..aea961ff8f08 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -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);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 12915f6e7617..0b70e17cd1fb 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -18,8 +18,10 @@
#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,
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 7fc49ca3f597..b1dd5600fd02 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -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) {
diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c
index 050ce7c70d74..83856d1a6101 100644
--- a/drivers/net/wireless/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/iwlegacy/3945-mac.c
@@ -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);
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index f1dc04006564..9741ac10a334 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -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);
diff --git a/drivers/net/wireless/iwlegacy/4965.c b/drivers/net/wireless/iwlegacy/4965.c
index 5db11714e047..91eb2d07fdb8 100644
--- a/drivers/net/wireless/iwlegacy/4965.c
+++ b/drivers/net/wireless/iwlegacy/4965.c
@@ -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);
diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h
index 25dd7d28d022..3b6c99400892 100644
--- a/drivers/net/wireless/iwlegacy/commands.h
+++ b/drivers/net/wireless/iwlegacy/commands.h
@@ -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)
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h
index 37fe553b25e0..96f2025d936e 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/iwlegacy/common.h
@@ -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;
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index 5cf43236421e..ba319cba3f1e 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 170ec330d2a9..6c7800044a04 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -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)
diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h
index 33b3ad2e546b..f41ae79e6bc0 100644
--- a/drivers/net/wireless/iwlwifi/dvm/agn.h
+++ b/drivers/net/wireless/iwlwifi/dvm/agn.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c
index de54713b680c..6468de8634b0 100644
--- a/drivers/net/wireless/iwlwifi/dvm/calib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/calib.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.h b/drivers/net/wireless/iwlwifi/dvm/calib.h
index 2349f393cc42..65e920cab2b7 100644
--- a/drivers/net/wireless/iwlwifi/dvm/calib.h
+++ b/drivers/net/wireless/iwlwifi/dvm/calib.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h
index 0ca99c13f7f2..8bce4b0148e0 100644
--- a/drivers/net/wireless/iwlwifi/dvm/commands.h
+++ b/drivers/net/wireless/iwlwifi/dvm/commands.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c
index 72c74af38138..20806cae11b7 100644
--- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h
index 2653a891cc7e..71ea77576d22 100644
--- a/drivers/net/wireless/iwlwifi/dvm/dev.h
+++ b/drivers/net/wireless/iwlwifi/dvm/dev.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c
index 8c72be3f37c1..15cca2ef9294 100644
--- a/drivers/net/wireless/iwlwifi/dvm/devices.c
+++ b/drivers/net/wireless/iwlwifi/dvm/devices.c
@@ -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/dvm/led.c b/drivers/net/wireless/iwlwifi/dvm/led.c
index 844a17f99a18..33c7e15d24f5 100644
--- a/drivers/net/wireless/iwlwifi/dvm/led.c
+++ b/drivers/net/wireless/iwlwifi/dvm/led.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/led.h b/drivers/net/wireless/iwlwifi/dvm/led.h
index b02a853103d3..8749dcfe695f 100644
--- a/drivers/net/wireless/iwlwifi/dvm/led.h
+++ b/drivers/net/wireless/iwlwifi/dvm/led.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c
index 6ff46605ad4f..86ea5f4c3939 100644
--- a/drivers/net/wireless/iwlwifi/dvm/lib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/lib.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index 0353e1c0670d..c2f03ecd4bf8 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -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);
diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c
index a64f361e341c..b9e3517652d6 100644
--- a/drivers/net/wireless/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/iwlwifi/dvm/main.c
@@ -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,
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c
index 518cf3715809..bd69018d07a9 100644
--- a/drivers/net/wireless/iwlwifi/dvm/power.c
+++ b/drivers/net/wireless/iwlwifi/dvm/power.c
@@ -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.
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.h b/drivers/net/wireless/iwlwifi/dvm/power.h
index a2cee7f04848..7b03e1342d47 100644
--- a/drivers/net/wireless/iwlwifi/dvm/power.h
+++ b/drivers/net/wireless/iwlwifi/dvm/power.h
@@ -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.
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c
index f3dd0da60d8a..a131227c49e9 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.c
@@ -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;
}
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/iwlwifi/dvm/rs.h
index ad3aea8f626a..5d83cab22d62 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.h
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c
index cac4f37cc427..e8d5b90abf5c 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rx.c
@@ -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.
diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c
index 9a891e6e60e8..9fabd26997ca 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rxon.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c
index 610ed2204e1f..3a4aa5239c45 100644
--- a/drivers/net/wireless/iwlwifi/dvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/dvm/scan.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c
index bdba9543c351..ab768045696b 100644
--- a/drivers/net/wireless/iwlwifi/dvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/dvm/sta.c
@@ -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.
diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c
index 57b918ce3b5f..dc6f965a123a 100644
--- a/drivers/net/wireless/iwlwifi/dvm/testmode.c
+++ b/drivers/net/wireless/iwlwifi/dvm/testmode.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c
index b28cfc8553d7..67e2e1321b40 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tt.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tt.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.h b/drivers/net/wireless/iwlwifi/dvm/tt.h
index 44c7c8f30a2d..9356c4b908ca 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tt.h
+++ b/drivers/net/wireless/iwlwifi/dvm/tt.h
@@ -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.
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c
index 191b9d4bee47..f4a013675947 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tx.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c
index ebec13a3329f..736fe9bb140e 100644
--- a/drivers/net/wireless/iwlwifi/dvm/ucode.c
+++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
index 7960a52f6ad4..e9975c54c276 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h
@@ -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-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
index 864219d2136a..743b48343358 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/iwlwifi/iwl-config.h
@@ -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,
};
/*
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index b419a1efac0a..df3463a38704 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index 42b20b0e83bc..8cf5db7fb5c9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -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)
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c
index 70191ddbd8f6..8f61c717f619 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
index dc7e26b2f383..9a0f45ec9e01 100644
--- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h
+++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index d3549f493a17..6f228bb2b844 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -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);
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h
index 285de5f68c05..594a5c71b272 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.h
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.h
@@ -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>"
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
index 471986690cf0..034f2ff4f43d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
@@ -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;
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
index 555f0eb61d48..683fe6a8c58f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
@@ -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__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
index 27c7da3c6ed1..ef4806f27cf8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
index 1337c9d36fee..b2588c5cbf93 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h
index c646a90b725e..f5592fb3b1ed 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fh.h
@@ -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)
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index e71564053e7f..90873eca35f7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 715291eb7f8e..b545178e46e3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -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;
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c
index bff3ac96c00b..276410d82de4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.c
+++ b/drivers/net/wireless/iwlwifi/iwl-io.c
@@ -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.
*
@@ -35,54 +35,6 @@
#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);
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index dc478068596b..fd9f5b97fff3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -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);
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h
index d9a86d6b2bd7..e5e3a79eae2f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h
@@ -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-notif-wait.c b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
index c61f2070f15a..c3affbc62cdf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
+++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c
@@ -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-notif-wait.h b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
index 821523100cf1..c2ce764463a3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
+++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h
@@ -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
index 000000000000..a70213bdb83c
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
@@ -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
index 000000000000..b2692bd287fa
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
@@ -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__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
index c8d9b9517468..dc792584f401 100644
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
@@ -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
index 000000000000..14fc8d39fc28
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c
@@ -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
index 000000000000..d0e43d96ab38
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.h
@@ -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__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index c3a4bb41e533..f76e9cad7757 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -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
*
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c
index 1a226114fe73..ce0c67b425ee 100644
--- a/drivers/net/wireless/iwlwifi/iwl-test.c
+++ b/drivers/net/wireless/iwlwifi/iwl-test.c
@@ -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;
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.h b/drivers/net/wireless/iwlwifi/iwl-test.h
index e13ffa8acc02..7fbf4d717caa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-test.h
+++ b/drivers/net/wireless/iwlwifi/iwl-test.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h
index 6ba211b09426..a963f45c6849 100644
--- a/drivers/net/wireless/iwlwifi/iwl-testmode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-testmode.h
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 0f85eb305878..0a3d4df5f434 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -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
index 000000000000..807b250ec396
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -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
index 000000000000..73d24aacb90a
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/binding.c
@@ -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
index 000000000000..9a95c374990d
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -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
index 000000000000..c1bdb5582126
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -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
index 000000000000..cf6f9a02fb74
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
@@ -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
index 000000000000..ae39b7dfda7b
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
@@ -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
index 000000000000..be36b7604b7f
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -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
index 000000000000..aa3474d08231
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
@@ -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
index 000000000000..670ac8f95e26
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
@@ -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
index 000000000000..0acb53dda22d
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
@@ -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
index 000000000000..2677914bf0a6
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
@@ -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
index 000000000000..9fd49db32a32
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -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
index 000000000000..90473c2ba1c7
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -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
index 000000000000..011906e73a05
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/led.c
@@ -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
index 000000000000..c08a17a3cab9
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -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
index 000000000000..a6b05a02cfd4
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -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
index 000000000000..4e339ccfa800
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -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
index 000000000000..20016bcbdeab
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c
@@ -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
index 000000000000..983dca3f888a
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -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
index 000000000000..b428448f8ddf
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
@@ -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
index 000000000000..63628739cf4a
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -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
index 000000000000..2d4611a563c5
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/quota.c
@@ -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
index 000000000000..60a4291ca221
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -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
index 000000000000..219c6857cc0f
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.h
@@ -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
index 000000000000..52da375e5740
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -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
index 000000000000..406c53ad0a49
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -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
index 000000000000..69603c3b2b39
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -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
index 000000000000..1bf301097984
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -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
index 000000000000..b9f076f4f17c
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -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
index 000000000000..64fb57a5ab43
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h
@@ -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
index 000000000000..cada8efe0cca
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -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
index 000000000000..000e842c2edd
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -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);
+}
diff --git a/drivers/net/wireless/iwlwifi/pcie/1000.c b/drivers/net/wireless/iwlwifi/pcie/1000.c
index f8620ecae6b4..ff3389757281 100644
--- a/drivers/net/wireless/iwlwifi/pcie/1000.c
+++ b/drivers/net/wireless/iwlwifi/pcie/1000.c
@@ -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/2000.c b/drivers/net/wireless/iwlwifi/pcie/2000.c
index 244019cec3e1..e7de33128b16 100644
--- a/drivers/net/wireless/iwlwifi/pcie/2000.c
+++ b/drivers/net/wireless/iwlwifi/pcie/2000.c
@@ -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/5000.c b/drivers/net/wireless/iwlwifi/pcie/5000.c
index 83ca40321ff1..5096f7c96ab6 100644
--- a/drivers/net/wireless/iwlwifi/pcie/5000.c
+++ b/drivers/net/wireless/iwlwifi/pcie/5000.c
@@ -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
diff --git a/drivers/net/wireless/iwlwifi/pcie/6000.c b/drivers/net/wireless/iwlwifi/pcie/6000.c
index d4df976d4709..801ff49796dd 100644
--- a/drivers/net/wireless/iwlwifi/pcie/6000.c
+++ b/drivers/net/wireless/iwlwifi/pcie/6000.c
@@ -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
index 000000000000..6e35b2b72332
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/pcie/7000.c
@@ -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));
diff --git a/drivers/net/wireless/iwlwifi/pcie/cfg.h b/drivers/net/wireless/iwlwifi/pcie/cfg.h
index 82152311d73b..c6f8e83c3551 100644
--- a/drivers/net/wireless/iwlwifi/pcie/cfg.h
+++ b/drivers/net/wireless/iwlwifi/pcie/cfg.h
@@ -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__ */
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index c2e141af353c..7bc0fb9128dd 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -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);
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 20735a008cab..5f6bb4e09d42 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -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;
};
/**
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
index 4e6591d24e61..a9ca1d35fa93 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/rx.c
@@ -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,
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index c57641eb83d5..56d4f72500bc 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -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
@@ -75,6 +75,33 @@
#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));
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index a93f06762b96..041127ad372a 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -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.
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index 7b0ae2407083..25596ab0c576 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -400,45 +400,6 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
}
/*
- * 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.
*/
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h
index 46006a54a656..29a4c02479d6 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/mwifiex/11n.h
@@ -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,
diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README
index b55badef4660..3d64613ebb29 100644
--- a/drivers/net/wireless/mwifiex/README
+++ b/drivers/net/wireless/mwifiex/README
@@ -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>
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c
index 46e34aa65d1c..753b5682d53f 100644
--- a/drivers/net/wireless/mwifiex/debugfs.c
+++ b/drivers/net/wireless/mwifiex/debugfs.c
@@ -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),
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 84848c33b7f0..e38aa9b3663d 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -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;
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index 6095b3e53f4e..f3d9d0445529 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -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;
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 893d809ba83c..a537297866c6 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -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 */
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 51044e3ea89b..ac799a046eb7 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -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;
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c
index 237949c070cc..df88e65595c8 100644
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ b/drivers/net/wireless/mwifiex/pcie.c
@@ -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;
}
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index 31d7b2bdaa31..d3fb9a14580a 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -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);
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index 65c12eb3e5e7..847056415ac9 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -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,
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c
index 5d4a10a8a005..f90fe21e5bfd 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/mwifiex/usb.c
@@ -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;
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index 0982375ba3b1..21553976b550 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -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);
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 224cf917744a..2031130d860b 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -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:
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index a5c694f23d33..a658b4bc7da2 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -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;
}
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 0e8d1705e368..48a01aa21f1c 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -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) },
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 4721cada1591..42b5b659af16 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -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, }
};
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index b52512b8ac5f..9a3f31a543ce 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -88,11 +88,9 @@
#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...) \
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index b40a53857498..1031db66474a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -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.
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index ed7a1bb3f245..20c6eccce5aa 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -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;
}
diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c
index c1e065f136ba..204f46c4510d 100644
--- a/drivers/net/wireless/rtlwifi/rc.c
+++ b/drivers/net/wireless/rtlwifi/rc.c
@@ -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,
};
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
index 1cdf5a271c9f..b793a659a465 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c
@@ -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};
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
index b7e6607e6b6d..d9e659f92767 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
@@ -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)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
index fd8df233ff22..5251fb8a111e 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c
@@ -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);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
index f55b1767ef57..35cb8f83eed4 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c
@@ -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);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c
index 887d521fe690..68c28340f791 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c
@@ -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
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
index 0a8c03863fb2..149804816ac4 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
@@ -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)));
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
index 3d8536bb0d2b..eafbb18dd48e 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c
@@ -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;
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
index ce8ad12bce5b..b1fd2b328abf 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c
@@ -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,
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c
index 1535efda3d52..d42bbe21ba6e 100644
--- a/drivers/net/wireless/rtlwifi/usb.c
+++ b/drivers/net/wireless/rtlwifi/usb.c
@@ -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);
}
diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h
index 21a5f4f4a135..f13258a8d995 100644
--- a/drivers/net/wireless/rtlwifi/wifi.h
+++ b/drivers/net/wireless/rtlwifi/wifi.h
@@ -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 */
diff --git a/drivers/net/wireless/ti/wl1251/Kconfig b/drivers/net/wireless/ti/wl1251/Kconfig
index 1fb65849414f..8fec4ed36ac2 100644
--- a/drivers/net/wireless/ti/wl1251/Kconfig
+++ b/drivers/net/wireless/ti/wl1251/Kconfig
@@ -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---
diff --git a/drivers/net/wireless/ti/wl12xx/Makefile b/drivers/net/wireless/ti/wl12xx/Makefile
index da509aa7d009..e6a24056b3c8 100644
--- a/drivers/net/wireless/ti/wl12xx/Makefile
+++ b/drivers/net/wireless/ti/wl12xx/Makefile
@@ -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
diff --git a/drivers/net/wireless/ti/wl12xx/cmd.c b/drivers/net/wireless/ti/wl12xx/cmd.c
index 622206241e83..7dc9f965037d 100644
--- a/drivers/net/wireless/ti/wl12xx/cmd.c
+++ b/drivers/net/wireless/ti/wl12xx/cmd.c
@@ -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;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/cmd.h b/drivers/net/wireless/ti/wl12xx/cmd.h
index 140a0e8829d5..32cbad54e993 100644
--- a/drivers/net/wireless/ti/wl12xx/cmd.h
+++ b/drivers/net/wireless/ti/wl12xx/cmd.h
@@ -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
index 000000000000..6ac0ed751da8
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/event.c
@@ -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
index 000000000000..a5cc3fcd9eea
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/event.h
@@ -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
+
diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index e5f5f8f39144..3254bfc81a2a 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -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
index 000000000000..affdb3ec6225
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/scan.c
@@ -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
index 000000000000..264af7ac2785
--- /dev/null
+++ b/drivers/net/wireless/ti/wl12xx/scan.h
@@ -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
diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h
index 7182bbf6625d..d4552857480c 100644
--- a/drivers/net/wireless/ti/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h
@@ -24,19 +24,37 @@
#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__ */
diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile
index 67c098734c7f..ae2b81735785 100644
--- a/drivers/net/wireless/ti/wl18xx/Makefile
+++ b/drivers/net/wireless/ti/wl18xx/Makefile
@@ -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
diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c
index 72840e23bf59..a169bb5a5dbf 100644
--- a/drivers/net/wireless/ti/wl18xx/acx.c
+++ b/drivers/net/wireless/ti/wl18xx/acx.c
@@ -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;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h
index e2609a6b7341..0e636def1217 100644
--- a/drivers/net/wireless/ti/wl18xx/acx.h
+++ b/drivers/net/wireless/ti/wl18xx/acx.h
@@ -26,7 +26,13 @@
#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
index 000000000000..1d1f6cc7a50a
--- /dev/null
+++ b/drivers/net/wireless/ti/wl18xx/cmd.c
@@ -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
index 000000000000..6687d10899ac
--- /dev/null
+++ b/drivers/net/wireless/ti/wl18xx/cmd.h
@@ -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
diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h
index 4d426cc20274..b5f114857191 100644
--- a/drivers/net/wireless/ti/wl18xx/conf.h
+++ b/drivers/net/wireless/ti/wl18xx/conf.h
@@ -23,20 +23,21 @@
#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
index 000000000000..c9199d7804c6
--- /dev/null
+++ b/drivers/net/wireless/ti/wl18xx/event.c
@@ -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
index 000000000000..398f3d2c0a6c
--- /dev/null
+++ b/drivers/net/wireless/ti/wl18xx/event.h
@@ -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
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 8d8c1f8c63b7..0be1cfc17a86 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -34,10 +34,13 @@
#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
index 000000000000..09d944505ac0
--- /dev/null
+++ b/drivers/net/wireless/ti/wl18xx/scan.c
@@ -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
index 000000000000..eadee42689d1
--- /dev/null
+++ b/drivers/net/wireless/ti/wl18xx/scan.h
@@ -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
diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c
index 5b1fb10d9fd7..57c694396647 100644
--- a/drivers/net/wireless/ti/wl18xx/tx.c
+++ b/drivers/net/wireless/ti/wl18xx/tx.c
@@ -28,6 +28,49 @@
#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)
diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h
index 96a1e438d677..b6739e79efcf 100644
--- a/drivers/net/wireless/ti/wl18xx/wl18xx.h
+++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h
@@ -26,10 +26,10 @@
/* 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
diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index ce108a736bd0..c79654323396 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -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;
diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h
index d03215d6b3bd..126536c6a393 100644
--- a/drivers/net/wireless/ti/wlcore/acx.h
+++ b/drivers/net/wireless/ti/wlcore/acx.h
@@ -1025,7 +1025,6 @@ enum {
ACX_CONFIG_HANGOVER = 0x0042,
ACX_FEATURE_CFG = 0x0043,
ACX_PROTECTION_CFG = 0x0044,
- ACX_CHECKSUM_CONFIG = 0x0045,
};
diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c
index 375ea574eafb..b58ae5fc1487 100644
--- a/drivers/net/wireless/ti/wlcore/boot.c
+++ b/drivers/net/wireless/ti/wlcore/boot.c
@@ -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");
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index 27f83f72a93b..1201aca9c89a 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -48,14 +48,15 @@
* @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;
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index 2409f3d71f63..fd34123047cd 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -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 */
diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
index 9e40760bafe1..2b96ff821341 100644
--- a/drivers/net/wireless/ti/wlcore/conf.h
+++ b/drivers/net/wireless/ti/wlcore/conf.h
@@ -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 {
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index c86bb00c2488..e70a7c864865 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -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;
diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index 48907054d493..70f289aa1bc6 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -29,34 +29,39 @@
#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;
diff --git a/drivers/net/wireless/ti/wlcore/event.h b/drivers/net/wireless/ti/wlcore/event.h
index 8adf18d6c58f..acc7a59d3828 100644
--- a/drivers/net/wireless/ti/wlcore/event.h
+++ b/drivers/net/wireless/ti/wlcore/event.h
@@ -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
diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h
index 2673d783ec1e..7fd260c02a0a 100644
--- a/drivers/net/wireless/ti/wlcore/hw_ops.h
+++ b/drivers/net/wireless/ti/wlcore/hw_ops.h
@@ -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
diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c
index 32d157f62f31..5c6f11e157d9 100644
--- a/drivers/net/wireless/ti/wlcore/init.c
+++ b/drivers/net/wireless/ti/wlcore/init.c
@@ -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)
diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h
index f48530fec14f..af7d9f9b3b4d 100644
--- a/drivers/net/wireless/ti/wlcore/io.h
+++ b/drivers/net/wireless/ti/wlcore/io.h
@@ -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,
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index ce6e62a37e14..e1dfdf94d0f7 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -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,147 +2876,10 @@ 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)
-{
- 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 (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);
- }
-
- ret = wl12xx_start_dev(wl, wlvif);
- if (ret < 0)
- goto out;
- set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
- }
-
-out:
- return ret;
-}
-
static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_conf *conf, u32 changed)
{
- bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
- int channel, ret;
-
- channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
-
- /* 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);
- }
- }
+ int ret;
if (conf->power_level != wlvif->power_level) {
ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
@@ -2721,37 +2897,17 @@ 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);
+ int ret = 0;
- 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;
+
+ ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
+ if (ret)
+ goto out_sleep;
+
+ set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
- if (!ret)
- 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;
@@ -5279,6 +5672,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
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");
diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c
index 4d1414a673fb..9b7b6e2e4fbc 100644
--- a/drivers/net/wireless/ti/wlcore/ps.c
+++ b/drivers/net/wireless/ti/wlcore/ps.c
@@ -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);
diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c
index 9ee0ec6fd1db..6791a1a6afba 100644
--- a/drivers/net/wireless/ti/wlcore/rx.c
+++ b/drivers/net/wireless/ti/wlcore/rx.c
@@ -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;
}
diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h
index 71eba1899915..3363f60fb7da 100644
--- a/drivers/net/wireless/ti/wlcore/rx.h
+++ b/drivers/net/wireless/ti/wlcore/rx.h
@@ -84,12 +84,11 @@
* 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
diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c
index d00501493dfe..f407101e525b 100644
--- a/drivers/net/wireless/ti/wlcore/scan.c
+++ b/drivers/net/wireless/ti/wlcore/scan.c
@@ -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);
diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h
index 29f3c8d6b046..a6ab24b5c0f9 100644
--- a/drivers/net/wireless/ti/wlcore/scan.h
+++ b/drivers/net/wireless/ti/wlcore/scan.h
@@ -26,22 +26,20 @@
#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__ */
diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c
index a3a20be2036f..75622f6f3e6c 100644
--- a/drivers/net/wireless/ti/wlcore/sdio.c
+++ b/drivers/net/wireless/ti/wlcore/sdio.c
@@ -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);
}
diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c
index 18cadc07b754..53790d1d0d27 100644
--- a/drivers/net/wireless/ti/wlcore/spi.c
+++ b/drivers/net/wireless/ti/wlcore/spi.c
@@ -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;
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index a90d3cd09408..ece392c54d9c 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -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];
}
diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h
index 349520d8b724..55aa4acf9105 100644
--- a/drivers/net/wireless/ti/wlcore/tx.h
+++ b/drivers/net/wireless/ti/wlcore/tx.h
@@ -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);
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index c3884937c007..ebd8c6fad7cd 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -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
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
index 5ce26cf402fc..910f8e2e556a 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -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;
}
diff --git a/drivers/ssb/driver_gpio.c b/drivers/ssb/driver_gpio.c
index 97ac0a38e3d0..accabe39b320 100644
--- a/drivers/ssb/driver_gpio.c
+++ b/drivers/ssb/driver_gpio.c
@@ -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
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
index 2a7684c90243..33b37dac40bd 100644
--- a/drivers/ssb/driver_mipscore.c
+++ b/drivers/ssb/driver_mipscore.c
@@ -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>
@@ -17,6 +18,25 @@
#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)
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 772ad9b5c304..9987d9f366f4 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -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. */
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 77d942630750..53198dcec90e 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -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);
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index ee332fab825b..1d002b58b60b 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -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
diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h
index 0d1ea297851a..fb61f3fb4ddb 100644
--- a/include/linux/bcma/bcma_driver_mips.h
+++ b/include/linux/bcma/bcma_driver_mips.h
@@ -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_ */
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index ccf9ee1dca8c..11c8bc87fdcb 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -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
diff --git a/include/linux/ssb/ssb_driver_mips.h b/include/linux/ssb/ssb_driver_mips.h
index 07a9c7a2e088..afe79d40a99e 100644
--- a/include/linux/ssb/ssb_driver_mips.h
+++ b/include/linux/ssb/ssb_driver_mips.h
@@ -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_ */
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 42f21766c538..487b54c1308f 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -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 */
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
index 2554b3f5222a..9531beee09b5 100644
--- a/include/net/bluetooth/bluetooth.h
+++ b/include/net/bluetooth/bluetooth.h
@@ -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} })
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 45eee08157bb..7f12c25f1fca 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -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
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 014a2eaa5389..90cf75afcb02 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -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;
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 7588ef44ebaf..cdd33021f831 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -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;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 1b9830ebe6c6..f9df20028bbd 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -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;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 3037f49e51c8..5c98d654fc75 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -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
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index e6eeb4ba5dc5..5b7dbc1ea966 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -170,7 +170,8 @@
* %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
@@ -586,6 +587,16 @@
* @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 */
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 2f67d5ecc907..eb0f4b16ff09 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -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;
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index 1b0d92c0643a..d459ed43c779 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -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);
}
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index a5b639702637..e430b1abcd2f 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -33,7 +33,6 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/l2cap.h>
#include "bnep.h"
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 0f78e34220c9..22e77a786545 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -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);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 81b44481d0d9..477726a63512 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -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)
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 55cceee02a84..23b4e242a31a 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -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)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 22e658322845..7c7e9321f1ea 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -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;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index f559b966279c..39395c7144aa 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -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);
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 57f250c20e39..b5178d62064e 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -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) ----- */
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 808338a1bce5..31bf2586fb84 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -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);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 2f0ccbc5f13e..13b7683de5a4 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -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;
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 0c07f94c5378..434b3c4f31b5 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -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 */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 63f0430c131e..5fba867d9e2e 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -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 -
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 06fac2991d40..0a36dc6346bb 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -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);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 619c5d697999..ef252eb58c36 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -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);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 39cfe8f10ad2..2bdd454e8bcf 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -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();
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 9e0416696a83..81e612682bc3 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -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();
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index e930175771ff..5913fb924b12 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -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);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 9d864ed5f3da..227233c3ff7f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -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;
}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 41861b91daa3..6ca53d64cb28 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -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
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index f32d68186dbc..a2cb6a302cc7 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -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:
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 9245729694d2..40dbe37cfbf6 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -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);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 33de80364c5c..b5978ab4ad7a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -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 = {
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 6c0c8191f837..422d38291d66 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -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 */
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 2134576f426e..8bc553199686 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -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 *
*************************************************************/
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 1c2795d52db0..d7873c7ae0ec 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -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: