]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 25 Jul 2014 14:22:36 +0000 (10:22 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 25 Jul 2014 14:22:36 +0000 (10:22 -0400)
Conflicts:
net/mac80211/cfg.c

Signed-off-by: John W. Linville <linville@tuxdriver.com>
374 files changed:
MAINTAINERS
drivers/bcma/Makefile
drivers/bcma/driver_chipcommon_pmu.c
drivers/bcma/driver_gpio.c
drivers/bcma/driver_pcie2.c [new file with mode: 0644]
drivers/bcma/host_pci.c
drivers/bcma/main.c
drivers/bcma/sprom.c
drivers/bluetooth/Kconfig
drivers/bluetooth/ath3k.c
drivers/bluetooth/btmrvl_drv.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btmrvl_sdio.h
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_h5.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_uart.h
drivers/bluetooth/hci_vhci.c
drivers/net/wireless/at76c50x-usb.c
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/pci.h
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath5k/ath5k.h
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath6kl/bmi.h
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.c
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/htc_pipe.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/ath6kl/main.c
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ar9002_mac.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar953x_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/channel.c [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/common-beacon.c
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/link.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/spectral.c
drivers/net/wireless/ath/ath9k/tx99.c
drivers/net/wireless/ath/ath9k/wow.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/rx_reorder.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/b43/Makefile
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/main.h
drivers/net/wireless/b43/phy_a.c
drivers/net/wireless/b43/phy_a.h
drivers/net/wireless/b43/phy_common.c
drivers/net/wireless/b43/phy_common.h
drivers/net/wireless/b43/phy_ht.c
drivers/net/wireless/b43/phy_lcn.c
drivers/net/wireless/b43/phy_n.c
drivers/net/wireless/b43/phy_n.h
drivers/net/wireless/b43/radio_2057.c
drivers/net/wireless/b43/radio_2057.h
drivers/net/wireless/b43/tables_nphy.c
drivers/net/wireless/b43/tables_nphy.h
drivers/net/wireless/b43/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/Makefile
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
drivers/net/wireless/brcm80211/brcmfmac/chip.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/feature.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/feature.h [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/firmware.c
drivers/net/wireless/brcm80211/brcmfmac/firmware.h
drivers/net/wireless/brcm80211/brcmfmac/fwil.c
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/p2p.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/vendor.c [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/vendor.h [new file with mode: 0644]
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/cw1200/scan.c
drivers/net/wireless/cw1200/scan.h
drivers/net/wireless/cw1200/sta.c
drivers/net/wireless/ipw2x00/libipw_module.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/common.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/power.c
drivers/net/wireless/iwlwifi/iwl-8000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/coex.c
drivers/net/wireless/iwlwifi/mvm/coex_legacy.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/constants.h
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/time-event.h
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/libertas/Kconfig
drivers/net/wireless/libertas/cmd.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11ac.c
drivers/net/wireless/mwifiex/11ac.h
drivers/net/wireless/mwifiex/11h.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n.h
drivers/net/wireless/mwifiex/11n_aggr.c
drivers/net/wireless/mwifiex/11n_aggr.h
drivers/net/wireless/mwifiex/11n_rxreorder.c
drivers/net/wireless/mwifiex/11n_rxreorder.h
drivers/net/wireless/mwifiex/Makefile
drivers/net/wireless/mwifiex/README
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cfg80211.h
drivers/net/wireless/mwifiex/cfp.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/debugfs.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/ethtool.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/ie.c
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/ioctl.h
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/pcie.h
drivers/net/wireless/mwifiex/scan.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sdio.h
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_event.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/sta_rx.c
drivers/net/wireless/mwifiex/sta_tx.c
drivers/net/wireless/mwifiex/tdls.c
drivers/net/wireless/mwifiex/txrx.c
drivers/net/wireless/mwifiex/uap_cmd.c
drivers/net/wireless/mwifiex/uap_event.c
drivers/net/wireless/mwifiex/uap_txrx.c
drivers/net/wireless/mwifiex/usb.c
drivers/net/wireless/mwifiex/usb.h
drivers/net/wireless/mwifiex/util.c
drivers/net/wireless/mwifiex/util.h
drivers/net/wireless/mwifiex/wmm.c
drivers/net/wireless/mwifiex/wmm.h
drivers/net/wireless/orinoco/Kconfig
drivers/net/wireless/p54/p54spi.c
drivers/net/wireless/rsi/rsi_91x_core.c
drivers/net/wireless/rsi/rsi_91x_debugfs.c
drivers/net/wireless/rsi/rsi_91x_mac80211.c
drivers/net/wireless/rsi/rsi_91x_mgmt.c
drivers/net/wireless/rsi/rsi_91x_pkt.c
drivers/net/wireless/rsi/rsi_91x_sdio.c
drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
drivers/net/wireless/rsi/rsi_91x_usb.c
drivers/net/wireless/rsi/rsi_main.h
drivers/net/wireless/rsi/rsi_mgmt.h
drivers/net/wireless/rsi/rsi_sdio.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00mmio.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rt2x00/rt2x00queue.h
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/rtl818x/rtl8180/rtl8180.h
drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h
drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
drivers/net/wireless/rtlwifi/rtl8188ee/reg.h
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
drivers/net/wireless/rtlwifi/rtl8192de/hw.c
drivers/net/wireless/rtlwifi/rtl8192de/phy.c
drivers/net/wireless/rtlwifi/rtl8192de/reg.h
drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
drivers/net/wireless/rtlwifi/rtl8723ae/reg.h
drivers/net/wireless/rtlwifi/rtl8723be/hw.c
drivers/net/wireless/rtlwifi/rtl8723be/reg.h
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl12xx/scan.c
drivers/net/wireless/ti/wl12xx/scan.h
drivers/net/wireless/ti/wl18xx/cmd.c
drivers/net/wireless/ti/wl18xx/cmd.h
drivers/net/wireless/ti/wl18xx/event.c
drivers/net/wireless/ti/wl18xx/event.h
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/scan.c
drivers/net/wireless/ti/wl18xx/scan.h
drivers/net/wireless/ti/wl18xx/tx.c
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/Makefile
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/scan.h
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/vendor_cmd.c [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/vendor_cmd.h [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
drivers/net/wireless/zd1211rw/Kconfig
drivers/ssb/pci.c
fs/compat_ioctl.c
include/linux/bcma/bcma.h
include/linux/bcma/bcma_driver_pcie2.h [new file with mode: 0644]
include/linux/ieee80211.h
include/linux/ssb/ssb_regs.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/hci_sock.h [new file with mode: 0644]
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
include/net/bluetooth/sco.h
include/net/cfg80211.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/6lowpan/Kconfig [new file with mode: 0644]
net/6lowpan/Makefile [new file with mode: 0644]
net/6lowpan/iphc.c [new file with mode: 0644]
net/Kconfig
net/Makefile
net/bluetooth/6lowpan.c
net/bluetooth/6lowpan.h [deleted file]
net/bluetooth/Kconfig
net/bluetooth/Makefile
net/bluetooth/a2mp.c
net/bluetooth/af_bluetooth.c
net/bluetooth/amp.c
net/bluetooth/cmtp/capi.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/sco.c
net/bluetooth/smp.c
net/bluetooth/smp.h
net/ieee802154/6lowpan_iphc.c [deleted file]
net/ieee802154/Kconfig
net/ieee802154/Makefile
net/mac80211/Kconfig
net/mac80211/Makefile
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/ethtool.c [new file with mode: 0644]
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/pm.c
net/mac80211/rate.h
net/mac80211/rc80211_pid.h [deleted file]
net/mac80211/rc80211_pid_algo.c [deleted file]
net/mac80211/rc80211_pid_debugfs.c [deleted file]
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tdls.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wep.c
net/mac80211/wpa.c
net/wireless/Kconfig
net/wireless/core.c
net/wireless/ethtool.c
net/wireless/ethtool.h [deleted file]
net/wireless/genregdb.awk
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/trace.h

index 134483f206e42661947e0bc4b84ef09ab07abd1b..0057ff5805366aa15d206d805cfe731d49840aab 100644 (file)
@@ -148,6 +148,13 @@ L: linux-scsi@vger.kernel.org
 S:     Maintained
 F:     drivers/scsi/53c700*
 
+6LOWPAN GENERIC (BTLE/IEEE 802.15.4)
+M:     Alexander Aring <alex.aring@gmail.com>
+L:     linux-zigbee-devel@lists.sourceforge.net (moderated for non-subscribers)
+L:     linux-bluetooth@vger.kernel.org
+S:     Maintained
+F:     net/6lowpan/
+
 6PACK NETWORK DRIVER FOR AX.25
 M:     Andreas Koensgen <ajk@comnets.uni-bremen.de>
 L:     linux-hams@vger.kernel.org
@@ -5629,16 +5636,6 @@ F:       Documentation/networking/mac80211-injection.txt
 F:     include/net/mac80211.h
 F:     net/mac80211/
 
-MAC80211 PID RATE CONTROL
-M:     Stefano Brivio <stefano.brivio@polimi.it>
-M:     Mattias Nissler <mattias.nissler@gmx.de>
-L:     linux-wireless@vger.kernel.org
-W:     http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git
-S:     Maintained
-F:     net/mac80211/rc80211_pid*
-
 MACVLAN DRIVER
 M:     Patrick McHardy <kaber@trash.net>
 L:     netdev@vger.kernel.org
index 734b32f09c0a21aa7ef734100400a47f13f485ad..91290f7f61b8c2b903d874345b6c32896da6114a 100644 (file)
@@ -3,6 +3,7 @@ bcma-y                                  += driver_chipcommon.o driver_chipcommon_pmu.o
 bcma-$(CONFIG_BCMA_SFLASH)             += driver_chipcommon_sflash.o
 bcma-$(CONFIG_BCMA_NFLASH)             += driver_chipcommon_nflash.o
 bcma-y                                 += driver_pci.o
+bcma-y                                 += driver_pcie2.o
 bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE)        += driver_pci_host.o
 bcma-$(CONFIG_BCMA_DRIVER_MIPS)                += driver_mips.o
 bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN)    += driver_gmac_cmn.o
index 5081a8c439ccd12707bb32b332b42d1fd38bdf7c..bb694e2e9f32917358e676d9f7203fe5d876c80c 100644 (file)
@@ -603,6 +603,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid)
                tmp = BCMA_CC_PMU_CTL_PLL_UPD | BCMA_CC_PMU_CTL_NOILPONW;
                break;
 
+       case BCMA_CHIP_ID_BCM43217:
        case BCMA_CHIP_ID_BCM43227:
        case BCMA_CHIP_ID_BCM43228:
        case BCMA_CHIP_ID_BCM43428:
index d7f81ad56b8af731ea10fb863460cebb717dddc0..aec9f850b4a80eba3ea7c0b06bd5df647829980e 100644 (file)
@@ -220,6 +220,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc)
 #endif
        switch (cc->core->bus->chipinfo.id) {
        case BCMA_CHIP_ID_BCM5357:
+       case BCMA_CHIP_ID_BCM53572:
                chip->ngpio     = 32;
                break;
        default:
diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c
new file mode 100644 (file)
index 0000000..e4be537
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Broadcom specific AMBA
+ * PCIe Gen 2 Core
+ *
+ * Copyright 2014, Broadcom Corporation
+ * Copyright 2014, RafaÅ‚ MiÅ‚ecki <zajec5@gmail.com>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include <linux/bcma/bcma.h>
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+#if 0
+static u32 bcma_core_pcie2_cfg_read(struct bcma_drv_pcie2 *pcie2, u32 addr)
+{
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr);
+       pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR);
+       return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA);
+}
+#endif
+
+static void bcma_core_pcie2_cfg_write(struct bcma_drv_pcie2 *pcie2, u32 addr,
+                                     u32 val)
+{
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, val);
+}
+
+/**************************************************
+ * Init.
+ **************************************************/
+
+static u32 bcma_core_pcie2_war_delay_perst_enab(struct bcma_drv_pcie2 *pcie2,
+                                               bool enable)
+{
+       u32 val;
+
+       /* restore back to default */
+       val = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL);
+       val |= PCIE2_CLKC_DLYPERST;
+       val &= ~PCIE2_CLKC_DISSPROMLD;
+       if (enable) {
+               val &= ~PCIE2_CLKC_DLYPERST;
+               val |= PCIE2_CLKC_DISSPROMLD;
+       }
+       pcie2_write32(pcie2, (BCMA_CORE_PCIE2_CLK_CONTROL), val);
+       /* flush */
+       return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL);
+}
+
+static void bcma_core_pcie2_set_ltr_vals(struct bcma_drv_pcie2 *pcie2)
+{
+       /* LTR0 */
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x844);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x883c883c);
+       /* LTR1 */
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x848);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x88648864);
+       /* LTR2 */
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x84C);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x90039003);
+}
+
+static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2)
+{
+       u8 core_rev = pcie2->core->id.rev;
+       u32 devstsctr2;
+
+       if (core_rev < 2 || core_rev == 10 || core_rev > 13)
+               return;
+
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
+                     PCIE2_CAP_DEVSTSCTRL2_OFFSET);
+       devstsctr2 = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA);
+       if (devstsctr2 & PCIE2_CAP_DEVSTSCTRL2_LTRENAB) {
+               /* force the right LTR values */
+               bcma_core_pcie2_set_ltr_vals(pcie2);
+
+               /* TODO:
+               si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); */
+
+               /* enable the LTR */
+               devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB;
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
+                             PCIE2_CAP_DEVSTSCTRL2_OFFSET);
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, devstsctr2);
+
+               /* set the LTR state to be active */
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE,
+                             PCIE2_LTR_ACTIVE);
+               usleep_range(1000, 2000);
+
+               /* set the LTR state to be sleep */
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE,
+                             PCIE2_LTR_SLEEP);
+               usleep_range(1000, 2000);
+       }
+}
+
+static void pciedev_crwlpciegen2(struct bcma_drv_pcie2 *pcie2)
+{
+       u8 core_rev = pcie2->core->id.rev;
+       bool pciewar160, pciewar162;
+
+       pciewar160 = core_rev == 7 || core_rev == 9 || core_rev == 11;
+       pciewar162 = core_rev == 5 || core_rev == 7 || core_rev == 8 ||
+                    core_rev == 9 || core_rev == 11;
+
+       if (!pciewar160 && !pciewar162)
+               return;
+
+/* TODO */
+#if 0
+       pcie2_set32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL,
+                   PCIE_DISABLE_L1CLK_GATING);
+#if 0
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
+                     PCIEGEN2_COE_PVT_TL_CTRL_0);
+       pcie2_mask32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA,
+                    ~(1 << COE_PVT_TL_CTRL_0_PM_DIS_L1_REENTRY_BIT));
+#endif
+#endif
+}
+
+static void pciedev_crwlpciegen2_180(struct bcma_drv_pcie2 *pcie2)
+{
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_PMCR_REFUP);
+       pcie2_set32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x1f);
+}
+
+static void pciedev_crwlpciegen2_182(struct bcma_drv_pcie2 *pcie2)
+{
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_SBMBX);
+       pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 1 << 0);
+}
+
+static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2)
+{
+       struct bcma_drv_cc *drv_cc = &pcie2->core->bus->drv_cc;
+       u8 core_rev = pcie2->core->id.rev;
+       u32 alp_khz, pm_value;
+
+       if (core_rev <= 13) {
+               alp_khz = bcma_pmu_get_alp_clock(drv_cc) / 1000;
+               pm_value = (1000000 * 2) / alp_khz;
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR,
+                             PCIE2_PVT_REG_PM_CLK_PERIOD);
+               pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, pm_value);
+       }
+}
+
+void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2)
+{
+       struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo;
+       u32 tmp;
+
+       tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54));
+       if ((tmp & 0xe) >> 1 == 2)
+               bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17);
+
+       /* TODO: Do we need pcie_reqsize? */
+
+       if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3)
+               bcma_core_pcie2_war_delay_perst_enab(pcie2, true);
+       bcma_core_pcie2_hw_ltr_war(pcie2);
+       pciedev_crwlpciegen2(pcie2);
+       pciedev_reg_pm_clk_period(pcie2);
+       pciedev_crwlpciegen2_180(pcie2);
+       pciedev_crwlpciegen2_182(pcie2);
+}
index e333305363aa0491b9ce7fef3473e1a2cbbf1e07..3cf725a49dc184e83951e44628ae80b75654bed4 100644 (file)
@@ -279,6 +279,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
        { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
        { 0, },
 };
index 34ea4c588d36bd798ee83bc9db6273eba6baf034..0ff8d58831ef30fcbe7a5452baa8b00e3fdababa 100644 (file)
@@ -132,6 +132,7 @@ static int bcma_register_cores(struct bcma_bus *bus)
                case BCMA_CORE_CHIPCOMMON:
                case BCMA_CORE_PCI:
                case BCMA_CORE_PCIE:
+               case BCMA_CORE_PCIE2:
                case BCMA_CORE_MIPS_74K:
                case BCMA_CORE_4706_MAC_GBIT_COMMON:
                        continue;
@@ -281,6 +282,13 @@ int bcma_bus_register(struct bcma_bus *bus)
                bcma_core_pci_init(&bus->drv_pci[1]);
        }
 
+       /* Init PCIe Gen 2 core */
+       core = bcma_find_core_unit(bus, BCMA_CORE_PCIE2, 0);
+       if (core) {
+               bus->drv_pcie2.core = core;
+               bcma_core_pcie2_init(&bus->drv_pcie2);
+       }
+
        /* Init GBIT MAC COMMON core */
        core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON);
        if (core) {
index 72bf4540f5658425b4836303d8ed6d95b43a6702..97bb38e9ed653ba0aa6712333705d860b114611d 100644 (file)
@@ -201,6 +201,23 @@ static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
                SPEX(_field[7], _offset + 14, _mask, _shift);   \
        } while (0)
 
+static s8 sprom_extract_antgain(const u16 *in, u16 offset, u16 mask, u16 shift)
+{
+       u16 v;
+       u8 gain;
+
+       v = in[SPOFF(offset)];
+       gain = (v & mask) >> shift;
+       if (gain == 0xFF) {
+               gain = 8; /* If unset use 2dBm */
+       } else {
+               /* Q5.2 Fractional part is stored in 0xC0 */
+               gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
+       }
+
+       return (s8)gain;
+}
+
 static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
 {
        u16 v, o;
@@ -381,14 +398,22 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
        SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0);
 
        /* Extract the antenna gain values. */
-       SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01,
-            SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
-       SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01,
-            SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
-       SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23,
-            SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
-       SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23,
-            SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
+       bus->sprom.antenna_gain.a0 = sprom_extract_antgain(sprom,
+                                                          SSB_SPROM8_AGAIN01,
+                                                          SSB_SPROM8_AGAIN0,
+                                                          SSB_SPROM8_AGAIN0_SHIFT);
+       bus->sprom.antenna_gain.a1 = sprom_extract_antgain(sprom,
+                                                          SSB_SPROM8_AGAIN01,
+                                                          SSB_SPROM8_AGAIN1,
+                                                          SSB_SPROM8_AGAIN1_SHIFT);
+       bus->sprom.antenna_gain.a2 = sprom_extract_antgain(sprom,
+                                                          SSB_SPROM8_AGAIN23,
+                                                          SSB_SPROM8_AGAIN2,
+                                                          SSB_SPROM8_AGAIN2_SHIFT);
+       bus->sprom.antenna_gain.a3 = sprom_extract_antgain(sprom,
+                                                          SSB_SPROM8_AGAIN23,
+                                                          SSB_SPROM8_AGAIN3,
+                                                          SSB_SPROM8_AGAIN3_SHIFT);
 
        SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
             SSB_SPROM8_LEDDC_ON_SHIFT);
@@ -509,6 +534,7 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
                /* for these chips OTP is always available */
                present = true;
                break;
+       case BCMA_CHIP_ID_BCM43217:
        case BCMA_CHIP_ID_BCM43227:
        case BCMA_CHIP_ID_BCM43228:
        case BCMA_CHIP_ID_BCM43428:
index f5ce64e03fd7bad524d6c172afcd204854202195..fa7fd62ddffa429234323a4b0df7d5b1c5c5c6ed 100644 (file)
@@ -30,8 +30,8 @@ config BT_HCIUART
        help
          Bluetooth HCI UART driver.
          This driver is required if you want to use Bluetooth devices with
-         serial port interface. You will also need this driver if you have 
-         UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card 
+         serial port interface. You will also need this driver if you have
+         UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card
          adapter and BrainBoxes Bluetooth PC Card.
 
          Say Y here to compile support for Bluetooth UART devices into the
@@ -41,9 +41,9 @@ config BT_HCIUART_H4
        bool "UART (H4) protocol support"
        depends on BT_HCIUART
        help
-         UART (H4) is serial protocol for communication between Bluetooth 
-         device and host. This protocol is required for most Bluetooth devices 
-         with UART interface, including PCMCIA and CF cards. 
+         UART (H4) is serial protocol for communication between Bluetooth
+         device and host. This protocol is required for most Bluetooth devices
+         with UART interface, including PCMCIA and CF cards.
 
          Say Y here to compile support for HCI UART (H4) protocol.
 
@@ -52,7 +52,7 @@ config BT_HCIUART_BCSP
        depends on BT_HCIUART
        select BITREVERSE
        help
-         BCSP (BlueCore Serial Protocol) is serial protocol for communication 
+         BCSP (BlueCore Serial Protocol) is serial protocol for communication
          between Bluetooth device and host. This protocol is required for non
          USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and
          CF cards.
index f50dffc0374fb4ca9222d75683523c73a9dfce43..a0d7355ef1275e865ad09d6e3503c953111cf4fd 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/device.h>
 #include <linux/firmware.h>
 #include <linux/usb.h>
+#include <asm/unaligned.h>
 #include <net/bluetooth/bluetooth.h>
 
 #define VERSION "1.0"
 #define ATH3K_NAME_LEN                         0xFF
 
 struct ath3k_version {
-       unsigned int    rom_version;
-       unsigned int    build_version;
-       unsigned int    ram_version;
-       unsigned char   ref_clock;
-       unsigned char   reserved[0x07];
-};
+       __le32  rom_version;
+       __le32  build_version;
+       __le32  ram_version;
+       __u8    ref_clock;
+       __u8    reserved[7];
+} __packed;
 
 static const struct usb_device_id ath3k_table[] = {
        /* Atheros AR3011 */
@@ -103,6 +104,7 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x13d3, 0x3375) },
        { USB_DEVICE(0x13d3, 0x3393) },
        { USB_DEVICE(0x13d3, 0x3402) },
+       { USB_DEVICE(0x13d3, 0x3432) },
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xE02C) },
@@ -152,6 +154,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU22 with sflash firmware */
        { USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
@@ -288,10 +291,10 @@ static int ath3k_load_fwfile(struct usb_device *udev,
        sent += size;
        count -= size;
 
+       pipe = usb_sndbulkpipe(udev, 0x02);
+
        while (count) {
                size = min_t(uint, count, BULK_SIZE);
-               pipe = usb_sndbulkpipe(udev, 0x02);
-
                memcpy(send_buf, firmware->data + sent, size);
 
                err = usb_bulk_msg(udev, pipe, send_buf, size,
@@ -347,7 +350,8 @@ static int ath3k_load_patch(struct usb_device *udev)
        unsigned char fw_state;
        char filename[ATH3K_NAME_LEN] = {0};
        const struct firmware *firmware;
-       struct ath3k_version fw_version, pt_version;
+       struct ath3k_version fw_version;
+       __u32 pt_rom_version, pt_build_version;
        int ret;
 
        ret = ath3k_get_state(udev, &fw_state);
@@ -368,7 +372,7 @@ static int ath3k_load_patch(struct usb_device *udev)
        }
 
        snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
-               le32_to_cpu(fw_version.rom_version));
+                le32_to_cpu(fw_version.rom_version));
 
        ret = request_firmware(&firmware, filename, &udev->dev);
        if (ret < 0) {
@@ -376,12 +380,13 @@ static int ath3k_load_patch(struct usb_device *udev)
                return ret;
        }
 
-       pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8);
-       pt_version.build_version = *(int *)
-               (firmware->data + firmware->size - 4);
+       pt_rom_version = get_unaligned_le32(firmware->data +
+                                           firmware->size - 8);
+       pt_build_version = get_unaligned_le32(firmware->data +
+                                             firmware->size - 4);
 
-       if ((pt_version.rom_version != fw_version.rom_version) ||
-               (pt_version.build_version <= fw_version.build_version)) {
+       if (pt_rom_version != le32_to_cpu(fw_version.rom_version) ||
+           pt_build_version <= le32_to_cpu(fw_version.build_version)) {
                BT_ERR("Patch file version did not match with firmware");
                release_firmware(firmware);
                return -EINVAL;
index dc79f88f8717f478c8d8ab6d0a7d45849f772ac1..38ad66289ad6206204138a31d4d4bda83a4297c9 100644 (file)
@@ -68,6 +68,7 @@ struct btmrvl_adapter {
        u8 hs_state;
        u8 wakeup_tries;
        wait_queue_head_t cmd_wait_q;
+       wait_queue_head_t event_hs_wait_q;
        u8 cmd_complete;
        bool is_suspended;
 };
@@ -89,6 +90,8 @@ struct btmrvl_private {
 #define MRVL_VENDOR_PKT                        0xFE
 
 /* Vendor specific Bluetooth commands */
+#define BT_CMD_PSCAN_WIN_REPORT_ENABLE 0xFC03
+#define BT_CMD_SET_BDADDR              0xFC22
 #define BT_CMD_AUTO_SLEEP_MODE         0xFC23
 #define BT_CMD_HOST_SLEEP_CONFIG       0xFC59
 #define BT_CMD_HOST_SLEEP_ENABLE       0xFC5A
@@ -143,6 +146,7 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
 int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
 
 int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd);
+int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd);
 int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv);
 int btmrvl_enable_ps(struct btmrvl_private *priv);
 int btmrvl_prepare_command(struct btmrvl_private *priv);
index e9dbddb0b8f1efb1f15ede65f50d80390ca370c3..bae8e6a0ecf684b64d71d42b730ba96c58da327c 100644 (file)
@@ -114,6 +114,7 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
                        adapter->hs_state = HS_ACTIVATED;
                        if (adapter->psmode)
                                adapter->ps_state = PS_SLEEP;
+                       wake_up_interruptible(&adapter->event_hs_wait_q);
                        BT_DBG("HS ACTIVATED!");
                } else {
                        BT_DBG("HS Enable failed");
@@ -214,6 +215,23 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)
 }
 EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
 
+int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd)
+{
+       struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+       int ret;
+
+       if (!card->support_pscan_win_report)
+               return 0;
+
+       ret = btmrvl_send_sync_cmd(priv, BT_CMD_PSCAN_WIN_REPORT_ENABLE,
+                                  &subcmd, 1);
+       if (ret)
+               BT_ERR("PSCAN_WIN_REPORT_ENABLE command failed: %#x", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(btmrvl_pscan_window_reporting);
+
 int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)
 {
        int ret;
@@ -253,11 +271,31 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
 
 int btmrvl_enable_hs(struct btmrvl_private *priv)
 {
+       struct btmrvl_adapter *adapter = priv->adapter;
        int ret;
 
        ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);
-       if (ret)
+       if (ret) {
                BT_ERR("Host sleep enable command failed\n");
+               return ret;
+       }
+
+       ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q,
+                                              adapter->hs_state,
+                       msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED));
+       if (ret < 0) {
+               BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d",
+                      ret, adapter->hs_state, adapter->ps_state,
+                      adapter->wakeup_tries);
+       } else if (!ret) {
+               BT_ERR("hs_enable timeout: %d,%d,%d", adapter->hs_state,
+                      adapter->ps_state, adapter->wakeup_tries);
+               ret = -ETIMEDOUT;
+       } else {
+               BT_DBG("host sleep enabled: %d,%d,%d", adapter->hs_state,
+                      adapter->ps_state, adapter->wakeup_tries);
+               ret = 0;
+       }
 
        return ret;
 }
@@ -358,6 +396,7 @@ static void btmrvl_init_adapter(struct btmrvl_private *priv)
        }
 
        init_waitqueue_head(&priv->adapter->cmd_wait_q);
+       init_waitqueue_head(&priv->adapter->event_hs_wait_q);
 }
 
 static void btmrvl_free_adapter(struct btmrvl_private *priv)
@@ -489,6 +528,8 @@ static int btmrvl_setup(struct hci_dev *hdev)
 
        btmrvl_cal_data_dt(priv);
 
+       btmrvl_pscan_window_reporting(priv, 0x01);
+
        priv->btmrvl_dev.psmode = 1;
        btmrvl_enable_ps(priv);
 
@@ -498,6 +539,29 @@ static int btmrvl_setup(struct hci_dev *hdev)
        return 0;
 }
 
+static int btmrvl_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       long ret;
+       u8 buf[8];
+
+       buf[0] = MRVL_VENDOR_PKT;
+       buf[1] = sizeof(bdaddr_t);
+       memcpy(buf + 2, bdaddr, sizeof(bdaddr_t));
+
+       skb = __hci_cmd_sync(hdev, BT_CMD_SET_BDADDR, sizeof(buf), buf,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: changing btmrvl device address failed (%ld)",
+                      hdev->name, ret);
+               return ret;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+
 /*
  * This function handles the event generated by firmware, rx data
  * received from firmware, and tx data sent from kernel.
@@ -591,6 +655,7 @@ int btmrvl_register_hdev(struct btmrvl_private *priv)
        hdev->flush = btmrvl_flush;
        hdev->send  = btmrvl_send_frame;
        hdev->setup = btmrvl_setup;
+       hdev->set_bdaddr = btmrvl_set_bdaddr;
 
        hdev->dev_type = priv->btmrvl_dev.dev_type;
 
@@ -666,6 +731,7 @@ int btmrvl_remove_card(struct btmrvl_private *priv)
        hdev = priv->btmrvl_dev.hcidev;
 
        wake_up_interruptible(&priv->adapter->cmd_wait_q);
+       wake_up_interruptible(&priv->adapter->event_hs_wait_q);
 
        kthread_stop(priv->main_thread.task);
 
index 9dedca516ff50567a278fb9a511dbcdfd7a1980c..3e683b1532598231dc156bbc69887dd2e836d4da 100644 (file)
@@ -108,6 +108,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
        .helper         = "mrvl/sd8688_helper.bin",
        .firmware       = "mrvl/sd8688.bin",
        .reg            = &btmrvl_reg_8688,
+       .support_pscan_win_report = false,
        .sd_blksz_fw_dl = 64,
 };
 
@@ -115,6 +116,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8787_uapsta.bin",
        .reg            = &btmrvl_reg_87xx,
+       .support_pscan_win_report = false,
        .sd_blksz_fw_dl = 256,
 };
 
@@ -122,6 +124,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8797_uapsta.bin",
        .reg            = &btmrvl_reg_87xx,
+       .support_pscan_win_report = false,
        .sd_blksz_fw_dl = 256,
 };
 
@@ -129,6 +132,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
        .helper         = NULL,
        .firmware       = "mrvl/sd8897_uapsta.bin",
        .reg            = &btmrvl_reg_88xx,
+       .support_pscan_win_report = true,
        .sd_blksz_fw_dl = 256,
 };
 
@@ -1067,6 +1071,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
                card->firmware = data->firmware;
                card->reg = data->reg;
                card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
+               card->support_pscan_win_report = data->support_pscan_win_report;
        }
 
        if (btmrvl_sdio_register_dev(card) < 0) {
@@ -1164,6 +1169,10 @@ static int btmrvl_sdio_suspend(struct device *dev)
        }
 
        priv = card->priv;
+       hcidev = priv->btmrvl_dev.hcidev;
+       BT_DBG("%s: SDIO suspend", hcidev->name);
+       hci_suspend_dev(hcidev);
+       skb_queue_purge(&priv->adapter->tx_queue);
 
        if (priv->adapter->hs_state != HS_ACTIVATED) {
                if (btmrvl_enable_hs(priv)) {
@@ -1171,10 +1180,6 @@ static int btmrvl_sdio_suspend(struct device *dev)
                        return -EBUSY;
                }
        }
-       hcidev = priv->btmrvl_dev.hcidev;
-       BT_DBG("%s: SDIO suspend", hcidev->name);
-       hci_suspend_dev(hcidev);
-       skb_queue_purge(&priv->adapter->tx_queue);
 
        priv->adapter->is_suspended = true;
 
@@ -1216,13 +1221,13 @@ static int btmrvl_sdio_resume(struct device *dev)
                return 0;
        }
 
-       priv->adapter->is_suspended = false;
-       hcidev = priv->btmrvl_dev.hcidev;
-       BT_DBG("%s: SDIO resume", hcidev->name);
-       hci_resume_dev(hcidev);
        priv->hw_wakeup_firmware(priv);
        priv->adapter->hs_state = HS_DEACTIVATED;
+       hcidev = priv->btmrvl_dev.hcidev;
        BT_DBG("%s: HS DEACTIVATED in resume!", hcidev->name);
+       priv->adapter->is_suspended = false;
+       BT_DBG("%s: SDIO resume", hcidev->name);
+       hci_resume_dev(hcidev);
 
        return 0;
 }
index d4dd3b0fa53d16d68da0101494e43ca4664716c2..453559f98a75e4a895bf1a1086e1d5e754c2509a 100644 (file)
@@ -89,6 +89,7 @@ struct btmrvl_sdio_card {
        const char *helper;
        const char *firmware;
        const struct btmrvl_sdio_card_reg *reg;
+       bool support_pscan_win_report;
        u16 sd_blksz_fw_dl;
        u8 rx_unit;
        struct btmrvl_private *priv;
@@ -98,6 +99,7 @@ struct btmrvl_sdio_device {
        const char *helper;
        const char *firmware;
        const struct btmrvl_sdio_card_reg *reg;
+       const bool support_pscan_win_report;
        u16 sd_blksz_fw_dl;
 };
 
index 6250fc2fb93a7257697fa2efe34acfae204dfe7f..292c38e8aa1760c000cdcce9ee6d223e9f819559 100644 (file)
@@ -30,9 +30,6 @@
 
 #define VERSION "0.6"
 
-static bool ignore_dga;
-static bool ignore_csr;
-static bool ignore_sniffer;
 static bool disable_scofix;
 static bool force_scofix;
 
@@ -49,7 +46,9 @@ static struct usb_driver btusb_driver;
 #define BTUSB_WRONG_SCO_MTU    0x40
 #define BTUSB_ATH3012          0x80
 #define BTUSB_INTEL            0x100
-#define BTUSB_BCM_PATCHRAM     0x200
+#define BTUSB_INTEL_BOOT       0x200
+#define BTUSB_BCM_PATCHRAM     0x400
+#define BTUSB_MARVELL          0x800
 
 static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
@@ -115,12 +114,19 @@ static const struct usb_device_id btusb_table[] = {
        { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
          .driver_info = BTUSB_BCM_PATCHRAM },
 
+       /* ASUSTek Computer - Broadcom based */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) },
+
        /* Belkin F8065bf - Broadcom based */
        { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
 
        /* IMC Networks - Broadcom based */
        { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
 
+       /* Intel Bluetooth USB Bootloader (RAM module) */
+       { USB_DEVICE(0x8087, 0x0a5a),
+         .driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC },
+
        { }     /* Terminating entry */
 };
 
@@ -175,6 +181,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
@@ -228,15 +235,21 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
 
        /* CSR BlueCore Bluetooth Sniffer */
-       { USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER },
+       { USB_DEVICE(0x0a12, 0x0002),
+         .driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC },
 
        /* Frontline ComProbe Bluetooth Sniffer */
-       { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
+       { USB_DEVICE(0x16d3, 0x0002),
+         .driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC },
 
        /* Intel Bluetooth device */
        { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
        { USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
 
+       /* Marvell device */
+       { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL },
+       { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
+
        { }     /* Terminating entry */
 };
 
@@ -1182,6 +1195,51 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev,
        return 0;
 }
 
+#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
+
+static int btusb_check_bdaddr_intel(struct hci_dev *hdev)
+{
+       struct sk_buff *skb;
+       struct hci_rp_read_bd_addr *rp;
+
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("%s reading Intel device address failed (%ld)",
+                      hdev->name, PTR_ERR(skb));
+               return PTR_ERR(skb);
+       }
+
+       if (skb->len != sizeof(*rp)) {
+               BT_ERR("%s Intel device address length mismatch", hdev->name);
+               kfree_skb(skb);
+               return -EIO;
+       }
+
+       rp = (struct hci_rp_read_bd_addr *) skb->data;
+       if (rp->status) {
+               BT_ERR("%s Intel device address result failed (%02x)",
+                      hdev->name, rp->status);
+               kfree_skb(skb);
+               return -bt_to_errno(rp->status);
+       }
+
+       /* For some Intel based controllers, the default Bluetooth device
+        * address 00:03:19:9E:8B:00 can be found. These controllers are
+        * fully operational, but have the danger of duplicate addresses
+        * and that in turn can cause problems with Bluetooth operation.
+        */
+       if (!bacmp(&rp->bdaddr, BDADDR_INTEL)) {
+               BT_ERR("%s found Intel default device address (%pMR)",
+                      hdev->name, &rp->bdaddr);
+               set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+       }
+
+       kfree_skb(skb);
+
+       return 0;
+}
+
 static int btusb_setup_intel(struct hci_dev *hdev)
 {
        struct sk_buff *skb;
@@ -1254,6 +1312,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
                BT_INFO("%s: Intel device is already patched. patch num: %02x",
                        hdev->name, ver->fw_patch_num);
                kfree_skb(skb);
+               btusb_check_bdaddr_intel(hdev);
                return 0;
        }
 
@@ -1266,6 +1325,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        fw = btusb_setup_intel_get_fw(hdev, ver);
        if (!fw) {
                kfree_skb(skb);
+               btusb_check_bdaddr_intel(hdev);
                return 0;
        }
        fw_ptr = fw->data;
@@ -1345,6 +1405,7 @@ static int btusb_setup_intel(struct hci_dev *hdev)
        BT_INFO("%s: Intel Bluetooth firmware patch completed and activated",
                hdev->name);
 
+       btusb_check_bdaddr_intel(hdev);
        return 0;
 
 exit_mfg_disable:
@@ -1359,6 +1420,8 @@ exit_mfg_disable:
        kfree_skb(skb);
 
        BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name);
+
+       btusb_check_bdaddr_intel(hdev);
        return 0;
 
 exit_mfg_deactivate:
@@ -1379,9 +1442,52 @@ exit_mfg_deactivate:
        BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated",
                hdev->name);
 
+       btusb_check_bdaddr_intel(hdev);
+       return 0;
+}
+
+static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       long ret;
+
+       skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: changing Intel device address failed (%ld)",
+                       hdev->name, ret);
+               return ret;
+       }
+       kfree_skb(skb);
+
        return 0;
 }
 
+static int btusb_set_bdaddr_marvell(struct hci_dev *hdev,
+                                   const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       u8 buf[8];
+       long ret;
+
+       buf[0] = 0xfe;
+       buf[1] = sizeof(bdaddr_t);
+       memcpy(buf + 2, bdaddr, sizeof(bdaddr_t));
+
+       skb = __hci_cmd_sync(hdev, 0xfc22, sizeof(buf), buf, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: changing Marvell device address failed (%ld)",
+                      hdev->name, ret);
+               return ret;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+
+#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
+
 static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
 {
        struct btusb_data *data = hci_get_drvdata(hdev);
@@ -1395,6 +1501,7 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
        u16 opcode;
        struct sk_buff *skb;
        struct hci_rp_read_local_version *ver;
+       struct hci_rp_read_bd_addr *bda;
        long ret;
 
        snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd",
@@ -1404,8 +1511,7 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev)
 
        ret = request_firmware(&fw, fw_name, &hdev->dev);
        if (ret < 0) {
-               BT_INFO("%s: BCM: patch %s not found", hdev->name,
-                       fw_name);
+               BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name);
                return 0;
        }
 
@@ -1524,12 +1630,67 @@ reset_fw:
                ver->lmp_ver, ver->lmp_subver);
        kfree_skb(skb);
 
+       /* Read BD Address */
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)",
+                       hdev->name, ret);
+               goto done;
+       }
+
+       if (skb->len != sizeof(*bda)) {
+               BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch",
+                       hdev->name);
+               kfree_skb(skb);
+               ret = -EIO;
+               goto done;
+       }
+
+       bda = (struct hci_rp_read_bd_addr *) skb->data;
+       if (bda->status) {
+               BT_ERR("%s: HCI_OP_READ_BD_ADDR error status (%02x)",
+                      hdev->name, bda->status);
+               kfree_skb(skb);
+               ret = -bt_to_errno(bda->status);
+               goto done;
+       }
+
+       /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
+        * with no configured address.
+        */
+       if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
+               BT_INFO("%s: BCM: using default device address (%pMR)",
+                       hdev->name, &bda->bdaddr);
+               set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
+       }
+
+       kfree_skb(skb);
+
 done:
        release_firmware(fw);
 
        return ret;
 }
 
+static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+       struct sk_buff *skb;
+       long ret;
+
+       skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               ret = PTR_ERR(skb);
+               BT_ERR("%s: BCM: Change address command failed (%ld)",
+                       hdev->name, ret);
+               return ret;
+       }
+       kfree_skb(skb);
+
+       return 0;
+}
+
 static int btusb_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
 {
@@ -1554,15 +1715,6 @@ static int btusb_probe(struct usb_interface *intf,
        if (id->driver_info == BTUSB_IGNORE)
                return -ENODEV;
 
-       if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER)
-               return -ENODEV;
-
-       if (ignore_csr && id->driver_info & BTUSB_CSR)
-               return -ENODEV;
-
-       if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
-               return -ENODEV;
-
        if (id->driver_info & BTUSB_ATH3012) {
                struct usb_device *udev = interface_to_usbdev(intf);
 
@@ -1635,11 +1787,21 @@ static int btusb_probe(struct usb_interface *intf,
        if (id->driver_info & BTUSB_BCM92035)
                hdev->setup = btusb_setup_bcm92035;
 
-       if (id->driver_info & BTUSB_BCM_PATCHRAM)
+       if (id->driver_info & BTUSB_BCM_PATCHRAM) {
                hdev->setup = btusb_setup_bcm_patchram;
+               hdev->set_bdaddr = btusb_set_bdaddr_bcm;
+       }
 
-       if (id->driver_info & BTUSB_INTEL)
+       if (id->driver_info & BTUSB_INTEL) {
                hdev->setup = btusb_setup_intel;
+               hdev->set_bdaddr = btusb_set_bdaddr_intel;
+       }
+
+       if (id->driver_info & BTUSB_MARVELL)
+               hdev->set_bdaddr = btusb_set_bdaddr_marvell;
+
+       if (id->driver_info & BTUSB_INTEL_BOOT)
+               set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
 
        /* Interface numbers are hardcoded in the specification */
        data->isoc = usb_ifnum_to_if(data->udev, 1);
@@ -1679,8 +1841,18 @@ static int btusb_probe(struct usb_interface *intf,
                /* New sniffer firmware has crippled HCI interface */
                if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
                        set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+       }
 
-               data->isoc = NULL;
+       if (id->driver_info & BTUSB_INTEL_BOOT) {
+               /* A bug in the bootloader causes that interrupt interface is
+                * only enabled after receiving SetInterface(0, AltSetting=0).
+                */
+               err = usb_set_interface(data->udev, 0, 0);
+               if (err < 0) {
+                       BT_ERR("failed to set interface 0, alt 0 %d", err);
+                       hci_free_dev(hdev);
+                       return err;
+               }
        }
 
        if (data->isoc) {
@@ -1845,15 +2017,6 @@ static struct usb_driver btusb_driver = {
 
 module_usb_driver(btusb_driver);
 
-module_param(ignore_dga, bool, 0644);
-MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
-
-module_param(ignore_csr, bool, 0644);
-MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
-
-module_param(ignore_sniffer, bool, 0644);
-MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
-
 module_param(disable_scofix, bool, 0644);
 MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");
 
index fede8ca7147c8bbc778f1a25ec15501ed5f15f99..caacb422995dd4a69e18585275f32904a17bf708 100644 (file)
@@ -355,10 +355,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
 
 static int h5_rx_crc(struct hci_uart *hu, unsigned char c)
 {
-       struct h5 *h5 = hu->priv;
-
        h5_complete_rx_pkt(hu);
-       h5_reset_rx(h5);
 
        return 0;
 }
@@ -373,7 +370,6 @@ static int h5_rx_payload(struct hci_uart *hu, unsigned char c)
                h5->rx_pending = 2;
        } else {
                h5_complete_rx_pkt(hu);
-               h5_reset_rx(h5);
        }
 
        return 0;
index e00f8f5b5c8e10041a3acb0d74afd164135f78ee..dc487b5d11568f9ebcfec033ee0bc47f6d34ece0 100644 (file)
@@ -431,6 +431,9 @@ static int hci_uart_register_dev(struct hci_uart *hu)
        if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
                set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
 
+       if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags))
+               set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
+
        if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
                set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
 
@@ -477,6 +480,22 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
        return 0;
 }
 
+static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
+{
+       unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) |
+                                   BIT(HCI_UART_RESET_ON_INIT) |
+                                   BIT(HCI_UART_CREATE_AMP) |
+                                   BIT(HCI_UART_INIT_PENDING) |
+                                   BIT(HCI_UART_EXT_CONFIG);
+
+       if ((flags & ~valid_flags))
+               return -EINVAL;
+
+       hu->hdev_flags = flags;
+
+       return 0;
+}
+
 /* hci_uart_tty_ioctl()
  *
  *    Process IOCTL system call for the tty device.
@@ -520,14 +539,16 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
                return -EUNATCH;
 
        case HCIUARTGETDEVICE:
-               if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+               if (test_bit(HCI_UART_REGISTERED, &hu->flags))
                        return hu->hdev->id;
                return -EUNATCH;
 
        case HCIUARTSETFLAGS:
                if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
                        return -EBUSY;
-               hu->hdev_flags = arg;
+               err = hci_uart_set_flags(hu, arg);
+               if (err)
+                       return err;
                break;
 
        case HCIUARTGETFLAGS:
index 12df101ca942d1889280660e43ef37cec55c9340..247488edcbf93711168c9ee072a97edf923ecd9c 100644 (file)
@@ -48,6 +48,7 @@
 #define HCI_UART_RESET_ON_INIT 1
 #define HCI_UART_CREATE_AMP    2
 #define HCI_UART_INIT_PENDING  3
+#define HCI_UART_EXT_CONFIG    4
 
 struct hci_uart;
 
index add1c6a720637a4009e35bb2b0a9b4e605fb59b8..5bb5872ffee60c060232eb5ddcab7957125b0161 100644 (file)
@@ -40,7 +40,7 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-#define VERSION "1.4"
+#define VERSION "1.5"
 
 static bool amp;
 
@@ -95,10 +95,21 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
        return 0;
 }
 
-static int vhci_create_device(struct vhci_data *data, __u8 dev_type)
+static int vhci_create_device(struct vhci_data *data, __u8 opcode)
 {
        struct hci_dev *hdev;
        struct sk_buff *skb;
+       __u8 dev_type;
+
+       /* bits 0-1 are dev_type (BR/EDR or AMP) */
+       dev_type = opcode & 0x03;
+
+       if (dev_type != HCI_BREDR && dev_type != HCI_AMP)
+               return -EINVAL;
+
+       /* bits 2-5 are reserved (must be zero) */
+       if (opcode & 0x3c)
+               return -EINVAL;
 
        skb = bt_skb_alloc(4, GFP_KERNEL);
        if (!skb)
@@ -121,6 +132,14 @@ static int vhci_create_device(struct vhci_data *data, __u8 dev_type)
        hdev->flush = vhci_flush;
        hdev->send  = vhci_send_frame;
 
+       /* bit 6 is for external configuration */
+       if (opcode & 0x40)
+               set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
+
+       /* bit 7 is for raw device */
+       if (opcode & 0x80)
+               set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+
        if (hci_register_dev(hdev) < 0) {
                BT_ERR("Can't register HCI device");
                hci_free_dev(hdev);
@@ -132,7 +151,7 @@ static int vhci_create_device(struct vhci_data *data, __u8 dev_type)
        bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
 
        *skb_put(skb, 1) = 0xff;
-       *skb_put(skb, 1) = dev_type;
+       *skb_put(skb, 1) = opcode;
        put_unaligned_le16(hdev->id, skb_put(skb, 2));
        skb_queue_tail(&data->readq, skb);
 
@@ -146,7 +165,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
 {
        size_t len = iov_length(iov, count);
        struct sk_buff *skb;
-       __u8 pkt_type, dev_type;
+       __u8 pkt_type, opcode;
        unsigned long i;
        int ret;
 
@@ -190,7 +209,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
 
                cancel_delayed_work_sync(&data->open_timeout);
 
-               dev_type = *((__u8 *) skb->data);
+               opcode = *((__u8 *) skb->data);
                skb_pull(skb, 1);
 
                if (skb->len > 0) {
@@ -200,10 +219,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
 
                kfree_skb(skb);
 
-               if (dev_type != HCI_BREDR && dev_type != HCI_AMP)
-                       return -EINVAL;
-
-               ret = vhci_create_device(data, dev_type);
+               ret = vhci_create_device(data, opcode);
                break;
 
        default:
index d48776e4f343e050787510923bc01e689a2bd9cc..334c2ece855a92af6576b01ec87cd47af58b8540 100644 (file)
@@ -1955,8 +1955,9 @@ static void at76_dwork_hw_scan(struct work_struct *work)
 
 static int at76_hw_scan(struct ieee80211_hw *hw,
                        struct ieee80211_vif *vif,
-                       struct cfg80211_scan_request *req)
+                       struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct at76_priv *priv = hw->priv;
        struct at76_req_scan scan;
        u8 *ssid = NULL;
index a889fd66fc63190ec7f393ae8e64a6d117281267..fd9e5305e77fd263cb55ecb46ff08e6d06ca0935 100644 (file)
@@ -63,6 +63,7 @@ enum ath_op_flags {
        ATH_OP_PRIM_STA_VIF,
        ATH_OP_HW_RESET,
        ATH_OP_SCANNING,
+       ATH_OP_MULTI_CHANNEL,
 };
 
 enum ath_bus_type {
index d185dc0cd12b2f739535a6bc6fe77020b0764133..4333107ecf37b8325c55f31569361a9b537dddc6 100644 (file)
@@ -603,16 +603,19 @@ static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
                if (ret)
                        return ret;
 
-               src_ring->hw_index =
-                       ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
-               src_ring->hw_index &= nentries_mask;
+               read_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+               if (read_index == 0xffffffff)
+                       return -ENODEV;
+
+               read_index &= nentries_mask;
+               src_ring->hw_index = read_index;
 
                ath10k_pci_sleep(ar);
        }
 
        read_index = src_ring->hw_index;
 
-       if ((read_index == sw_index) || (read_index == 0xffffffff))
+       if (read_index == sw_index)
                return -EIO;
 
        sbase = src_ring->shadow_base;
index e6c56c5bb0f608c3c7377b06cb9d3ff491caee42..93adb8c5896926c787bb80e6c0869cd7f5fd4672 100644 (file)
@@ -802,7 +802,7 @@ int ath10k_core_start(struct ath10k *ar)
 
        INIT_LIST_HEAD(&ar->arvifs);
 
-       if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags))
+       if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) {
                ath10k_info("%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n",
                            ar->hw_params.name,
                            ar->target_version,
@@ -811,6 +811,12 @@ int ath10k_core_start(struct ath10k *ar)
                            ar->fw_api,
                            ar->htt.target_version_major,
                            ar->htt.target_version_minor);
+               ath10k_info("debug %d debugfs %d tracing %d dfs %d\n",
+                           config_enabled(CONFIG_ATH10K_DEBUG),
+                           config_enabled(CONFIG_ATH10K_DEBUGFS),
+                           config_enabled(CONFIG_ATH10K_TRACING),
+                           config_enabled(CONFIG_ATH10K_DFS_CERTIFIED));
+       }
 
        __set_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags);
 
@@ -988,7 +994,9 @@ err_unregister_mac:
 err_release_fw:
        ath10k_core_free_firmware_files(ar);
 err:
-       device_release_driver(ar->dev);
+       /* TODO: It's probably a good idea to release device from the driver
+        * but calling device_release_driver() here will cause a deadlock.
+        */
        return;
 }
 
index 68ceef61933db6b769122ab2500fa524619ac465..83a5fa91531d159b9095ef0fff37db55c38e0eeb 100644 (file)
@@ -290,6 +290,9 @@ struct ath10k_debug {
        struct ath_dfs_pool_stats dfs_pool_stats;
 
        u32 fw_dbglog_mask;
+
+       u8 htt_max_amsdu;
+       u8 htt_max_ampdu;
 };
 
 enum ath10k_state {
index 1b7ff4ba122ce42af61265eae30d8fb98d97201e..3030158c478e86cd9ee193af14b29da614f0d168 100644 (file)
@@ -671,6 +671,72 @@ static const struct file_operations fops_htt_stats_mask = {
        .llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file,
+                                              char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       char buf[64];
+       u8 amsdu = 3, ampdu = 64;
+       unsigned int len;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (ar->debug.htt_max_amsdu)
+               amsdu = ar->debug.htt_max_amsdu;
+
+       if (ar->debug.htt_max_ampdu)
+               ampdu = ar->debug.htt_max_ampdu;
+
+       mutex_unlock(&ar->conf_mutex);
+
+       len = scnprintf(buf, sizeof(buf), "%u %u\n", amsdu, ampdu);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_write_htt_max_amsdu_ampdu(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       int res;
+       char buf[64];
+       unsigned int amsdu, ampdu;
+
+       simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count);
+
+       /* make sure that buf is null terminated */
+       buf[sizeof(buf) - 1] = 0;
+
+       res = sscanf(buf, "%u %u", &amsdu, &ampdu);
+
+       if (res != 2)
+               return -EINVAL;
+
+       mutex_lock(&ar->conf_mutex);
+
+       res = ath10k_htt_h2t_aggr_cfg_msg(&ar->htt, ampdu, amsdu);
+       if (res)
+               goto out;
+
+       res = count;
+       ar->debug.htt_max_amsdu = amsdu;
+       ar->debug.htt_max_ampdu = ampdu;
+
+out:
+       mutex_unlock(&ar->conf_mutex);
+       return res;
+}
+
+static const struct file_operations fops_htt_max_amsdu_ampdu = {
+       .read = ath10k_read_htt_max_amsdu_ampdu,
+       .write = ath10k_write_htt_max_amsdu_ampdu,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 static ssize_t ath10k_read_fw_dbglog(struct file *file,
                                            char __user *user_buf,
                                            size_t count, loff_t *ppos)
@@ -757,6 +823,9 @@ void ath10k_debug_stop(struct ath10k *ar)
         * warning from del_timer(). */
        if (ar->debug.htt_stats_mask != 0)
                cancel_delayed_work(&ar->debug.htt_stats_dwork);
+
+       ar->debug.htt_max_amsdu = 0;
+       ar->debug.htt_max_ampdu = 0;
 }
 
 static ssize_t ath10k_write_simulate_radar(struct file *file,
@@ -867,6 +936,10 @@ int ath10k_debug_create(struct ath10k *ar)
        debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
                            ar, &fops_htt_stats_mask);
 
+       debugfs_create_file("htt_max_amsdu_ampdu", S_IRUSR | S_IWUSR,
+                           ar->debug.debugfs_phy, ar,
+                           &fops_htt_max_amsdu_ampdu);
+
        debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
                            ar, &fops_fw_dbglog);
 
index 9a263462c79353fa4b6a3c646ff6d025087c9463..6c93f3885ee571097eeb59f59e9d72e772c97ab7 100644 (file)
@@ -240,16 +240,10 @@ struct htt_oob_sync_req {
        __le16 rsvd0;
 } __packed;
 
-#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F
-#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB  0
-
 struct htt_aggr_conf {
        u8 max_num_ampdu_subframes;
-       union {
-               /* dont use bitfields; undefined behaviour */
-               u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */
-               u8 max_num_amsdu_subframes:5;
-       } __packed;
+       /* amsdu_subframes is limited by 0x1F mask */
+       u8 max_num_amsdu_subframes;
 } __packed;
 
 #define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32
@@ -1343,6 +1337,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb);
 int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
 int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie);
 int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
+int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
+                               u8 max_subfrms_ampdu,
+                               u8 max_subfrms_amsdu);
 
 void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
 int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
index 7064354d1f4f0aa82f4624a7d878dea220aa4b2a..accb6b4f6fafedea2a2102b5c43c39e4835840b8 100644 (file)
@@ -307,6 +307,52 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt)
        return 0;
 }
 
+int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
+                               u8 max_subfrms_ampdu,
+                               u8 max_subfrms_amsdu)
+{
+       struct htt_aggr_conf *aggr_conf;
+       struct sk_buff *skb;
+       struct htt_cmd *cmd;
+       int len;
+       int ret;
+
+       /* Firmware defaults are: amsdu = 3 and ampdu = 64 */
+
+       if (max_subfrms_ampdu == 0 || max_subfrms_ampdu > 64)
+               return -EINVAL;
+
+       if (max_subfrms_amsdu == 0 || max_subfrms_amsdu > 31)
+               return -EINVAL;
+
+       len = sizeof(cmd->hdr);
+       len += sizeof(cmd->aggr_conf);
+
+       skb = ath10k_htc_alloc_skb(len);
+       if (!skb)
+               return -ENOMEM;
+
+       skb_put(skb, len);
+       cmd = (struct htt_cmd *)skb->data;
+       cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_AGGR_CFG;
+
+       aggr_conf = &cmd->aggr_conf;
+       aggr_conf->max_num_ampdu_subframes = max_subfrms_ampdu;
+       aggr_conf->max_num_amsdu_subframes = max_subfrms_amsdu;
+
+       ath10k_dbg(ATH10K_DBG_HTT, "htt h2t aggr cfg msg amsdu %d ampdu %d",
+                  aggr_conf->max_num_amsdu_subframes,
+                  aggr_conf->max_num_ampdu_subframes);
+
+       ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
+       if (ret) {
+               dev_kfree_skb_any(skb);
+               return ret;
+       }
+
+       return 0;
+}
+
 int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
 {
        struct device *dev = htt->ar->dev;
index a21080028c54eeedff71b9b00a327620d9780dc3..b8314a534972a6fc51fac13e69c3ac26f059c2dc 100644 (file)
@@ -3137,10 +3137,11 @@ exit:
 
 static int ath10k_hw_scan(struct ieee80211_hw *hw,
                          struct ieee80211_vif *vif,
-                         struct cfg80211_scan_request *req)
+                         struct ieee80211_scan_request *hw_req)
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wmi_start_scan_arg arg;
        int ret = 0;
        int i;
index d0004d59c97ec6c9292266662d9fd93cdd9eaf5e..06840d101c45cb74c9fd0655fa943b623cf1f7c6 100644 (file)
@@ -1362,8 +1362,6 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
                ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr);
        }
 
-       init_completion(&xfer.done);
-
        ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0);
        if (ret)
                goto err_resp;
@@ -1414,10 +1412,7 @@ static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state)
                                          &nbytes, &transfer_id))
                return;
 
-       if (xfer->wait_for_resp)
-               return;
-
-       complete(&xfer->done);
+       xfer->tx_done = true;
 }
 
 static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
@@ -1438,7 +1433,7 @@ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state)
        }
 
        xfer->resp_len = nbytes;
-       complete(&xfer->done);
+       xfer->rx_done = true;
 }
 
 static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
@@ -1451,7 +1446,7 @@ static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe,
                ath10k_pci_bmi_send_done(tx_pipe);
                ath10k_pci_bmi_recv_data(rx_pipe);
 
-               if (completion_done(&xfer->done))
+               if (xfer->tx_done && (xfer->rx_done == xfer->wait_for_resp))
                        return 0;
 
                schedule();
index dfdebb4157aa177acde13ea1149b5a7490c5593e..940129209990337a1a99f79599014abc48b6b605 100644 (file)
@@ -38,7 +38,8 @@
 #define DIAG_TRANSFER_LIMIT 2048
 
 struct bmi_xfer {
-       struct completion done;
+       bool tx_done;
+       bool rx_done;
        bool wait_for_resp;
        u32 resp_len;
 };
index 4b7782a529ac6b03c6a1bcd764dabc6568b70a6b..6f83cae5765513eeb6b3efb803a657faa6bc5703 100644 (file)
@@ -2106,7 +2106,6 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_event_id id;
-       u16 len;
 
        cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
        id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
@@ -2114,8 +2113,6 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb)
        if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
                return;
 
-       len = skb->len;
-
        trace_ath10k_wmi_event(id, skb->data, skb->len);
 
        switch (id) {
@@ -2225,7 +2222,6 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
 {
        struct wmi_cmd_hdr *cmd_hdr;
        enum wmi_10x_event_id id;
-       u16 len;
 
        cmd_hdr = (struct wmi_cmd_hdr *)skb->data;
        id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID);
@@ -2233,8 +2229,6 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb)
        if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)
                return;
 
-       len = skb->len;
-
        trace_ath10k_wmi_event(id, skb->data, skb->len);
 
        switch (id) {
index 74bd54d6acebaeb2052d0184898a6b7e13f27a49..85316bb3f8c66a51baffad61a5b306337f4ec04f 100644 (file)
@@ -1285,6 +1285,7 @@ struct ath5k_hw {
 #define ATH_STAT_STARTED       3               /* opened & irqs enabled */
 
        unsigned int            filter_flags;   /* HW flags, AR5K_RX_FILTER_* */
+       unsigned int            fif_filter_flags; /* Current FIF_* filter flags */
        struct ieee80211_channel *curchan;      /* current h/w channel */
 
        u16                     nvifs;
index 4b18434ba697c20c048e457bcdbc1c788c41da9b..8ad2550bce7ff4820511a606a30d10f81e09b91d 100644 (file)
@@ -1382,6 +1382,9 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
        rxs->flag = 0;
        if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
                rxs->flag |= RX_FLAG_MMIC_ERROR;
+       if (unlikely(rs->rs_status & AR5K_RXERR_CRC))
+               rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
+
 
        /*
         * always extend the mac timestamp, since this information is
@@ -1449,6 +1452,8 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
        ah->stats.rx_bytes_count += rs->rs_datalen;
 
        if (unlikely(rs->rs_status)) {
+               unsigned int filters;
+
                if (rs->rs_status & AR5K_RXERR_CRC)
                        ah->stats.rxerr_crc++;
                if (rs->rs_status & AR5K_RXERR_FIFO)
@@ -1457,7 +1462,20 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
                        ah->stats.rxerr_phy++;
                        if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32)
                                ah->stats.rxerr_phy_code[rs->rs_phyerr]++;
-                       return false;
+
+                       /*
+                        * Treat packets that underwent a CCK or OFDM reset as having a bad CRC.
+                        * These restarts happen when the radio resynchronizes to a stronger frame
+                        * while receiving a weaker frame. Here we receive the prefix of the weak
+                        * frame. Since these are incomplete packets, mark their CRC as invalid.
+                        */
+                       if (rs->rs_phyerr == AR5K_RX_PHY_ERROR_OFDM_RESTART ||
+                           rs->rs_phyerr == AR5K_RX_PHY_ERROR_CCK_RESTART) {
+                               rs->rs_status |= AR5K_RXERR_CRC;
+                               rs->rs_status &= ~AR5K_RXERR_PHY;
+                       } else {
+                               return false;
+                       }
                }
                if (rs->rs_status & AR5K_RXERR_DECRYPT) {
                        /*
@@ -1480,8 +1498,15 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
                        return true;
                }
 
-               /* reject any frames with non-crypto errors */
-               if (rs->rs_status & ~(AR5K_RXERR_DECRYPT))
+               /*
+                * Reject any frames with non-crypto errors, and take into account the
+                * current FIF_* filters.
+                */
+               filters = AR5K_RXERR_DECRYPT;
+               if (ah->fif_filter_flags & FIF_FCSFAIL)
+                       filters |= AR5K_RXERR_CRC;
+
+               if (rs->rs_status & ~filters)
                        return false;
        }
 
index afb23b3cc7be64d6b0ae8f79d46a3a9c9d07eab3..b65c38fdaa4b23723ac8f59add51e46acc7d16f1 100644 (file)
@@ -473,6 +473,8 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
        /* Set the cached hw filter flags, this will later actually
         * be set in HW */
        ah->filter_flags = rfilt;
+       /* Store current FIF filter flags */
+       ah->fif_filter_flags = *new_flags;
 
        mutex_unlock(&ah->lock);
 }
index 18fdd69e1f718dfd8eae5551d8fad9131613651f..397a52f2628b74e1f42a63c9a67d93f9c2407ffc 100644 (file)
@@ -242,7 +242,8 @@ struct ath6kl_bmi_target_info {
                (void) (check_type == val);                             \
                addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item));      \
                ret = ath6kl_bmi_read(ar, addr, (u8 *) &tmp, 4);        \
-               *val = le32_to_cpu(tmp);                                \
+               if (!ret)                                               \
+                       *val = le32_to_cpu(tmp);                        \
                ret;                                                    \
        })
 
index 0e26f4a34fda329910ecc278de9fe7bea8fa6c57..d139f2a7160a28120d423483070616529630e950 100644 (file)
@@ -2899,7 +2899,8 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        if (info->inactivity_timeout) {
                inactivity_timeout = info->inactivity_timeout;
 
-               if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS)
+               if (test_bit(ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS,
+                            ar->fw_capabilities))
                        inactivity_timeout = DIV_ROUND_UP(inactivity_timeout,
                                                          60);
 
@@ -3782,7 +3783,8 @@ int ath6kl_cfg80211_init(struct ath6kl *ar)
                ath6kl_band_5ghz.ht_cap.ht_supported = false;
        }
 
-       if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) {
+       if (test_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES,
+                    ar->fw_capabilities)) {
                ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
                ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff;
                ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
index b0b6520427600a05687e299318f78f873e3fd948..0df74b245af4c0180ea9219c8765453241aea9f0 100644 (file)
@@ -123,6 +123,22 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
 
        /* FIXME: we should free all firmwares in the error cases below */
 
+       /*
+        * Backwards compatibility support for older ar6004 firmware images
+        * which do not set these feature flags.
+        */
+       if (ar->target_type == TARGET_TYPE_AR6004 &&
+           ar->fw_api <= 4) {
+               __set_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES,
+                         ar->fw_capabilities);
+               __set_bit(ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS,
+                         ar->fw_capabilities);
+
+               if (ar->hw.id == AR6004_HW_1_3_VERSION)
+                       __set_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT,
+                                 ar->fw_capabilities);
+       }
+
        /* Indicate that WMI is enabled (although not ready yet) */
        set_bit(WMI_ENABLED, &ar->flag);
        ar->wmi = ath6kl_wmi_init(ar);
index 26b0f92424e16fb91beca55f3f07e9d907a9db04..2b78c863d03095b035991d3d123ea375b35a4cb6 100644 (file)
@@ -136,6 +136,21 @@ enum ath6kl_fw_capability {
         */
        ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL,
 
+       /* WMI_SET_TX_SELECT_RATES_CMDID uses 64 bit size rate table */
+       ATH6KL_FW_CAPABILITY_64BIT_RATES,
+
+       /* WMI_AP_CONN_INACT_CMDID uses minutes as units */
+       ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS,
+
+       /* use low priority endpoint for all data */
+       ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT,
+
+       /* ratetable is the 2 stream version (max MCS15) */
+       ATH6KL_FW_CAPABILITY_RATETABLE_MCS15,
+
+       /* firmare doesn't support IP checksumming */
+       ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM,
+
        /* this needs to be last */
        ATH6KL_FW_CAPABILITY_MAX,
 };
@@ -149,15 +164,13 @@ struct ath6kl_fw_ie {
 };
 
 enum ath6kl_hw_flags {
-       ATH6KL_HW_64BIT_RATES           = BIT(0),
-       ATH6KL_HW_AP_INACTIVITY_MINS    = BIT(1),
-       ATH6KL_HW_MAP_LP_ENDPOINT       = BIT(2),
        ATH6KL_HW_SDIO_CRC_ERROR_WAR    = BIT(3),
 };
 
 #define ATH6KL_FW_API2_FILE "fw-2.bin"
 #define ATH6KL_FW_API3_FILE "fw-3.bin"
 #define ATH6KL_FW_API4_FILE "fw-4.bin"
+#define ATH6KL_FW_API5_FILE "fw-5.bin"
 
 /* AR6003 1.0 definitions */
 #define AR6003_HW_1_0_VERSION                 0x300002ba
@@ -215,8 +228,21 @@ enum ath6kl_hw_flags {
 #define AR6004_HW_1_3_VERSION                  0x31c8088a
 #define AR6004_HW_1_3_FW_DIR                   "ath6k/AR6004/hw1.3"
 #define AR6004_HW_1_3_FIRMWARE_FILE            "fw.ram.bin"
-#define AR6004_HW_1_3_BOARD_DATA_FILE          "ath6k/AR6004/hw1.3/bdata.bin"
-#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE  "ath6k/AR6004/hw1.3/bdata.bin"
+#define AR6004_HW_1_3_TCMD_FIRMWARE_FILE       "utf.bin"
+#define AR6004_HW_1_3_UTF_FIRMWARE_FILE                "utf.bin"
+#define AR6004_HW_1_3_TESTSCRIPT_FILE          "nullTestFlow.bin"
+#define AR6004_HW_1_3_BOARD_DATA_FILE        AR6004_HW_1_3_FW_DIR "/bdata.bin"
+#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE AR6004_HW_1_3_FW_DIR "/bdata.bin"
+
+/* AR6004 3.0 definitions */
+#define AR6004_HW_3_0_VERSION                  0x31C809F8
+#define AR6004_HW_3_0_FW_DIR                   "ath6k/AR6004/hw3.0"
+#define AR6004_HW_3_0_FIRMWARE_FILE            "fw.ram.bin"
+#define AR6004_HW_3_0_TCMD_FIRMWARE_FILE       "utf.bin"
+#define AR6004_HW_3_0_UTF_FIRMWARE_FILE                "utf.bin"
+#define AR6004_HW_3_0_TESTSCRIPT_FILE          "nullTestFlow.bin"
+#define AR6004_HW_3_0_BOARD_DATA_FILE        AR6004_HW_3_0_FW_DIR "/bdata.bin"
+#define AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE AR6004_HW_3_0_FW_DIR "/bdata.bin"
 
 /* Per STA data, used in AP mode */
 #define STA_PS_AWAKE           BIT(0)
index 756fe52a12c8ad5a3496de58bbfec01d0ad9820b..ca1a18c86c0d79d64ad93f26f42fdaf87bcb6411 100644 (file)
@@ -1170,8 +1170,12 @@ static int htc_wait_recv_ctrl_message(struct htc_target *target)
 static void htc_rxctrl_complete(struct htc_target *context,
                                struct htc_packet *packet)
 {
-       /* TODO, can't really receive HTC control messages yet.... */
-       ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function\n", __func__);
+       struct sk_buff *skb = packet->skb;
+
+       if (packet->endpoint == ENDPOINT_0 &&
+           packet->status == -ECANCELED &&
+           skb != NULL)
+               dev_kfree_skb(skb);
 }
 
 /* htc pipe initialization */
@@ -1678,7 +1682,29 @@ static void ath6kl_htc_pipe_activity_changed(struct htc_target *target,
 
 static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target)
 {
-       /* TODO */
+       struct htc_endpoint *endpoint;
+       struct htc_packet *packet, *tmp_pkt;
+       int i;
+
+       for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
+               endpoint = &target->endpoint[i];
+
+               spin_lock_bh(&target->rx_lock);
+
+               list_for_each_entry_safe(packet, tmp_pkt,
+                                        &endpoint->rx_bufq, list) {
+                       list_del(&packet->list);
+                       spin_unlock_bh(&target->rx_lock);
+                       ath6kl_dbg(ATH6KL_DBG_HTC,
+                                  "htc rx flush pkt 0x%p len %d ep %d\n",
+                                  packet, packet->buf_len,
+                                  packet->endpoint);
+                       dev_kfree_skb(packet->pkt_cntxt);
+                       spin_lock_bh(&target->rx_lock);
+               }
+
+               spin_unlock_bh(&target->rx_lock);
+       }
 }
 
 static int ath6kl_htc_pipe_credit_setup(struct htc_target *target,
index d5ef211f261c2c19e6e8deeef985dc2b83130794..a61118484de6171b04ad004a0bb3a1be73d12c29 100644 (file)
@@ -93,8 +93,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x433900,
                .refclk_hz                      = 26000000,
                .uarttx_pin                     = 11,
-               .flags                          = ATH6KL_HW_64BIT_RATES |
-                                                 ATH6KL_HW_AP_INACTIVITY_MINS,
+               .flags                          = 0,
 
                .fw = {
                        .dir            = AR6004_HW_1_0_FW_DIR,
@@ -114,8 +113,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x43d400,
                .refclk_hz                      = 40000000,
                .uarttx_pin                     = 11,
-               .flags                          = ATH6KL_HW_64BIT_RATES |
-                                                 ATH6KL_HW_AP_INACTIVITY_MINS,
+               .flags                          = 0,
                .fw = {
                        .dir            = AR6004_HW_1_1_FW_DIR,
                        .fw             = AR6004_HW_1_1_FIRMWARE_FILE,
@@ -134,8 +132,7 @@ static const struct ath6kl_hw hw_list[] = {
                .board_addr                     = 0x435c00,
                .refclk_hz                      = 40000000,
                .uarttx_pin                     = 11,
-               .flags                          = ATH6KL_HW_64BIT_RATES |
-                                                 ATH6KL_HW_AP_INACTIVITY_MINS,
+               .flags                          = 0,
 
                .fw = {
                        .dir            = AR6004_HW_1_2_FW_DIR,
@@ -152,20 +149,43 @@ static const struct ath6kl_hw hw_list[] = {
                .board_ext_data_addr            = 0x437000,
                .reserved_ram_size              = 7168,
                .board_addr                     = 0x436400,
-               .refclk_hz                      = 40000000,
+               .refclk_hz                      = 0,
                .uarttx_pin                     = 11,
-               .flags                          = ATH6KL_HW_64BIT_RATES |
-                                                 ATH6KL_HW_AP_INACTIVITY_MINS |
-                                                 ATH6KL_HW_MAP_LP_ENDPOINT,
+               .flags                          = 0,
 
                .fw = {
                        .dir            = AR6004_HW_1_3_FW_DIR,
                        .fw             = AR6004_HW_1_3_FIRMWARE_FILE,
+                       .tcmd           = AR6004_HW_1_3_TCMD_FIRMWARE_FILE,
+                       .utf            = AR6004_HW_1_3_UTF_FIRMWARE_FILE,
+                       .testscript     = AR6004_HW_1_3_TESTSCRIPT_FILE,
                },
 
                .fw_board               = AR6004_HW_1_3_BOARD_DATA_FILE,
                .fw_default_board       = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE,
        },
+       {
+               .id                             = AR6004_HW_3_0_VERSION,
+               .name                           = "ar6004 hw 3.0",
+               .dataset_patch_addr             = 0,
+               .app_load_addr                  = 0x1234,
+               .board_ext_data_addr            = 0,
+               .reserved_ram_size              = 7168,
+               .board_addr                     = 0x436400,
+               .testscript_addr                = 0,
+               .flags                          = 0,
+
+               .fw = {
+                       .dir            = AR6004_HW_3_0_FW_DIR,
+                       .fw             = AR6004_HW_3_0_FIRMWARE_FILE,
+                       .tcmd           = AR6004_HW_3_0_TCMD_FIRMWARE_FILE,
+                       .utf            = AR6004_HW_3_0_UTF_FIRMWARE_FILE,
+                       .testscript     = AR6004_HW_3_0_TESTSCRIPT_FILE,
+               },
+
+               .fw_board               = AR6004_HW_3_0_BOARD_DATA_FILE,
+               .fw_default_board       = AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE,
+       },
 };
 
 /*
@@ -601,7 +621,9 @@ int ath6kl_configure_target(struct ath6kl *ar)
         * but possible in theory.
         */
 
-       if (ar->target_type == TARGET_TYPE_AR6003) {
+       if ((ar->target_type == TARGET_TYPE_AR6003) ||
+           (ar->version.target_ver == AR6004_HW_1_3_VERSION) ||
+           (ar->version.target_ver == AR6004_HW_3_0_VERSION)) {
                param = ar->hw.board_ext_data_addr;
                ram_reserved_size = ar->hw.reserved_ram_size;
 
@@ -629,9 +651,12 @@ int ath6kl_configure_target(struct ath6kl *ar)
                return status;
 
        /* Configure target refclk_hz */
-       status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, ar->hw.refclk_hz);
-       if (status)
-               return status;
+       if (ar->hw.refclk_hz != 0) {
+               status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz,
+                                              ar->hw.refclk_hz);
+               if (status)
+                       return status;
+       }
 
        return 0;
 }
@@ -1112,6 +1137,12 @@ int ath6kl_init_fetch_firmwares(struct ath6kl *ar)
        if (ret)
                return ret;
 
+       ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API5_FILE);
+       if (ret == 0) {
+               ar->fw_api = 5;
+               goto out;
+       }
+
        ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API4_FILE);
        if (ret == 0) {
                ar->fw_api = 4;
@@ -1161,11 +1192,19 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
                ath6kl_bmi_write_hi32(ar, hi_board_data,
                                      board_address);
        } else {
-               ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address);
+               ret = ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address);
+               if (ret) {
+                       ath6kl_err("Failed to get board file target address.\n");
+                       return ret;
+               }
        }
 
        /* determine where in target ram to write extended board data */
-       ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address);
+       ret = ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address);
+       if (ret) {
+               ath6kl_err("Failed to get extended board file target address.\n");
+               return ret;
+       }
 
        if (ar->target_type == TARGET_TYPE_AR6003 &&
            board_ext_address == 0) {
@@ -1230,7 +1269,13 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)
        }
 
        /* record the fact that Board Data IS initialized */
-       ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, 1);
+       if ((ar->version.target_ver == AR6004_HW_1_3_VERSION) ||
+           (ar->version.target_ver == AR6004_HW_3_0_VERSION))
+               param = board_data_size;
+       else
+               param = 1;
+
+       ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, param);
 
        return ret;
 }
@@ -1361,7 +1406,11 @@ static int ath6kl_upload_testscript(struct ath6kl *ar)
        }
 
        ath6kl_bmi_write_hi32(ar, hi_ota_testscript, address);
-       ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096);
+
+       if ((ar->version.target_ver != AR6004_HW_1_3_VERSION) &&
+           (ar->version.target_ver != AR6004_HW_3_0_VERSION))
+               ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096);
+
        ath6kl_bmi_write_hi32(ar, hi_test_apps_related, 1);
 
        return 0;
@@ -1567,6 +1616,11 @@ static const struct fw_capa_str_map {
        { ATH6KL_FW_CAPABILITY_REGDOMAIN, "regdomain" },
        { ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, "sched-scan-v2" },
        { ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, "hb-poll" },
+       { ATH6KL_FW_CAPABILITY_64BIT_RATES, "64bit-rates" },
+       { ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, "ap-inactivity-mins" },
+       { ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, "map-lp-endpoint" },
+       { ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, "ratetable-mcs15" },
+       { ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM, "no-ip-checksum" },
 };
 
 static const char *ath6kl_init_get_fw_capa_name(unsigned int id)
index d56554674da47924477c02423ad08c50caef2918..21516bc657857b8a44405c5c22944a275271e5f2 100644 (file)
@@ -702,6 +702,7 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
        struct ath6kl *ar = vif->ar;
        struct target_stats *stats = &vif->target_stats;
        struct tkip_ccmp_stats *ccmp_stats;
+       s32 rate;
        u8 ac;
 
        if (len < sizeof(*tgt_stats))
@@ -731,8 +732,9 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
                le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt);
        stats->tx_rts_fail_cnt +=
                le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt);
-       stats->tx_ucast_rate =
-           ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate));
+
+       rate = a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate);
+       stats->tx_ucast_rate = ath6kl_wmi_get_rate(ar->wmi, rate);
 
        stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt);
        stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte);
@@ -749,8 +751,9 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len)
                le32_to_cpu(tgt_stats->stats.rx.key_cache_miss);
        stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err);
        stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame);
-       stats->rx_ucast_rate =
-           ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate));
+
+       rate = a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate);
+       stats->rx_ucast_rate = ath6kl_wmi_get_rate(ar->wmi, rate);
 
        ccmp_stats = &tgt_stats->stats.tkip_ccmp_stats;
 
@@ -1290,6 +1293,8 @@ static const struct net_device_ops ath6kl_netdev_ops = {
 
 void init_netdev(struct net_device *dev)
 {
+       struct ath6kl *ar = ath6kl_priv(dev);
+
        dev->netdev_ops = &ath6kl_netdev_ops;
        dev->destructor = free_netdev;
        dev->watchdog_timeo = ATH6KL_TX_TIMEOUT;
@@ -1301,7 +1306,9 @@ void init_netdev(struct net_device *dev)
                                        WMI_MAX_TX_META_SZ +
                                        ATH6KL_HTC_ALIGN_BYTES, 4);
 
-       dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+       if (!test_bit(ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM,
+                     ar->fw_capabilities))
+               dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
 
        return;
 }
index 3afc5a463d06f822f339250deecfb1cefadea592..c44325856b81fc4cee191ec9aceb2102f2f0b23b 100644 (file)
@@ -802,7 +802,8 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id,
                break;
        case WMI_DATA_VI_SVC:
 
-               if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT)
+               if (test_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT,
+                            ar->fw_capabilities))
                        *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP;
                else
                        *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP;
@@ -814,7 +815,8 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id,
                break;
        case WMI_DATA_VO_SVC:
 
-               if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT)
+               if (test_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT,
+                            ar->fw_capabilities))
                        *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP;
                else
                        *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP;
@@ -1208,6 +1210,7 @@ static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf)
 
 /* table of devices that work with this driver */
 static struct usb_device_id ath6kl_usb_ids[] = {
+       {USB_DEVICE(0x0cf3, 0x9375)},
        {USB_DEVICE(0x0cf3, 0x9374)},
        { /* Terminating entry */ },
 };
index 4d7f9e4712e991deea8553f7505a7f79b9a6f6e7..94df345d08c24210fefe4b707079f85fe75e386f 100644 (file)
@@ -59,6 +59,55 @@ static const s32 wmi_rate_tbl[][2] = {
        {0, 0}
 };
 
+static const s32 wmi_rate_tbl_mcs15[][2] = {
+       /* {W/O SGI, with SGI} */
+       {1000, 1000},
+       {2000, 2000},
+       {5500, 5500},
+       {11000, 11000},
+       {6000, 6000},
+       {9000, 9000},
+       {12000, 12000},
+       {18000, 18000},
+       {24000, 24000},
+       {36000, 36000},
+       {48000, 48000},
+       {54000, 54000},
+       {6500, 7200},     /* HT 20, MCS 0 */
+       {13000, 14400},
+       {19500, 21700},
+       {26000, 28900},
+       {39000, 43300},
+       {52000, 57800},
+       {58500, 65000},
+       {65000, 72200},
+       {13000, 14400},   /* HT 20, MCS 8 */
+       {26000, 28900},
+       {39000, 43300},
+       {52000, 57800},
+       {78000, 86700},
+       {104000, 115600},
+       {117000, 130000},
+       {130000, 144400}, /* HT 20, MCS 15 */
+       {13500, 15000},   /*HT 40, MCS 0 */
+       {27000, 30000},
+       {40500, 45000},
+       {54000, 60000},
+       {81000, 90000},
+       {108000, 120000},
+       {121500, 135000},
+       {135000, 150000},
+       {27000, 30000},   /*HT 40, MCS 8 */
+       {54000, 60000},
+       {81000, 90000},
+       {108000, 120000},
+       {162000, 180000},
+       {216000, 240000},
+       {243000, 270000},
+       {270000, 300000}, /*HT 40, MCS 15 */
+       {0, 0}
+};
+
 /* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */
 static const u8 up_to_ac[] = {
        WMM_AC_BE,
@@ -2838,7 +2887,8 @@ int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx,
 {
        struct ath6kl *ar = wmi->parent_dev;
 
-       if (ar->hw.flags & ATH6KL_HW_64BIT_RATES)
+       if (test_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES,
+                    ar->fw_capabilities))
                return ath6kl_set_bitrate_mask64(wmi, if_idx, mask);
        else
                return ath6kl_set_bitrate_mask32(wmi, if_idx, mask);
@@ -3279,9 +3329,11 @@ int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2)
                                   NO_SYNC_WMIFLAG);
 }
 
-s32 ath6kl_wmi_get_rate(s8 rate_index)
+s32 ath6kl_wmi_get_rate(struct wmi *wmi, s8 rate_index)
 {
+       struct ath6kl *ar = wmi->parent_dev;
        u8 sgi = 0;
+       s32 ret;
 
        if (rate_index == RATE_AUTO)
                return 0;
@@ -3292,10 +3344,20 @@ s32 ath6kl_wmi_get_rate(s8 rate_index)
                sgi = 1;
        }
 
-       if (WARN_ON(rate_index > RATE_MCS_7_40))
-               rate_index = RATE_MCS_7_40;
+       if (test_bit(ATH6KL_FW_CAPABILITY_RATETABLE_MCS15,
+                    ar->fw_capabilities)) {
+               if (WARN_ON(rate_index >= ARRAY_SIZE(wmi_rate_tbl_mcs15)))
+                       return 0;
+
+               ret = wmi_rate_tbl_mcs15[(u32) rate_index][sgi];
+       } else {
+               if (WARN_ON(rate_index >= ARRAY_SIZE(wmi_rate_tbl)))
+                       return 0;
 
-       return wmi_rate_tbl[(u32) rate_index][sgi];
+               ret = wmi_rate_tbl[(u32) rate_index][sgi];
+       }
+
+       return ret;
 }
 
 static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap,
index bb23fc00111dc6c3329762ee976ce93489015b90..19f88b4a24fbcef48bb31888abcd92a5bb0ce354 100644 (file)
@@ -2632,7 +2632,7 @@ int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx,
                             struct ath6kl_htcap *htcap);
 int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len);
 
-s32 ath6kl_wmi_get_rate(s8 rate_index);
+s32 ath6kl_wmi_get_rate(struct wmi *wmi, s8 rate_index);
 
 int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx,
                          __be32 ips0, __be32 ips1);
index 8fcd586d1c3980c413c8499cb3aefd518f507f69..6b4020a5798477e3ab21b22ce2219aea3cf6553c 100644 (file)
@@ -5,7 +5,8 @@ ath9k-y +=      beacon.o \
                recv.o \
                xmit.o \
                link.o \
-               antenna.o
+               antenna.o \
+               channel.o
 
 ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
 ath9k-$(CONFIG_ATH9K_PCI) += pci.o
index 741b38ddcb378e8cb17dbfe0f79f33516be75527..59af9f9712da852e0ebf4e05747b35da7373dbeb 100644 (file)
@@ -281,7 +281,7 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
 
        ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen)
                | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
-               | SM(i->txpower, AR_XmitPower)
+               | SM(i->txpower, AR_XmitPower0)
                | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
                | (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
                | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
@@ -306,6 +306,10 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
                | set11nRateFlags(i->rates, 2)
                | set11nRateFlags(i->rates, 3)
                | SM(i->rtscts_rate, AR_RTSCTSRate);
+
+       ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1);
+       ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2);
+       ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3);
 }
 
 static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
index 235053ba773765392d08427916bfb398ff52d053..80c6eacbda53349727fe4bfba72e32eb2abe28fd 100644 (file)
@@ -3535,7 +3535,8 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz)
 {
        int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl;
 
-       if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah))
+       if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
+           AR_SREV_9531(ah))
                REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias);
        else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah))
                REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias);
index ec1da0cc25f53b14412414f2d3aebef7eb1224cc..ddef9eedbac6f0d5e0b2ea2d0bfacda4f96e63c1 100644 (file)
@@ -314,10 +314,17 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
                               qca953x_1p0_mac_core);
                INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
                               qca953x_1p0_mac_postamble);
-               INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
-                              qca953x_1p0_baseband_core);
-               INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
-                              qca953x_1p0_baseband_postamble);
+               if (AR_SREV_9531_20(ah)) {
+                       INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+                                      qca953x_2p0_baseband_core);
+                       INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+                                      qca953x_2p0_baseband_postamble);
+               } else {
+                       INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+                                      qca953x_1p0_baseband_core);
+                       INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+                                      qca953x_1p0_baseband_postamble);
+               }
                INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
                               qca953x_1p0_radio_core);
                INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],
index 729ffbf07343bb72c9313f037cc5965e58d8366f..71e38e85aa99801546e8e64332bbce3458edd604 100644 (file)
@@ -101,7 +101,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
 
        ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen)
                | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
-               | SM(i->txpower, AR_XmitPower)
+               | SM(i->txpower, AR_XmitPower0)
                | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
                | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
                | (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0)
@@ -151,6 +151,10 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
                | SM(i->rtscts_rate, AR_RTSCTSRate);
 
        ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
+
+       ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1);
+       ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2);
+       ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3);
 }
 
 static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
index 8927fc34d84c2f009e192dc9a37cb140be941e54..542a8d51d3b033bd21521a6b34972107eb44ff04 100644 (file)
@@ -1552,13 +1552,15 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
                                      u8 *ini_reloaded)
 {
        unsigned int regWrites = 0;
-       u32 modesIndex;
+       u32 modesIndex, txgain_index;
 
        if (IS_CHAN_5GHZ(chan))
                modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
        else
                modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
 
+       txgain_index = AR_SREV_9531(ah) ? 1 : modesIndex;
+
        if (modesIndex == ah->modes_index) {
                *ini_reloaded = false;
                goto set_rfmode;
@@ -1573,7 +1575,7 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
                ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant,
                                   modesIndex);
 
-       REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
+       REG_WRITE_ARRAY(&ah->iniModesTxGain, txgain_index, regWrites);
 
        if (AR_SREV_9462_20_OR_LATER(ah)) {
                /*
index 8e5c3b9786e3ac3fab8cf42e0d80bf84f268d4d2..812a9d787bf353236ebc906450583abce3c58a97 100644 (file)
@@ -219,7 +219,7 @@ static const u32 qca953x_1p0_baseband_core[][2] = {
        {0x00009d04, 0x40206c10},
        {0x00009d08, 0x009c4060},
        {0x00009d0c, 0x9883800a},
-       {0x00009d10, 0x01884061},
+       {0x00009d10, 0x018848c6},
        {0x00009d14, 0x00c0040b},
        {0x00009d18, 0x00000000},
        {0x00009e08, 0x0038230c},
@@ -715,4 +715,203 @@ static const u32 qca953x_1p1_modes_no_xpa_tx_gain_table[][2] = {
        {0x00016448, 0x6c927a70},
 };
 
+static const u32 qca953x_2p0_baseband_core[][2] = {
+       /* Addr      allmodes */
+       {0x00009800, 0xafe68e30},
+       {0x00009804, 0xfd14e000},
+       {0x00009808, 0x9c0a9f6b},
+       {0x0000980c, 0x04900000},
+       {0x00009814, 0x0280c00a},
+       {0x00009818, 0x00000000},
+       {0x0000981c, 0x00020028},
+       {0x00009834, 0x6400a190},
+       {0x00009838, 0x0108ecff},
+       {0x0000983c, 0x14000600},
+       {0x00009880, 0x201fff00},
+       {0x00009884, 0x00001042},
+       {0x000098a4, 0x00200400},
+       {0x000098b0, 0x32840bbe},
+       {0x000098bc, 0x00000002},
+       {0x000098d0, 0x004b6a8e},
+       {0x000098d4, 0x00000820},
+       {0x000098dc, 0x00000000},
+       {0x000098f0, 0x00000000},
+       {0x000098f4, 0x00000000},
+       {0x00009c04, 0xff55ff55},
+       {0x00009c08, 0x0320ff55},
+       {0x00009c0c, 0x00000000},
+       {0x00009c10, 0x00000000},
+       {0x00009c14, 0x00046384},
+       {0x00009c18, 0x05b6b440},
+       {0x00009c1c, 0x00b6b440},
+       {0x00009d00, 0xc080a333},
+       {0x00009d04, 0x40206c10},
+       {0x00009d08, 0x009c4060},
+       {0x00009d0c, 0x9883800a},
+       {0x00009d10, 0x018848c6},
+       {0x00009d14, 0x00c0040b},
+       {0x00009d18, 0x00000000},
+       {0x00009e08, 0x0038230c},
+       {0x00009e24, 0x990bb515},
+       {0x00009e28, 0x0c6f0000},
+       {0x00009e30, 0x06336f77},
+       {0x00009e34, 0x6af6532f},
+       {0x00009e38, 0x0cc80c00},
+       {0x00009e40, 0x0d261820},
+       {0x00009e4c, 0x00001004},
+       {0x00009e50, 0x00ff03f1},
+       {0x00009fc0, 0x813e4788},
+       {0x00009fc4, 0x0001efb5},
+       {0x00009fcc, 0x40000014},
+       {0x00009fd0, 0x02993b93},
+       {0x0000a20c, 0x00000000},
+       {0x0000a220, 0x00000000},
+       {0x0000a224, 0x00000000},
+       {0x0000a228, 0x10002310},
+       {0x0000a23c, 0x00000000},
+       {0x0000a244, 0x0c000000},
+       {0x0000a248, 0x00000140},
+       {0x0000a2a0, 0x00000007},
+       {0x0000a2c0, 0x00000007},
+       {0x0000a2c8, 0x00000000},
+       {0x0000a2d4, 0x00000000},
+       {0x0000a2ec, 0x00000000},
+       {0x0000a2f0, 0x00000000},
+       {0x0000a2f4, 0x00000000},
+       {0x0000a2f8, 0x00000000},
+       {0x0000a344, 0x00000000},
+       {0x0000a34c, 0x00000000},
+       {0x0000a350, 0x0000a000},
+       {0x0000a364, 0x00000000},
+       {0x0000a370, 0x00000000},
+       {0x0000a390, 0x00000001},
+       {0x0000a394, 0x00000444},
+       {0x0000a398, 0x001f0e0f},
+       {0x0000a39c, 0x0075393f},
+       {0x0000a3a0, 0xb79f6427},
+       {0x0000a3a4, 0x000400ff},
+       {0x0000a3a8, 0x6a6a6a6a},
+       {0x0000a3ac, 0x6a6a6a6a},
+       {0x0000a3b0, 0x00c8641a},
+       {0x0000a3b4, 0x0000001a},
+       {0x0000a3b8, 0x0088642a},
+       {0x0000a3bc, 0x000001fa},
+       {0x0000a3c0, 0x20202020},
+       {0x0000a3c4, 0x22222220},
+       {0x0000a3c8, 0x20200020},
+       {0x0000a3cc, 0x20202020},
+       {0x0000a3d0, 0x20202020},
+       {0x0000a3d4, 0x20202020},
+       {0x0000a3d8, 0x20202020},
+       {0x0000a3dc, 0x20202020},
+       {0x0000a3e0, 0x20202020},
+       {0x0000a3e4, 0x20202020},
+       {0x0000a3e8, 0x20202020},
+       {0x0000a3ec, 0x20202020},
+       {0x0000a3f0, 0x00000000},
+       {0x0000a3f4, 0x00000000},
+       {0x0000a3f8, 0x0c9bd380},
+       {0x0000a3fc, 0x000f0f01},
+       {0x0000a400, 0x8fa91f01},
+       {0x0000a404, 0x00000000},
+       {0x0000a408, 0x0e79e5c6},
+       {0x0000a40c, 0x00820820},
+       {0x0000a414, 0x1ce42108},
+       {0x0000a418, 0x2d001dce},
+       {0x0000a41c, 0x1ce73908},
+       {0x0000a420, 0x000001ce},
+       {0x0000a424, 0x1ce738e7},
+       {0x0000a428, 0x000001ce},
+       {0x0000a42c, 0x1ce739ce},
+       {0x0000a430, 0x1ce739ce},
+       {0x0000a434, 0x00000000},
+       {0x0000a438, 0x00001801},
+       {0x0000a43c, 0x00100000},
+       {0x0000a444, 0x00000000},
+       {0x0000a448, 0x05000080},
+       {0x0000a44c, 0x00000001},
+       {0x0000a450, 0x00010000},
+       {0x0000a458, 0x00000000},
+       {0x0000a644, 0xbfad9d74},
+       {0x0000a648, 0x0048060a},
+       {0x0000a64c, 0x00003c37},
+       {0x0000a670, 0x03020100},
+       {0x0000a674, 0x09080504},
+       {0x0000a678, 0x0d0c0b0a},
+       {0x0000a67c, 0x13121110},
+       {0x0000a680, 0x31301514},
+       {0x0000a684, 0x35343332},
+       {0x0000a688, 0x00000036},
+       {0x0000a690, 0x08000838},
+       {0x0000a7cc, 0x00000000},
+       {0x0000a7d0, 0x00000000},
+       {0x0000a7d4, 0x00000004},
+       {0x0000a7dc, 0x00000000},
+       {0x0000a8d0, 0x004b6a8e},
+       {0x0000a8d4, 0x00000820},
+       {0x0000a8dc, 0x00000000},
+       {0x0000a8f0, 0x00000000},
+       {0x0000a8f4, 0x00000000},
+       {0x0000b2d0, 0x00000080},
+       {0x0000b2d4, 0x00000000},
+       {0x0000b2ec, 0x00000000},
+       {0x0000b2f0, 0x00000000},
+       {0x0000b2f4, 0x00000000},
+       {0x0000b2f8, 0x00000000},
+       {0x0000b408, 0x0e79e5c0},
+       {0x0000b40c, 0x00820820},
+       {0x0000b420, 0x00000000},
+};
+
+static const u32 qca953x_2p0_baseband_postamble[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
+       {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
+       {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+       {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+       {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+       {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
+       {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
+       {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
+       {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
+       {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+       {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e},
+       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e},
+       {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+       {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+       {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
+       {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222},
+       {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+       {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+       {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+       {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0},
+       {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+       {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f},
+       {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+       {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
+       {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018},
+       {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+       {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+       {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+       {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e},
+       {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
+       {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e},
+       {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+       {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+       {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
+       {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
+       {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+       {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
+       {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
+       {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
+       {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+       {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
+       {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+       {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+       {0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+};
+
 #endif /* INITVALS_953X_H */
index 2ca8f7e061742420fe00a9bddf61fb366feb93dc..7fc13a8da6757347e4daff1d1ede61da93648d3d 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/leds.h>
 #include <linux/completion.h>
+#include <linux/time.h>
 
 #include "common.h"
 #include "debug.h"
@@ -35,10 +36,7 @@ extern struct ieee80211_ops ath9k_ops;
 extern int ath9k_modparam_nohwcrypt;
 extern int led_blink;
 extern bool is_ath9k_unloaded;
-
-struct ath_config {
-       u16 txpowlimit;
-};
+extern int ath9k_use_chanctx;
 
 /*************************/
 /* Descriptor Management */
@@ -167,7 +165,6 @@ struct ath_txq {
        u32 axq_ampdu_depth;
        bool stopped;
        bool axq_tx_inprogress;
-       struct list_head axq_acq;
        struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
        u8 txq_headidx;
        u8 txq_tailidx;
@@ -185,7 +182,8 @@ struct ath_atx_ac {
 
 struct ath_frame_info {
        struct ath_buf *bf;
-       int framelen;
+       u16 framelen;
+       s8 txq;
        enum ath9k_key_type keytype;
        u8 keyix;
        u8 rtscts_rate;
@@ -280,8 +278,9 @@ struct ath_node {
 struct ath_tx_control {
        struct ath_txq *txq;
        struct ath_node *an;
-       u8 paprd;
        struct ieee80211_sta *sta;
+       u8 paprd;
+       bool force_channel;
 };
 
 
@@ -325,6 +324,116 @@ struct ath_rx {
        u32 ampdu_ref;
 };
 
+struct ath_chanctx {
+       struct cfg80211_chan_def chandef;
+       struct list_head vifs;
+       struct list_head acq[IEEE80211_NUM_ACS];
+       int hw_queue_base;
+
+       /* do not dereference, use for comparison only */
+       struct ieee80211_vif *primary_sta;
+
+       struct ath_beacon_config beacon;
+       struct ath9k_hw_cal_data caldata;
+       struct timespec tsf_ts;
+       u64 tsf_val;
+       u32 last_beacon;
+
+       u16 txpower;
+       bool offchannel;
+       bool stopped;
+       bool active;
+       bool assigned;
+       bool switch_after_beacon;
+};
+
+enum ath_chanctx_event {
+       ATH_CHANCTX_EVENT_BEACON_PREPARE,
+       ATH_CHANCTX_EVENT_BEACON_SENT,
+       ATH_CHANCTX_EVENT_TSF_TIMER,
+       ATH_CHANCTX_EVENT_BEACON_RECEIVED,
+       ATH_CHANCTX_EVENT_ASSOC,
+       ATH_CHANCTX_EVENT_SWITCH,
+       ATH_CHANCTX_EVENT_UNASSIGN,
+       ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL,
+};
+
+enum ath_chanctx_state {
+       ATH_CHANCTX_STATE_IDLE,
+       ATH_CHANCTX_STATE_WAIT_FOR_BEACON,
+       ATH_CHANCTX_STATE_WAIT_FOR_TIMER,
+       ATH_CHANCTX_STATE_SWITCH,
+       ATH_CHANCTX_STATE_FORCE_ACTIVE,
+};
+
+struct ath_chanctx_sched {
+       bool beacon_pending;
+       bool offchannel_pending;
+       enum ath_chanctx_state state;
+       u8 beacon_miss;
+
+       u32 next_tbtt;
+       u32 switch_start_time;
+       unsigned int offchannel_duration;
+       unsigned int channel_switch_time;
+
+       /* backup, in case the hardware timer fails */
+       struct timer_list timer;
+};
+
+enum ath_offchannel_state {
+       ATH_OFFCHANNEL_IDLE,
+       ATH_OFFCHANNEL_PROBE_SEND,
+       ATH_OFFCHANNEL_PROBE_WAIT,
+       ATH_OFFCHANNEL_SUSPEND,
+       ATH_OFFCHANNEL_ROC_START,
+       ATH_OFFCHANNEL_ROC_WAIT,
+       ATH_OFFCHANNEL_ROC_DONE,
+};
+
+struct ath_offchannel {
+       struct ath_chanctx chan;
+       struct timer_list timer;
+       struct cfg80211_scan_request *scan_req;
+       struct ieee80211_vif *scan_vif;
+       int scan_idx;
+       enum ath_offchannel_state state;
+       struct ieee80211_channel *roc_chan;
+       struct ieee80211_vif *roc_vif;
+       int roc_duration;
+       int duration;
+};
+#define ath_for_each_chanctx(_sc, _ctx)                             \
+       for (ctx = &sc->chanctx[0];                                 \
+            ctx <= &sc->chanctx[ARRAY_SIZE(sc->chanctx) - 1];      \
+            ctx++)
+
+void ath9k_fill_chanctx_ops(void);
+void ath9k_chanctx_force_active(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif);
+static inline struct ath_chanctx *
+ath_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+{
+       struct ath_chanctx **ptr = (void *) ctx->drv_priv;
+       return *ptr;
+}
+void ath_chanctx_init(struct ath_softc *sc);
+void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
+                            struct cfg80211_chan_def *chandef);
+void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
+                       struct cfg80211_chan_def *chandef);
+void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
+void ath_offchannel_timer(unsigned long data);
+void ath_offchannel_channel_change(struct ath_softc *sc);
+void ath_chanctx_offchan_switch(struct ath_softc *sc,
+                               struct ieee80211_channel *chan);
+struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc,
+                                             bool active);
+void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
+                      enum ath_chanctx_event ev);
+void ath_chanctx_timer(unsigned long data);
+
+int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
 int ath_startrecv(struct ath_softc *sc);
 bool ath_stoprecv(struct ath_softc *sc);
 u32 ath_calcrxfilter(struct ath_softc *sc);
@@ -341,6 +450,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq);
 void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an);
 void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an);
 void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
+void ath_txq_schedule_all(struct ath_softc *sc);
 int ath_tx_init(struct ath_softc *sc, int nbufs);
 int ath_txq_update(struct ath_softc *sc, int qnum,
                   struct ath9k_tx_queue_info *q);
@@ -370,32 +480,47 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
 /********/
 
 struct ath_vif {
+       struct list_head list;
+
        struct ieee80211_vif *vif;
        struct ath_node mcast_node;
        int av_bslot;
-       bool primary_sta_vif;
        __le64 tsf_adjust; /* TSF adjustment for staggered beacons */
        struct ath_buf *av_bcbuf;
+       struct ath_chanctx *chanctx;
 
        /* P2P Client */
        struct ieee80211_noa_data noa;
+
+       /* P2P GO */
+       u8 noa_index;
+       u32 offchannel_start;
+       u32 offchannel_duration;
+
+       u32 periodic_noa_start;
+       u32 periodic_noa_duration;
 };
 
 struct ath9k_vif_iter_data {
        u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */
        u8 mask[ETH_ALEN]; /* bssid mask */
        bool has_hw_macaddr;
+       u8 slottime;
+       bool beacons;
 
        int naps;      /* number of AP vifs */
        int nmeshes;   /* number of mesh vifs */
        int nstations; /* number of station vifs */
        int nwds;      /* number of WDS vifs */
        int nadhocs;   /* number of adhoc vifs */
+       struct ieee80211_vif *primary_sta;
 };
 
-void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
-                              struct ieee80211_vif *vif,
+void ath9k_calculate_iter_data(struct ath_softc *sc,
+                              struct ath_chanctx *ctx,
                               struct ath9k_vif_iter_data *iter_data);
+void ath9k_calculate_summary_state(struct ath_softc *sc,
+                                  struct ath_chanctx *ctx);
 
 /*******************/
 /* Beacon Handling */
@@ -458,6 +583,7 @@ void ath9k_csa_update(struct ath_softc *sc);
 #define ATH_PAPRD_TIMEOUT         100 /* msecs */
 #define ATH_PLL_WORK_INTERVAL     100
 
+void ath_chanctx_work(struct work_struct *work);
 void ath_tx_complete_poll_work(struct work_struct *work);
 void ath_reset_work(struct work_struct *work);
 bool ath_hw_check(struct ath_softc *sc);
@@ -473,6 +599,7 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
 void ath_ps_full_sleep(unsigned long data);
 void ath9k_p2p_ps_timer(void *priv);
 void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif);
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
 
 /**********/
 /* BTCOEX */
@@ -702,6 +829,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
 #define PS_BEACON_SYNC            BIT(4)
 #define PS_WAIT_FOR_ANI           BIT(5)
 
+#define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */
+
 struct ath_softc {
        struct ieee80211_hw *hw;
        struct device *dev;
@@ -720,6 +849,7 @@ struct ath_softc {
        struct mutex mutex;
        struct work_struct paprd_work;
        struct work_struct hw_reset_work;
+       struct work_struct chanctx_work;
        struct completion paprd_complete;
        wait_queue_head_t tx_wait;
 
@@ -738,23 +868,27 @@ struct ath_softc {
        short nvifs;
        unsigned long ps_usecount;
 
-       struct ath_config config;
        struct ath_rx rx;
        struct ath_tx tx;
        struct ath_beacon beacon;
 
+       struct cfg80211_chan_def cur_chandef;
+       struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX];
+       struct ath_chanctx *cur_chan;
+       struct ath_chanctx *next_chan;
+       spinlock_t chan_lock;
+       struct ath_offchannel offchannel;
+       struct ath_chanctx_sched sched;
+
 #ifdef CONFIG_MAC80211_LEDS
        bool led_registered;
        char led_name[32];
        struct led_classdev led_cdev;
 #endif
 
-       struct ath9k_hw_cal_data caldata;
-
 #ifdef CONFIG_ATH9K_DEBUGFS
        struct ath9k_debug debug;
 #endif
-       struct ath_beacon_config cur_beacon_conf;
        struct delayed_work tx_complete_work;
        struct delayed_work hw_pll_work;
        struct timer_list sleep_timer;
index e387f0b2954a0cf5500610b330f744a5b68cd69c..eaf8f058c15154941c1abfe0cc34585a5a217733 100644 (file)
@@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        u8 chainmask = ah->txchainmask;
        u8 rate = 0;
 
-       sband = &common->sbands[common->hw->conf.chandef.chan->band];
+       sband = &common->sbands[sc->cur_chandef.chan->band];
        rate = sband->bitrates[rateidx].hw_value;
        if (vif->bss_conf.use_short_preamble)
                rate |= sband->bitrates[rateidx].hw_value_short;
@@ -108,6 +108,55 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
        ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
 }
 
+static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
+                                struct sk_buff *skb)
+{
+       static const u8 noa_ie_hdr[] = {
+               WLAN_EID_VENDOR_SPECIFIC,       /* type */
+               0,                              /* length */
+               0x50, 0x6f, 0x9a,               /* WFA OUI */
+               0x09,                           /* P2P subtype */
+               0x0c,                           /* Notice of Absence */
+               0x00,                           /* LSB of little-endian len */
+               0x00,                           /* MSB of little-endian len */
+       };
+
+       struct ieee80211_p2p_noa_attr *noa;
+       int noa_len, noa_desc, i = 0;
+       u8 *hdr;
+
+       if (!avp->offchannel_duration && !avp->periodic_noa_duration)
+               return;
+
+       noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration;
+       noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc;
+
+       hdr = skb_put(skb, sizeof(noa_ie_hdr));
+       memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr));
+       hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2;
+       hdr[7] = noa_len;
+
+       noa = (void *) skb_put(skb, noa_len);
+       memset(noa, 0, noa_len);
+
+       noa->index = avp->noa_index;
+       if (avp->periodic_noa_duration) {
+               u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
+
+               noa->desc[i].count = 255;
+               noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start);
+               noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration);
+               noa->desc[i].interval = cpu_to_le32(interval);
+               i++;
+       }
+
+       if (avp->offchannel_duration) {
+               noa->desc[i].count = 1;
+               noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start);
+               noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration);
+       }
+}
+
 static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                                             struct ieee80211_vif *vif)
 {
@@ -155,6 +204,9 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
                hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
        }
 
+       if (vif->p2p)
+               ath9k_beacon_add_noa(sc, avp, skb);
+
        bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
                                         skb->len, DMA_TO_DEVICE);
        if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
@@ -249,7 +301,7 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
 static int ath9k_beacon_choose_slot(struct ath_softc *sc)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
        u16 intval;
        u32 tsftu;
        u64 tsf;
@@ -277,8 +329,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
 static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
        struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_beacon_config *cur_conf = &avp->chanctx->beacon;
        u32 tsfadjust;
 
        if (avp->av_bslot == 0)
@@ -374,12 +426,19 @@ void ath9k_beacon_tasklet(unsigned long data)
        vif = sc->beacon.bslot[slot];
 
        /* EDMA devices check that in the tx completion function. */
-       if (!edma && ath9k_csa_is_finished(sc, vif))
-               return;
+       if (!edma) {
+               if (sc->sched.beacon_pending)
+                       ath_chanctx_event(sc, NULL,
+                                         ATH_CHANCTX_EVENT_BEACON_SENT);
+
+               if (ath9k_csa_is_finished(sc, vif))
+                       return;
+       }
 
        if (!vif || !vif->bss_conf.enable_beacon)
                return;
 
+       ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE);
        bf = ath9k_beacon_generate(sc->hw, vif);
 
        if (sc->beacon.bmisscnt != 0) {
@@ -500,7 +559,6 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
                                      struct ieee80211_vif *vif)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_vif *avp = (void *)vif->drv_priv;
 
        if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
                if ((vif->type != NL80211_IFTYPE_AP) ||
@@ -514,7 +572,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
        if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
                if ((vif->type == NL80211_IFTYPE_STATION) &&
                    test_bit(ATH_OP_BEACONS, &common->op_flags) &&
-                   !avp->primary_sta_vif) {
+                   vif != sc->cur_chan->primary_sta) {
                        ath_dbg(common, CONFIG,
                                "Beacon already configured for a station interface\n");
                        return false;
@@ -525,10 +583,11 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
 }
 
 static void ath9k_cache_beacon_config(struct ath_softc *sc,
+                                     struct ath_chanctx *ctx,
                                      struct ieee80211_bss_conf *bss_conf)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &ctx->beacon;
 
        ath_dbg(common, BEACON,
                "Caching beacon data for BSS: %pM\n", bss_conf->bssid);
@@ -564,20 +623,29 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
                         u32 changed)
 {
        struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
         struct ath_hw *ah = sc->sc_ah;
         struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_chanctx *ctx = avp->chanctx;
+       struct ath_beacon_config *cur_conf;
        unsigned long flags;
        bool skip_beacon = false;
 
+       if (!ctx)
+               return;
+
+       cur_conf = &avp->chanctx->beacon;
        if (vif->type == NL80211_IFTYPE_AP)
                ath9k_set_tsfadjust(sc, vif);
 
        if (!ath9k_allow_beacon_config(sc, vif))
                return;
 
-       if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
-               ath9k_cache_beacon_config(sc, bss_conf);
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               ath9k_cache_beacon_config(sc, ctx, bss_conf);
+               if (ctx != sc->cur_chan)
+                       return;
+
                ath9k_set_beacon(sc);
                set_bit(ATH_OP_BEACONS, &common->op_flags);
                return;
@@ -593,10 +661,13 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
                        cur_conf->enable_beacon = false;
                } else if (bss_conf->enable_beacon) {
                        cur_conf->enable_beacon = true;
-                       ath9k_cache_beacon_config(sc, bss_conf);
+                       ath9k_cache_beacon_config(sc, ctx, bss_conf);
                }
        }
 
+       if (ctx != sc->cur_chan)
+               return;
+
        /*
         * Configure the HW beacon registers only when we have a valid
         * beacon interval.
@@ -631,7 +702,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
 void ath9k_set_beacon(struct ath_softc *sc)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
 
        switch (sc->sc_ah->opmode) {
        case NL80211_IFTYPE_AP:
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
new file mode 100644 (file)
index 0000000..ba214eb
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 2014 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ath9k.h"
+
+/* Set/change channels.  If the channel is really being changed, it's done
+ * by reseting the chip.  To accomplish this we must first cleanup any pending
+ * DMA, then restart stuff.
+ */
+static int ath_set_channel(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ieee80211_hw *hw = sc->hw;
+       struct ath9k_channel *hchan;
+       struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef;
+       struct ieee80211_channel *chan = chandef->chan;
+       int pos = chan->hw_value;
+       int old_pos = -1;
+       int r;
+
+       if (test_bit(ATH_OP_INVALID, &common->op_flags))
+               return -EIO;
+
+       if (ah->curchan)
+               old_pos = ah->curchan - &ah->channels[0];
+
+       ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+               chan->center_freq, chandef->width);
+
+       /* update survey stats for the old channel before switching */
+       spin_lock_bh(&common->cc_lock);
+       ath_update_survey_stats(sc);
+       spin_unlock_bh(&common->cc_lock);
+
+       ath9k_cmn_get_channel(hw, ah, chandef);
+
+       /* If the operating channel changes, change the survey in-use flags
+        * along with it.
+        * Reset the survey data for the new channel, unless we're switching
+        * back to the operating channel from an off-channel operation.
+        */
+       if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) {
+               if (sc->cur_survey)
+                       sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
+
+               sc->cur_survey = &sc->survey[pos];
+
+               memset(sc->cur_survey, 0, sizeof(struct survey_info));
+               sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
+       } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
+               memset(&sc->survey[pos], 0, sizeof(struct survey_info));
+       }
+
+       hchan = &sc->sc_ah->channels[pos];
+       r = ath_reset_internal(sc, hchan);
+       if (r)
+               return r;
+
+       /* The most recent snapshot of channel->noisefloor for the old
+        * channel is only available after the hardware reset. Copy it to
+        * the survey stats now.
+        */
+       if (old_pos >= 0)
+               ath_update_survey_nf(sc, old_pos);
+
+       /* Enable radar pulse detection if on a DFS channel. Spectral
+        * scanning and radar detection can not be used concurrently.
+        */
+       if (hw->conf.radar_enabled) {
+               u32 rxfilter;
+
+               /* set HW specific DFS configuration */
+               ath9k_hw_set_radar_params(ah);
+               rxfilter = ath9k_hw_getrxfilter(ah);
+               rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
+                               ATH9K_RX_FILTER_PHYERR;
+               ath9k_hw_setrxfilter(ah, rxfilter);
+               ath_dbg(common, DFS, "DFS enabled at freq %d\n",
+                       chan->center_freq);
+       } else {
+               /* perform spectral scan if requested. */
+               if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
+                       sc->spectral_mode == SPECTRAL_CHANSCAN)
+                       ath9k_spectral_scan_trigger(hw);
+       }
+
+       return 0;
+}
+
+static bool
+ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
+                             bool powersave)
+{
+       struct ieee80211_vif *vif = avp->vif;
+       struct ieee80211_sta *sta = NULL;
+       struct ieee80211_hdr_3addr *nullfunc;
+       struct ath_tx_control txctl;
+       struct sk_buff *skb;
+       int band = sc->cur_chan->chandef.chan->band;
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               if (!vif->bss_conf.assoc)
+                       return false;
+
+               skb = ieee80211_nullfunc_get(sc->hw, vif);
+               if (!skb)
+                       return false;
+
+               nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
+               if (powersave)
+                       nullfunc->frame_control |=
+                               cpu_to_le16(IEEE80211_FCTL_PM);
+
+               skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+               if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
+                       dev_kfree_skb_any(skb);
+                       return false;
+               }
+               break;
+       default:
+               return false;
+       }
+
+       memset(&txctl, 0, sizeof(txctl));
+       txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
+       txctl.sta = sta;
+       txctl.force_channel = true;
+       if (ath_tx_start(sc->hw, skb, &txctl)) {
+               ieee80211_free_txskb(sc->hw, skb);
+               return false;
+       }
+
+       return true;
+}
+
+void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_vif *avp;
+       bool active = false;
+       u8 n_active = 0;
+
+       if (!ctx)
+               return;
+
+       list_for_each_entry(avp, &ctx->vifs, list) {
+               struct ieee80211_vif *vif = avp->vif;
+
+               switch (vif->type) {
+               case NL80211_IFTYPE_P2P_CLIENT:
+               case NL80211_IFTYPE_STATION:
+                       if (vif->bss_conf.assoc)
+                               active = true;
+                       break;
+               default:
+                       active = true;
+                       break;
+               }
+       }
+       ctx->active = active;
+
+       ath_for_each_chanctx(sc, ctx) {
+               if (!ctx->assigned || list_empty(&ctx->vifs))
+                       continue;
+               n_active++;
+       }
+
+       if (n_active <= 1) {
+               clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
+               return;
+       }
+       if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+               return;
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL);
+}
+
+static bool
+ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave)
+{
+       struct ath_vif *avp;
+       bool sent = false;
+
+       rcu_read_lock();
+       list_for_each_entry(avp, &sc->cur_chan->vifs, list) {
+               if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave))
+                       sent = true;
+       }
+       rcu_read_unlock();
+
+       return sent;
+}
+
+static bool ath_chanctx_defer_switch(struct ath_softc *sc)
+{
+       if (sc->cur_chan == &sc->offchannel.chan)
+               return false;
+
+       switch (sc->sched.state) {
+       case ATH_CHANCTX_STATE_SWITCH:
+               return false;
+       case ATH_CHANCTX_STATE_IDLE:
+               if (!sc->cur_chan->switch_after_beacon)
+                       return false;
+
+               sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+               break;
+       default:
+               break;
+       }
+
+       return true;
+}
+
+static void ath_chanctx_set_next(struct ath_softc *sc, bool force)
+{
+       struct timespec ts;
+       bool measure_time = false;
+       bool send_ps = false;
+
+       spin_lock_bh(&sc->chan_lock);
+       if (!sc->next_chan) {
+               spin_unlock_bh(&sc->chan_lock);
+               return;
+       }
+
+       if (!force && ath_chanctx_defer_switch(sc)) {
+               spin_unlock_bh(&sc->chan_lock);
+               return;
+       }
+
+       if (sc->cur_chan != sc->next_chan) {
+               sc->cur_chan->stopped = true;
+               spin_unlock_bh(&sc->chan_lock);
+
+               if (sc->next_chan == &sc->offchannel.chan) {
+                       getrawmonotonic(&ts);
+                       measure_time = true;
+               }
+               __ath9k_flush(sc->hw, ~0, true);
+
+               if (ath_chanctx_send_ps_frame(sc, true))
+                       __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false);
+
+               send_ps = true;
+               spin_lock_bh(&sc->chan_lock);
+
+               if (sc->cur_chan != &sc->offchannel.chan) {
+                       getrawmonotonic(&sc->cur_chan->tsf_ts);
+                       sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah);
+               }
+       }
+       sc->cur_chan = sc->next_chan;
+       sc->cur_chan->stopped = false;
+       sc->next_chan = NULL;
+       sc->sched.offchannel_duration = 0;
+       if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE)
+               sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+
+       spin_unlock_bh(&sc->chan_lock);
+
+       if (sc->sc_ah->chip_fullsleep ||
+           memcmp(&sc->cur_chandef, &sc->cur_chan->chandef,
+                  sizeof(sc->cur_chandef))) {
+               ath_set_channel(sc);
+               if (measure_time)
+                       sc->sched.channel_switch_time =
+                               ath9k_hw_get_tsf_offset(&ts, NULL);
+       }
+       if (send_ps)
+               ath_chanctx_send_ps_frame(sc, false);
+
+       ath_offchannel_channel_change(sc);
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH);
+}
+
+void ath_chanctx_work(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc,
+                                           chanctx_work);
+       mutex_lock(&sc->mutex);
+       ath_chanctx_set_next(sc, false);
+       mutex_unlock(&sc->mutex);
+}
+
+void ath_chanctx_init(struct ath_softc *sc)
+{
+       struct ath_chanctx *ctx;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *chan;
+       int i, j;
+
+       sband = &common->sbands[IEEE80211_BAND_2GHZ];
+       if (!sband->n_channels)
+               sband = &common->sbands[IEEE80211_BAND_5GHZ];
+
+       chan = &sband->channels[0];
+       for (i = 0; i < ATH9K_NUM_CHANCTX; i++) {
+               ctx = &sc->chanctx[i];
+               cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
+               INIT_LIST_HEAD(&ctx->vifs);
+               ctx->txpower = ATH_TXPOWER_MAX;
+               for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
+                       INIT_LIST_HEAD(&ctx->acq[j]);
+       }
+       ctx = &sc->offchannel.chan;
+       cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
+       INIT_LIST_HEAD(&ctx->vifs);
+       ctx->txpower = ATH_TXPOWER_MAX;
+       for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
+               INIT_LIST_HEAD(&ctx->acq[j]);
+       sc->offchannel.chan.offchannel = true;
+
+}
+
+void ath9k_chanctx_force_active(struct ieee80211_hw *hw,
+                               struct ieee80211_vif *vif)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
+       bool changed = false;
+
+       if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+               return;
+
+       if (!avp->chanctx)
+               return;
+
+       mutex_lock(&sc->mutex);
+
+       spin_lock_bh(&sc->chan_lock);
+       if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
+               sc->next_chan = avp->chanctx;
+               changed = true;
+       }
+       sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
+       spin_unlock_bh(&sc->chan_lock);
+
+       if (changed)
+               ath_chanctx_set_next(sc, true);
+
+       mutex_unlock(&sc->mutex);
+}
+
+void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
+                       struct cfg80211_chan_def *chandef)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+       spin_lock_bh(&sc->chan_lock);
+
+       if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) &&
+           (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) {
+               sc->sched.offchannel_pending = true;
+               spin_unlock_bh(&sc->chan_lock);
+               return;
+       }
+
+       sc->next_chan = ctx;
+       if (chandef)
+               ctx->chandef = *chandef;
+
+       if (sc->next_chan == &sc->offchannel.chan) {
+               sc->sched.offchannel_duration =
+                       TU_TO_USEC(sc->offchannel.duration) +
+                       sc->sched.channel_switch_time;
+       }
+       spin_unlock_bh(&sc->chan_lock);
+       ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+}
+
+void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
+                            struct cfg80211_chan_def *chandef)
+{
+       bool cur_chan;
+
+       spin_lock_bh(&sc->chan_lock);
+       if (chandef)
+               memcpy(&ctx->chandef, chandef, sizeof(*chandef));
+       cur_chan = sc->cur_chan == ctx;
+       spin_unlock_bh(&sc->chan_lock);
+
+       if (!cur_chan)
+               return;
+
+       ath_set_channel(sc);
+}
+
+struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active)
+{
+       struct ath_chanctx *ctx;
+
+       ath_for_each_chanctx(sc, ctx) {
+               if (!ctx->assigned || list_empty(&ctx->vifs))
+                       continue;
+               if (active && !ctx->active)
+                       continue;
+
+               if (ctx->switch_after_beacon)
+                       return ctx;
+       }
+
+       return &sc->chanctx[0];
+}
+
+void ath_chanctx_offchan_switch(struct ath_softc *sc,
+                               struct ieee80211_channel *chan)
+{
+       struct cfg80211_chan_def chandef;
+
+       cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+
+       ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef);
+}
+
+static struct ath_chanctx *
+ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx)
+{
+       int idx = ctx - &sc->chanctx[0];
+
+       return &sc->chanctx[!idx];
+}
+
+static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc)
+{
+       struct ath_chanctx *prev, *cur;
+       struct timespec ts;
+       u32 cur_tsf, prev_tsf, beacon_int;
+       s32 offset;
+
+       beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
+
+       cur = sc->cur_chan;
+       prev = ath_chanctx_get_next(sc, cur);
+
+       getrawmonotonic(&ts);
+       cur_tsf = (u32) cur->tsf_val +
+                 ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts);
+
+       prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf;
+       prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts);
+
+       /* Adjust the TSF time of the AP chanctx to keep its beacons
+        * at half beacon interval offset relative to the STA chanctx.
+        */
+       offset = cur_tsf - prev_tsf;
+
+       /* Ignore stale data or spurious timestamps */
+       if (offset < 0 || offset > 3 * beacon_int)
+               return;
+
+       offset = beacon_int / 2 - (offset % beacon_int);
+       prev->tsf_val += offset;
+}
+
+void ath_chanctx_timer(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *) data;
+
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
+}
+
+/* Configure the TSF based hardware timer for a channel switch.
+ * Also set up backup software timer, in case the gen timer fails.
+ * This could be caused by a hardware reset.
+ */
+static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
+{
+       struct ath_hw *ah = sc->sc_ah;
+
+       ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000);
+       tsf_time -= ath9k_hw_gettsf32(ah);
+       tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1;
+       mod_timer(&sc->sched.timer, tsf_time);
+}
+
+void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
+                      enum ath_chanctx_event ev)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_beacon_config *cur_conf;
+       struct ath_vif *avp = NULL;
+       struct ath_chanctx *ctx;
+       u32 tsf_time;
+       u32 beacon_int;
+       bool noa_changed = false;
+
+       if (vif)
+               avp = (struct ath_vif *) vif->drv_priv;
+
+       spin_lock_bh(&sc->chan_lock);
+
+       switch (ev) {
+       case ATH_CHANCTX_EVENT_BEACON_PREPARE:
+               if (avp->offchannel_duration)
+                       avp->offchannel_duration = 0;
+
+               if (avp->chanctx != sc->cur_chan)
+                       break;
+
+               if (sc->sched.offchannel_pending) {
+                       sc->sched.offchannel_pending = false;
+                       sc->next_chan = &sc->offchannel.chan;
+                       sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+               }
+
+               ctx = ath_chanctx_get_next(sc, sc->cur_chan);
+               if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) {
+                       sc->next_chan = ctx;
+                       sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+               }
+
+               /* if the timer missed its window, use the next interval */
+               if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
+                       sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+
+               if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
+                       break;
+
+               sc->sched.beacon_pending = true;
+               sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER);
+
+               cur_conf = &sc->cur_chan->beacon;
+               beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
+
+               /* defer channel switch by a quarter beacon interval */
+               tsf_time = sc->sched.next_tbtt + beacon_int / 4;
+               sc->sched.switch_start_time = tsf_time;
+               sc->cur_chan->last_beacon = sc->sched.next_tbtt;
+
+               /* Prevent wrap-around issues */
+               if (avp->periodic_noa_duration &&
+                   tsf_time - avp->periodic_noa_start > BIT(30))
+                       avp->periodic_noa_duration = 0;
+
+               if (ctx->active && !avp->periodic_noa_duration) {
+                       avp->periodic_noa_start = tsf_time;
+                       avp->periodic_noa_duration =
+                               TU_TO_USEC(cur_conf->beacon_interval) / 2 -
+                               sc->sched.channel_switch_time;
+                       noa_changed = true;
+               } else if (!ctx->active && avp->periodic_noa_duration) {
+                       avp->periodic_noa_duration = 0;
+                       noa_changed = true;
+               }
+
+               /* If at least two consecutive beacons were missed on the STA
+                * chanctx, stay on the STA channel for one extra beacon period,
+                * to resync the timer properly.
+                */
+               if (ctx->active && sc->sched.beacon_miss >= 2)
+                       sc->sched.offchannel_duration = 3 * beacon_int / 2;
+
+               if (sc->sched.offchannel_duration) {
+                       noa_changed = true;
+                       avp->offchannel_start = tsf_time;
+                       avp->offchannel_duration =
+                               sc->sched.offchannel_duration;
+               }
+
+               if (noa_changed)
+                       avp->noa_index++;
+               break;
+       case ATH_CHANCTX_EVENT_BEACON_SENT:
+               if (!sc->sched.beacon_pending)
+                       break;
+
+               sc->sched.beacon_pending = false;
+               if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
+                       break;
+
+               sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
+               ath_chanctx_setup_timer(sc, sc->sched.switch_start_time);
+               break;
+       case ATH_CHANCTX_EVENT_TSF_TIMER:
+               if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
+                       break;
+
+               if (!sc->cur_chan->switch_after_beacon &&
+                   sc->sched.beacon_pending)
+                       sc->sched.beacon_miss++;
+
+               sc->sched.state = ATH_CHANCTX_STATE_SWITCH;
+               ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+               break;
+       case ATH_CHANCTX_EVENT_BEACON_RECEIVED:
+               if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
+                   sc->cur_chan == &sc->offchannel.chan)
+                       break;
+
+               ath_chanctx_adjust_tbtt_delta(sc);
+               sc->sched.beacon_pending = false;
+               sc->sched.beacon_miss = 0;
+
+               /* TSF time might have been updated by the incoming beacon,
+                * need update the channel switch timer to reflect the change.
+                */
+               tsf_time = sc->sched.switch_start_time;
+               tsf_time -= (u32) sc->cur_chan->tsf_val +
+                       ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
+               tsf_time += ath9k_hw_gettsf32(ah);
+
+
+               ath_chanctx_setup_timer(sc, tsf_time);
+               break;
+       case ATH_CHANCTX_EVENT_ASSOC:
+               if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
+                   avp->chanctx != sc->cur_chan)
+                       break;
+
+               sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+               /* fall through */
+       case ATH_CHANCTX_EVENT_SWITCH:
+               if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
+                   sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE ||
+                   sc->cur_chan->switch_after_beacon ||
+                   sc->cur_chan == &sc->offchannel.chan)
+                       break;
+
+               /* If this is a station chanctx, stay active for a half
+                * beacon period (minus channel switch time)
+                */
+               sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
+               cur_conf = &sc->cur_chan->beacon;
+
+               sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
+
+               tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2;
+               if (sc->sched.beacon_miss >= 2) {
+                       sc->sched.beacon_miss = 0;
+                       tsf_time *= 3;
+               }
+
+               tsf_time -= sc->sched.channel_switch_time;
+               tsf_time += ath9k_hw_gettsf32(sc->sc_ah);
+               sc->sched.switch_start_time = tsf_time;
+
+               ath_chanctx_setup_timer(sc, tsf_time);
+               sc->sched.beacon_pending = true;
+               break;
+       case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
+               if (sc->cur_chan == &sc->offchannel.chan ||
+                   sc->cur_chan->switch_after_beacon)
+                       break;
+
+               sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
+               ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+               break;
+       case ATH_CHANCTX_EVENT_UNASSIGN:
+               if (sc->cur_chan->assigned) {
+                       if (sc->next_chan && !sc->next_chan->assigned &&
+                           sc->next_chan != &sc->offchannel.chan)
+                               sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+                       break;
+               }
+
+               ctx = ath_chanctx_get_next(sc, sc->cur_chan);
+               sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+               if (!ctx->assigned)
+                       break;
+
+               sc->next_chan = ctx;
+               ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+               break;
+       }
+
+       spin_unlock_bh(&sc->chan_lock);
+}
index 775d1d20ce0b384bae1897879b1e4d17f6b4fbd4..733be5178481e6967141e952a23f348705bae2e4 100644 (file)
@@ -57,7 +57,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
                                 struct ath9k_beacon_state *bs)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       int dtim_intval;
+       int dtim_intval, sleepduration;
        u64 tsf;
 
        /* No need to configure beacon if we are not associated */
@@ -75,6 +75,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
         * last beacon we received (which may be none).
         */
        dtim_intval = conf->intval * conf->dtim_period;
+       sleepduration = ah->hw->conf.listen_interval * conf->intval;
 
        /*
         * Pull nexttbtt forward to reflect the current
@@ -112,7 +113,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
         */
 
        bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
-                                                conf->intval));
+                                                 sleepduration));
        if (bs->bs_sleepduration > bs->bs_dtimperiod)
                bs->bs_sleepduration = bs->bs_dtimperiod;
 
index 6cc42be48d4e60e68f3e2603ebfc465f89159a0d..d2279365be6fcc6a85b2252a9c68a2502f94c211 100644 (file)
@@ -202,7 +202,7 @@ static ssize_t write_file_ani(struct file *file,
        if (kstrtoul(buf, 0, &ani))
                return -EINVAL;
 
-       if (ani < 0 || ani > 1)
+       if (ani > 1)
                return -EINVAL;
 
        common->disable_ani = !ani;
@@ -750,13 +750,13 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
 {
        struct ath_softc *sc = file->private_data;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ieee80211_hw *hw = sc->hw;
        struct ath9k_vif_iter_data iter_data;
+       struct ath_chanctx *ctx;
        char buf[512];
        unsigned int len = 0;
        ssize_t retval = 0;
        unsigned int reg;
-       u32 rxfilter;
+       u32 rxfilter, i;
 
        len += scnprintf(buf + len, sizeof(buf) - len,
                         "BSSID: %pM\n", common->curbssid);
@@ -826,14 +826,20 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
 
        len += scnprintf(buf + len, sizeof(buf) - len, "\n");
 
-       ath9k_calculate_iter_data(hw, NULL, &iter_data);
-
-       len += scnprintf(buf + len, sizeof(buf) - len,
-                        "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i"
-                        " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
-                        iter_data.naps, iter_data.nstations, iter_data.nmeshes,
-                        iter_data.nwds, iter_data.nadhocs,
-                        sc->nvifs, sc->nbcnvifs);
+       i = 0;
+       ath_for_each_chanctx(sc, ctx) {
+               if (!ctx->assigned || list_empty(&ctx->vifs))
+                       continue;
+               ath9k_calculate_iter_data(sc, ctx, &iter_data);
+
+               len += scnprintf(buf + len, sizeof(buf) - len,
+                       "VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i",
+                       i++, iter_data.naps, iter_data.nstations,
+                       iter_data.nmeshes, iter_data.nwds);
+               len += scnprintf(buf + len, sizeof(buf) - len,
+                       " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
+                       iter_data.nadhocs, sc->nvifs, sc->nbcnvifs);
+       }
 
        if (len > sizeof(buf))
                len = sizeof(buf);
@@ -1080,7 +1086,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf,
 {
        struct ath_softc *sc = file->private_data;
        struct ath_hw *ah = sc->sc_ah;
-       struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist;
+       struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_conf *conf = &common->hw->conf;
        u32 len = 0, size = 1500;
index 2a8ed8375ec0584771f4f7dcc833d17131ec34b2..fd0158fdf144df989c501316cbbe4692899dc693 100644 (file)
@@ -791,7 +791,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
                                refdiv = 5;
                        } else {
                                pll2_divint = 0x11;
-                               pll2_divfrac = 0x26666;
+                               pll2_divfrac =
+                                       AR_SREV_9531(ah) ? 0x26665 : 0x26666;
                                refdiv = 1;
                        }
                }
@@ -1730,6 +1731,23 @@ fail:
        return -EINVAL;
 }
 
+u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur)
+{
+       struct timespec ts;
+       s64 usec;
+
+       if (!cur) {
+               getrawmonotonic(&ts);
+               cur = &ts;
+       }
+
+       usec = cur->tv_sec * 1000000ULL + cur->tv_nsec / 1000;
+       usec -= last->tv_sec * 1000000ULL + last->tv_nsec / 1000;
+
+       return (u32) usec;
+}
+EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
+
 int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
                   struct ath9k_hw_cal_data *caldata, bool fastcc)
 {
@@ -1739,7 +1757,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        u32 saveDefAntenna;
        u32 macStaId1;
        u64 tsf = 0;
-       s64 usec = 0;
        int r;
        bool start_mci_reset = false;
        bool save_fullsleep = ah->chip_fullsleep;
@@ -1785,7 +1802,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        /* Save TSF before chip reset, a cold reset clears it */
        tsf = ath9k_hw_gettsf64(ah);
        getrawmonotonic(&ts);
-       usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
 
        saveLedState = REG_READ(ah, AR_CFG_LED) &
                (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL |
@@ -1818,9 +1834,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        }
 
        /* Restore TSF */
-       getrawmonotonic(&ts);
-       usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec;
-       ath9k_hw_settsf64(ah, tsf + usec);
+       ath9k_hw_settsf64(ah, tsf + ath9k_hw_get_tsf_offset(&ts, NULL));
 
        if (AR_SREV_9280_20_OR_LATER(ah))
                REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE);
index 0acd4b5a48929f4443fde73706f6ee6bb8ce7a0b..51b4ebe04c04faaf612c744b2bbad93001c46622 100644 (file)
@@ -1000,6 +1000,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah);
 u64 ath9k_hw_gettsf64(struct ath_hw *ah);
 void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
 void ath9k_hw_reset_tsf(struct ath_hw *ah);
+u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur);
 void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
 void ath9k_hw_init_global_settings(struct ath_hw *ah);
 u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);
index 0246b990fe87ade49ffa26ca417a8ff999179ebc..39419ea845cc0640ea30927e600e5c2f769cf075 100644 (file)
@@ -61,7 +61,7 @@ static int ath9k_ps_enable;
 module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
 MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
 
-static int ath9k_use_chanctx;
+int ath9k_use_chanctx;
 module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444);
 MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
 
@@ -169,9 +169,9 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
 
        /* Set tx power */
        if (ah->curchan) {
-               sc->config.txpowlimit = 2 * ah->curchan->chan->max_power;
+               sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
                ath9k_ps_wakeup(sc);
-               ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+               ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
                sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
                /* synchronize DFS detector if regulatory domain changed */
                if (sc->dfs_detector != NULL)
@@ -335,7 +335,6 @@ static void ath9k_init_misc(struct ath_softc *sc)
        setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
 
        common->last_rssi = ATH_RSSI_DUMMY_MARKER;
-       sc->config.txpowlimit = ATH_TXPOWER_MAX;
        memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
        sc->beacon.slottime = ATH9K_SLOT_TIME_9;
 
@@ -511,6 +510,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
        sc->tx99_power = MAX_RATE_POWER + 1;
        init_waitqueue_head(&sc->tx_wait);
+       sc->cur_chan = &sc->chanctx[0];
+       if (!ath9k_use_chanctx)
+               sc->cur_chan->hw_queue_base = 0;
 
        if (!pdata || pdata->use_eeprom) {
                ah->ah_flags |= AH_USE_EEPROM;
@@ -556,6 +558,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        spin_lock_init(&common->cc_lock);
        spin_lock_init(&sc->sc_serial_rw);
        spin_lock_init(&sc->sc_pm_lock);
+       spin_lock_init(&sc->chan_lock);
        mutex_init(&sc->mutex);
        tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
        tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
@@ -564,7 +567,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc);
        INIT_WORK(&sc->hw_reset_work, ath_reset_work);
        INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
+       INIT_WORK(&sc->chanctx_work, ath_chanctx_work);
        INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
+       setup_timer(&sc->offchannel.timer, ath_offchannel_timer,
+                   (unsigned long)sc);
+       setup_timer(&sc->sched.timer, ath_chanctx_timer, (unsigned long)sc);
 
        /*
         * Cache line size is used to size and align various
@@ -599,6 +606,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ath9k_cmn_init_crypto(sc->sc_ah);
        ath9k_init_misc(sc);
        ath_fill_led_pin(sc);
+       ath_chanctx_init(sc);
 
        if (common->bus_ops->aspm_init)
                common->bus_ops->aspm_init(common);
@@ -664,6 +672,12 @@ static const struct ieee80211_iface_limit wds_limits[] = {
        { .max = 2048,  .types = BIT(NL80211_IFTYPE_WDS) },
 };
 
+static const struct ieee80211_iface_limit if_limits_multi[] = {
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_STATION) },
+       { .max = 1,     .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                BIT(NL80211_IFTYPE_P2P_GO) },
+};
+
 static const struct ieee80211_iface_limit if_dfs_limits[] = {
        { .max = 1,     .types = BIT(NL80211_IFTYPE_AP) |
 #ifdef CONFIG_MAC80211_MESH
@@ -672,6 +686,16 @@ static const struct ieee80211_iface_limit if_dfs_limits[] = {
                                 BIT(NL80211_IFTYPE_ADHOC) },
 };
 
+static const struct ieee80211_iface_combination if_comb_multi[] = {
+       {
+               .limits = if_limits_multi,
+               .n_limits = ARRAY_SIZE(if_limits_multi),
+               .max_interfaces = 2,
+               .num_different_channels = 2,
+               .beacon_int_infra_match = true,
+       },
+};
+
 static const struct ieee80211_iface_combination if_comb[] = {
        {
                .limits = if_limits,
@@ -712,6 +736,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                IEEE80211_HW_SPECTRUM_MGMT |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_SUPPORTS_RC_TABLE |
+               IEEE80211_HW_QUEUE_CONTROL |
                IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
 
        if (ath9k_ps_enable)
@@ -739,12 +764,21 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
                        BIT(NL80211_IFTYPE_STATION) |
                        BIT(NL80211_IFTYPE_ADHOC) |
                        BIT(NL80211_IFTYPE_MESH_POINT);
-               hw->wiphy->iface_combinations = if_comb;
                if (!ath9k_use_chanctx) {
+                       hw->wiphy->iface_combinations = if_comb;
                        hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
                        hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS);
-               } else
-                       hw->wiphy->n_iface_combinations = 1;
+               } else {
+                       hw->wiphy->iface_combinations = if_comb_multi;
+                       hw->wiphy->n_iface_combinations =
+                               ARRAY_SIZE(if_comb_multi);
+                       hw->wiphy->max_scan_ssids = 255;
+                       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+                       hw->wiphy->max_remain_on_channel_duration = 10000;
+                       hw->chanctx_data_size = sizeof(void *);
+                       hw->extra_beacon_tailroom =
+                               sizeof(struct ieee80211_p2p_noa_attr) + 9;
+               }
        }
 
        hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -756,9 +790,14 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
-       hw->queues = 4;
+       /* allow 4 queues per channel context +
+        * 1 cab queue + 1 offchannel tx queue
+        */
+       hw->queues = 10;
+       /* last queue for offchannel */
+       hw->offchannel_tx_hw_queue = hw->queues - 1;
        hw->max_rates = 4;
-       hw->max_listen_interval = 1;
+       hw->max_listen_interval = 10;
        hw->max_rate_tries = 10;
        hw->sta_data_size = sizeof(struct ath_node);
        hw->vif_data_size = sizeof(struct ath_vif);
index 72a715fe8f24e2699323e0b434cc6a4a1f894dd5..2343f56e64987730bcb3ad1cb5542c41dcd94310 100644 (file)
@@ -178,7 +178,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
        txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE];
 
        memset(tx_info, 0, sizeof(*tx_info));
-       tx_info->band = hw->conf.chandef.chan->band;
+       tx_info->band = sc->cur_chandef.chan->band;
        tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
        tx_info->control.rates[0].idx = 0;
        tx_info->control.rates[0].count = 1;
@@ -416,7 +416,7 @@ void ath_start_ani(struct ath_softc *sc)
 
        if (common->disable_ani ||
            !test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
-           (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+           sc->cur_chan->offchannel)
                return;
 
        common->ani.longcal_timer = timestamp;
@@ -440,7 +440,7 @@ void ath_check_ani(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
 
        /*
         * Check for the various conditions in which ANI has to
index da768675753595c3c27ef3fd920327af3c1a547e..6c56cafa5ca491fb284adad4fb156af9caa322b4 100644 (file)
@@ -346,8 +346,14 @@ struct ar5416_desc {
 #define AR_FrameLen         0x00000fff
 #define AR_VirtMoreFrag     0x00001000
 #define AR_TxCtlRsvd00      0x0000e000
-#define AR_XmitPower        0x003f0000
-#define AR_XmitPower_S      16
+#define AR_XmitPower0       0x003f0000
+#define AR_XmitPower0_S     16
+#define AR_XmitPower1      0x3f000000
+#define AR_XmitPower1_S     24
+#define AR_XmitPower2      0x3f000000
+#define AR_XmitPower2_S     24
+#define AR_XmitPower3      0x3f000000
+#define AR_XmitPower3_S     24
 #define AR_RTSEnable        0x00400000
 #define AR_VEOL             0x00800000
 #define AR_ClrDestMask      0x01000000
index 62ac95d6bb9d6e60b3bbc47e4f7715eecbe56bb9..e6ac8d2e610ca421f60dbfa6eb68dd46f8061c9d 100644 (file)
@@ -19,9 +19,6 @@
 #include "ath9k.h"
 #include "btcoex.h"
 
-static void ath9k_set_assoc_state(struct ath_softc *sc,
-                                 struct ieee80211_vif *vif);
-
 u8 ath9k_parse_mpdudensity(u8 mpdudensity)
 {
        /*
@@ -63,9 +60,16 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
 
        spin_lock_bh(&txq->axq_lock);
 
-       if (txq->axq_depth || !list_empty(&txq->axq_acq))
+       if (txq->axq_depth)
                pending = true;
 
+       if (txq->mac80211_qnum >= 0) {
+               struct list_head *list;
+
+               list = &sc->cur_chan->acq[txq->mac80211_qnum];
+               if (!list_empty(list))
+                       pending = true;
+       }
        spin_unlock_bh(&txq->axq_lock);
        return pending;
 }
@@ -227,13 +231,22 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
        }
 
        ath9k_cmn_update_txpow(ah, sc->curtxpow,
-                              sc->config.txpowlimit, &sc->curtxpow);
+                              sc->cur_chan->txpower, &sc->curtxpow);
 
        clear_bit(ATH_OP_HW_RESET, &common->op_flags);
-       ath9k_hw_set_interrupts(ah);
-       ath9k_hw_enable_interrupts(ah);
+       ath9k_calculate_summary_state(sc, sc->cur_chan);
+
+       if (!sc->cur_chan->offchannel && start) {
+               /* restore per chanctx TSF timer */
+               if (sc->cur_chan->tsf_val) {
+                       u32 offset;
+
+                       offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts,
+                                                        NULL);
+                       ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset);
+               }
+
 
-       if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) {
                if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
                        goto work;
 
@@ -247,26 +260,35 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
                }
        work:
                ath_restart_work(sc);
+               ath_txq_schedule_all(sc);
+       }
 
-               for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
-                       if (!ATH_TXQ_SETUP(sc, i))
-                               continue;
+       sc->gtt_cnt = 0;
 
-                       spin_lock_bh(&sc->tx.txq[i].axq_lock);
-                       ath_txq_schedule(sc, &sc->tx.txq[i]);
-                       spin_unlock_bh(&sc->tx.txq[i].axq_lock);
+       ath9k_hw_set_interrupts(ah);
+       ath9k_hw_enable_interrupts(ah);
+
+       if (!ath9k_use_chanctx)
+               ieee80211_wake_queues(sc->hw);
+       else {
+               if (sc->cur_chan == &sc->offchannel.chan)
+                       ieee80211_wake_queue(sc->hw,
+                                       sc->hw->offchannel_tx_hw_queue);
+               else {
+                       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+                               ieee80211_wake_queue(sc->hw,
+                                       sc->cur_chan->hw_queue_base + i);
                }
+               if (ah->opmode == NL80211_IFTYPE_AP)
+                       ieee80211_wake_queue(sc->hw, sc->hw->queues - 2);
        }
 
-       sc->gtt_cnt = 0;
-       ieee80211_wake_queues(sc->hw);
-
        ath9k_p2p_ps_timer(sc);
 
        return true;
 }
 
-static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
+int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
@@ -279,9 +301,9 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
        tasklet_disable(&sc->intr_tq);
        spin_lock_bh(&sc->sc_pcu_lock);
 
-       if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
+       if (!sc->cur_chan->offchannel) {
                fastcc = false;
-               caldata = &sc->caldata;
+               caldata = &sc->cur_chan->caldata;
        }
 
        if (!hchan) {
@@ -292,6 +314,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
        if (!ath_prepare_reset(sc))
                fastcc = false;
 
+       spin_lock_bh(&sc->chan_lock);
+       sc->cur_chandef = sc->cur_chan->chandef;
+       spin_unlock_bh(&sc->chan_lock);
+
        ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n",
                hchan->channel, IS_CHAN_HT40(hchan), fastcc);
 
@@ -307,7 +333,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
        }
 
        if (ath9k_hw_mci_is_enabled(sc->sc_ah) &&
-           (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+           sc->cur_chan->offchannel)
                ath9k_mci_set_txpower(sc, true, false);
 
        if (!ath_complete_reset(sc, true))
@@ -320,98 +346,6 @@ out:
        return r;
 }
 
-
-/*
- * Set/change channels.  If the channel is really being changed, it's done
- * by reseting the chip.  To accomplish this we must first cleanup any pending
- * DMA, then restart stuff.
-*/
-static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       struct ieee80211_hw *hw = sc->hw;
-       struct ath9k_channel *hchan;
-       struct ieee80211_channel *chan = chandef->chan;
-       bool offchannel;
-       int pos = chan->hw_value;
-       int old_pos = -1;
-       int r;
-
-       if (test_bit(ATH_OP_INVALID, &common->op_flags))
-               return -EIO;
-
-       offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
-
-       if (ah->curchan)
-               old_pos = ah->curchan - &ah->channels[0];
-
-       ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
-               chan->center_freq, chandef->width);
-
-       /* update survey stats for the old channel before switching */
-       spin_lock_bh(&common->cc_lock);
-       ath_update_survey_stats(sc);
-       spin_unlock_bh(&common->cc_lock);
-
-       ath9k_cmn_get_channel(hw, ah, chandef);
-
-       /*
-        * If the operating channel changes, change the survey in-use flags
-        * along with it.
-        * Reset the survey data for the new channel, unless we're switching
-        * back to the operating channel from an off-channel operation.
-        */
-       if (!offchannel && sc->cur_survey != &sc->survey[pos]) {
-               if (sc->cur_survey)
-                       sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
-
-               sc->cur_survey = &sc->survey[pos];
-
-               memset(sc->cur_survey, 0, sizeof(struct survey_info));
-               sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
-       } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
-               memset(&sc->survey[pos], 0, sizeof(struct survey_info));
-       }
-
-       hchan = &sc->sc_ah->channels[pos];
-       r = ath_reset_internal(sc, hchan);
-       if (r)
-               return r;
-
-       /*
-        * The most recent snapshot of channel->noisefloor for the old
-        * channel is only available after the hardware reset. Copy it to
-        * the survey stats now.
-        */
-       if (old_pos >= 0)
-               ath_update_survey_nf(sc, old_pos);
-
-       /*
-        * Enable radar pulse detection if on a DFS channel. Spectral
-        * scanning and radar detection can not be used concurrently.
-        */
-       if (hw->conf.radar_enabled) {
-               u32 rxfilter;
-
-               /* set HW specific DFS configuration */
-               ath9k_hw_set_radar_params(ah);
-               rxfilter = ath9k_hw_getrxfilter(ah);
-               rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
-                               ATH9K_RX_FILTER_PHYERR;
-               ath9k_hw_setrxfilter(ah, rxfilter);
-               ath_dbg(common, DFS, "DFS enabled at freq %d\n",
-                       chan->center_freq);
-       } else {
-               /* perform spectral scan if requested. */
-               if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
-                       sc->spectral_mode == SPECTRAL_CHANSCAN)
-                       ath9k_spectral_scan_trigger(hw);
-       }
-
-       return 0;
-}
-
 static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
                            struct ieee80211_vif *vif)
 {
@@ -712,7 +646,8 @@ static int ath9k_start(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 ieee80211_channel *curchan = hw->conf.chandef.chan;
+       struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
+       struct ath_chanctx *ctx = sc->cur_chan;
        struct ath9k_channel *init_channel;
        int r;
 
@@ -723,7 +658,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
        ath9k_ps_wakeup(sc);
        mutex_lock(&sc->mutex);
 
-       init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
+       init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef);
+       sc->cur_chandef = hw->conf.chandef;
 
        /* Reset SERDES registers */
        ath9k_hw_configpcipowersave(ah, false);
@@ -886,6 +822,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        struct ath_common *common = ath9k_hw_common(ah);
        bool prev_idle;
 
+       cancel_work_sync(&sc->chanctx_work);
        mutex_lock(&sc->mutex);
 
        ath_cancel_work(sc);
@@ -934,7 +871,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        }
 
        if (!ah->curchan)
-               ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
+               ah->curchan = ath9k_cmn_get_channel(hw, ah,
+                                                   &sc->cur_chan->chandef);
 
        ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        ath9k_hw_phy_disable(ah);
@@ -979,18 +917,29 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                iter_data->has_hw_macaddr = true;
        }
 
+       if (!vif->bss_conf.use_short_slot)
+               iter_data->slottime = ATH9K_SLOT_TIME_20;
+
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
                iter_data->naps++;
+               if (vif->bss_conf.enable_beacon)
+                       iter_data->beacons = true;
                break;
        case NL80211_IFTYPE_STATION:
                iter_data->nstations++;
+               if (vif->bss_conf.assoc && !iter_data->primary_sta)
+                       iter_data->primary_sta = vif;
                break;
        case NL80211_IFTYPE_ADHOC:
                iter_data->nadhocs++;
+               if (vif->bss_conf.enable_beacon)
+                       iter_data->beacons = true;
                break;
        case NL80211_IFTYPE_MESH_POINT:
                iter_data->nmeshes++;
+               if (vif->bss_conf.enable_beacon)
+                       iter_data->beacons = true;
                break;
        case NL80211_IFTYPE_WDS:
                iter_data->nwds++;
@@ -1000,26 +949,12 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        }
 }
 
-static void ath9k_sta_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct ath_softc *sc = data;
-       struct ath_vif *avp = (void *)vif->drv_priv;
-
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return;
-
-       if (avp->primary_sta_vif)
-               ath9k_set_assoc_state(sc, vif);
-}
-
 /* Called with sc->mutex held. */
-void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
-                              struct ieee80211_vif *vif,
+void ath9k_calculate_iter_data(struct ath_softc *sc,
+                              struct ath_chanctx *ctx,
                               struct ath9k_vif_iter_data *iter_data)
 {
-       struct ath_softc *sc = hw->priv;
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_vif *avp;
 
        /*
         * Pick the MAC address of the first interface as the new hardware
@@ -1028,29 +963,80 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
         */
        memset(iter_data, 0, sizeof(*iter_data));
        memset(&iter_data->mask, 0xff, ETH_ALEN);
+       iter_data->slottime = ATH9K_SLOT_TIME_9;
+
+       list_for_each_entry(avp, &ctx->vifs, list)
+               ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif);
+
+       if (ctx == &sc->offchannel.chan) {
+               struct ieee80211_vif *vif;
+
+               if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START)
+                       vif = sc->offchannel.scan_vif;
+               else
+                       vif = sc->offchannel.roc_vif;
+
+               if (vif)
+                       ath9k_vif_iter(iter_data, vif->addr, vif);
+               iter_data->beacons = false;
+       }
+}
+
+static void ath9k_set_assoc_state(struct ath_softc *sc,
+                                 struct ieee80211_vif *vif, bool changed)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+       unsigned long flags;
 
-       if (vif)
-               ath9k_vif_iter(iter_data, vif->addr, vif);
+       set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+       /* Set the AID, BSSID and do beacon-sync only when
+        * the HW opmode is STATION.
+        *
+        * But the primary bit is set above in any case.
+        */
+       if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
+               return;
+
+       ether_addr_copy(common->curbssid, bss_conf->bssid);
+       common->curaid = bss_conf->aid;
+       ath9k_hw_write_associd(sc->sc_ah);
 
-       /* Get list of all active MAC addresses */
-       ieee80211_iterate_active_interfaces_atomic(
-               sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-               ath9k_vif_iter, iter_data);
+       if (changed) {
+               common->last_rssi = ATH_RSSI_DUMMY_MARKER;
+               sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
 
-       memcpy(common->macaddr, iter_data->hw_macaddr, ETH_ALEN);
+               spin_lock_irqsave(&sc->sc_pm_lock, flags);
+               sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+       }
+
+       if (ath9k_hw_mci_is_enabled(sc->sc_ah))
+               ath9k_mci_update_wlan_channels(sc, false);
+
+       ath_dbg(common, CONFIG,
+               "Primary Station interface: %pM, BSSID: %pM\n",
+               vif->addr, common->curbssid);
 }
 
 /* Called with sc->mutex held. */
-static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
-                                         struct ieee80211_vif *vif)
+void ath9k_calculate_summary_state(struct ath_softc *sc,
+                                  struct ath_chanctx *ctx)
 {
-       struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_vif_iter_data iter_data;
-       enum nl80211_iftype old_opmode = ah->opmode;
 
-       ath9k_calculate_iter_data(hw, vif, &iter_data);
+       ath_chanctx_check_active(sc, ctx);
+
+       if (ctx != sc->cur_chan)
+               return;
+
+       ath9k_ps_wakeup(sc);
+       ath9k_calculate_iter_data(sc, ctx, &iter_data);
+
+       if (iter_data.has_hw_macaddr)
+               ether_addr_copy(common->macaddr, iter_data.hw_macaddr);
 
        memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
        ath_hw_setbssidmask(common);
@@ -1073,24 +1059,57 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
 
        ath9k_hw_setopmode(ah);
 
+       ctx->switch_after_beacon = false;
        if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0)
                ah->imask |= ATH9K_INT_TSFOOR;
-       else
+       else {
                ah->imask &= ~ATH9K_INT_TSFOOR;
+               if (iter_data.naps == 1 && iter_data.beacons)
+                       ctx->switch_after_beacon = true;
+       }
+
+       ah->imask &= ~ATH9K_INT_SWBA;
+       if (ah->opmode == NL80211_IFTYPE_STATION) {
+               bool changed = (iter_data.primary_sta != ctx->primary_sta);
 
+               iter_data.beacons = true;
+               if (iter_data.primary_sta) {
+                       ath9k_set_assoc_state(sc, iter_data.primary_sta,
+                                             changed);
+                       if (!ctx->primary_sta ||
+                           !ctx->primary_sta->bss_conf.assoc)
+                               ctx->primary_sta = iter_data.primary_sta;
+               } else {
+                       ctx->primary_sta = NULL;
+                       memset(common->curbssid, 0, ETH_ALEN);
+                       common->curaid = 0;
+                       ath9k_hw_write_associd(sc->sc_ah);
+                       if (ath9k_hw_mci_is_enabled(sc->sc_ah))
+                               ath9k_mci_update_wlan_channels(sc, true);
+               }
+       } else if (iter_data.beacons) {
+               ah->imask |= ATH9K_INT_SWBA;
+       }
        ath9k_hw_set_interrupts(ah);
 
-       /*
-        * If we are changing the opmode to STATION,
-        * a beacon sync needs to be done.
-        */
-       if (ah->opmode == NL80211_IFTYPE_STATION &&
-           old_opmode == NL80211_IFTYPE_AP &&
-           test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
-               ieee80211_iterate_active_interfaces_atomic(
-                       sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                       ath9k_sta_vif_iter, sc);
+       if (iter_data.beacons)
+               set_bit(ATH_OP_BEACONS, &common->op_flags);
+       else
+               clear_bit(ATH_OP_BEACONS, &common->op_flags);
+
+       if (ah->slottime != iter_data.slottime) {
+               ah->slottime = iter_data.slottime;
+               ath9k_hw_init_global_settings(ah);
        }
+
+       if (iter_data.primary_sta)
+               set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+       else
+               clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+
+       ctx->primary_sta = iter_data.primary_sta;
+
+       ath9k_ps_restore(sc);
 }
 
 static int ath9k_add_interface(struct ieee80211_hw *hw,
@@ -1101,6 +1120,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath_vif *avp = (void *)vif->drv_priv;
        struct ath_node *an = &avp->mcast_node;
+       int i;
 
        mutex_lock(&sc->mutex);
 
@@ -1115,14 +1135,20 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
        sc->nvifs++;
 
-       ath9k_ps_wakeup(sc);
-       ath9k_calculate_summary_state(hw, vif);
-       ath9k_ps_restore(sc);
-
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_assign_slot(sc, vif);
 
        avp->vif = vif;
+       if (!ath9k_use_chanctx) {
+               avp->chanctx = sc->cur_chan;
+               list_add_tail(&avp->list, &avp->chanctx->vifs);
+       }
+       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+               vif->hw_queue[i] = i;
+       if (vif->type == NL80211_IFTYPE_AP)
+               vif->cab_queue = hw->queues - 2;
+       else
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
 
        an->sc = sc;
        an->sta = NULL;
@@ -1141,6 +1167,8 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
 {
        struct ath_softc *sc = hw->priv;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       int i;
 
        mutex_lock(&sc->mutex);
 
@@ -1157,13 +1185,19 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
        vif->type = new_type;
        vif->p2p = p2p;
 
-       ath9k_ps_wakeup(sc);
-       ath9k_calculate_summary_state(hw, vif);
-       ath9k_ps_restore(sc);
-
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_assign_slot(sc, vif);
 
+       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+               vif->hw_queue[i] = i;
+
+       if (vif->type == NL80211_IFTYPE_AP)
+               vif->cab_queue = hw->queues - 2;
+       else
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+
+       ath9k_calculate_summary_state(sc, avp->chanctx);
+
        mutex_unlock(&sc->mutex);
        return 0;
 }
@@ -1211,14 +1245,12 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
 
        sc->nvifs--;
        sc->tx99_vif = NULL;
+       if (!ath9k_use_chanctx)
+               list_del(&avp->list);
 
        if (ath9k_uses_beacons(vif->type))
                ath9k_beacon_remove_slot(sc, vif);
 
-       ath9k_ps_wakeup(sc);
-       ath9k_calculate_summary_state(hw, NULL);
-       ath9k_ps_restore(sc);
-
        ath_tx_node_cleanup(sc, &avp->mcast_node);
 
        mutex_unlock(&sc->mutex);
@@ -1345,7 +1377,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ieee80211_conf *conf = &hw->conf;
-       bool reset_channel = false;
+       struct ath_chanctx *ctx = sc->cur_chan;
 
        ath9k_ps_wakeup(sc);
        mutex_lock(&sc->mutex);
@@ -1361,7 +1393,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                         * The chip needs a reset to properly wake up from
                         * full sleep
                         */
-                       reset_channel = ah->chip_fullsleep;
+                       ath_chanctx_set_channel(sc, ctx, &ctx->chandef);
                }
        }
 
@@ -1391,20 +1423,16 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                }
        }
 
-       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
-               if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
-                       ath_err(common, "Unable to set channel\n");
-                       mutex_unlock(&sc->mutex);
-                       ath9k_ps_restore(sc);
-                       return -EINVAL;
-               }
+       if (!ath9k_use_chanctx && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
+               ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL);
+               ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef);
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
                ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
-               sc->config.txpowlimit = 2 * conf->power_level;
+               sc->cur_chan->txpower = 2 * conf->power_level;
                ath9k_cmn_update_txpow(ah, sc->curtxpow,
-                                      sc->config.txpowlimit, &sc->curtxpow);
+                                      sc->cur_chan->txpower, &sc->curtxpow);
        }
 
        mutex_unlock(&sc->mutex);
@@ -1659,58 +1687,6 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
        return ret;
 }
 
-static void ath9k_set_assoc_state(struct ath_softc *sc,
-                                 struct ieee80211_vif *vif)
-{
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       struct ath_vif *avp = (void *)vif->drv_priv;
-       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-       unsigned long flags;
-
-       set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
-       avp->primary_sta_vif = true;
-
-       /*
-        * Set the AID, BSSID and do beacon-sync only when
-        * the HW opmode is STATION.
-        *
-        * But the primary bit is set above in any case.
-        */
-       if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
-               return;
-
-       memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
-       common->curaid = bss_conf->aid;
-       ath9k_hw_write_associd(sc->sc_ah);
-
-       common->last_rssi = ATH_RSSI_DUMMY_MARKER;
-       sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
-
-       spin_lock_irqsave(&sc->sc_pm_lock, flags);
-       sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
-       spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
-
-       if (ath9k_hw_mci_is_enabled(sc->sc_ah))
-               ath9k_mci_update_wlan_channels(sc, false);
-
-       ath_dbg(common, CONFIG,
-               "Primary Station interface: %pM, BSSID: %pM\n",
-               vif->addr, common->curbssid);
-}
-
-static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct ath_softc *sc = data;
-       struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-
-       if (test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
-               return;
-
-       if (bss_conf->assoc)
-               ath9k_set_assoc_state(sc, vif);
-}
-
 void ath9k_p2p_ps_timer(void *priv)
 {
        struct ath_softc *sc = priv;
@@ -1720,7 +1696,11 @@ void ath9k_p2p_ps_timer(void *priv)
        struct ath_node *an;
        u32 tsf;
 
-       if (!avp)
+       del_timer_sync(&sc->sched.timer);
+       ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer);
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
+
+       if (!avp || avp->chanctx != sc->cur_chan)
                return;
 
        tsf = ath9k_hw_gettsf32(sc->sc_ah);
@@ -1795,26 +1775,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n",
                        bss_conf->bssid, bss_conf->assoc);
 
-               if (avp->primary_sta_vif && !bss_conf->assoc) {
-                       clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
-                       avp->primary_sta_vif = false;
-
-                       if (ah->opmode == NL80211_IFTYPE_STATION)
-                               clear_bit(ATH_OP_BEACONS, &common->op_flags);
-               }
-
-               ieee80211_iterate_active_interfaces_atomic(
-                       sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-                       ath9k_bss_assoc_iter, sc);
-
-               if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags) &&
-                   ah->opmode == NL80211_IFTYPE_STATION) {
-                       memset(common->curbssid, 0, ETH_ALEN);
-                       common->curaid = 0;
-                       ath9k_hw_write_associd(sc->sc_ah);
-                       if (ath9k_hw_mci_is_enabled(sc->sc_ah))
-                               ath9k_mci_update_wlan_channels(sc, true);
-               }
+               ath9k_calculate_summary_state(sc, avp->chanctx);
+               if (bss_conf->assoc)
+                       ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_ASSOC);
        }
 
        if (changed & BSS_CHANGED_IBSS) {
@@ -1824,10 +1787,15 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
        }
 
        if ((changed & BSS_CHANGED_BEACON_ENABLED) ||
-           (changed & BSS_CHANGED_BEACON_INT))
+           (changed & BSS_CHANGED_BEACON_INT) ||
+           (changed & BSS_CHANGED_BEACON_INFO)) {
+               if (changed & BSS_CHANGED_BEACON_ENABLED)
+                       ath9k_calculate_summary_state(sc, avp->chanctx);
                ath9k_beacon_config(sc, vif, changed);
+       }
 
-       if (changed & BSS_CHANGED_ERP_SLOT) {
+       if ((avp->chanctx == sc->cur_chan) &&
+           (changed & BSS_CHANGED_ERP_SLOT)) {
                if (bss_conf->use_short_slot)
                        slottime = 9;
                else
@@ -2030,25 +1998,32 @@ static bool ath9k_has_tx_pending(struct ath_softc *sc)
 
 static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        u32 queues, bool drop)
+{
+       struct ath_softc *sc = hw->priv;
+
+       mutex_lock(&sc->mutex);
+       __ath9k_flush(hw, queues, drop);
+       mutex_unlock(&sc->mutex);
+}
+
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
 {
        struct ath_softc *sc = hw->priv;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        int timeout = HZ / 5; /* 200 ms */
        bool drain_txq;
+       int i;
 
-       mutex_lock(&sc->mutex);
        cancel_delayed_work_sync(&sc->tx_complete_work);
 
        if (ah->ah_flags & AH_UNPLUGGED) {
                ath_dbg(common, ANY, "Device has been unplugged!\n");
-               mutex_unlock(&sc->mutex);
                return;
        }
 
        if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
                ath_dbg(common, ANY, "Device not present\n");
-               mutex_unlock(&sc->mutex);
                return;
        }
 
@@ -2066,11 +2041,13 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        ath_reset(sc);
 
                ath9k_ps_restore(sc);
-               ieee80211_wake_queues(hw);
+               for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+                       ieee80211_wake_queue(sc->hw,
+                                            sc->cur_chan->hw_queue_base + i);
+               }
        }
 
        ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
-       mutex_unlock(&sc->mutex);
 }
 
 static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw)
@@ -2230,6 +2207,403 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
        clear_bit(ATH_OP_SCANNING, &common->op_flags);
 }
 
+static int ath_scan_channel_duration(struct ath_softc *sc,
+                                    struct ieee80211_channel *chan)
+{
+       struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+
+       if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR))
+               return (HZ / 9); /* ~110 ms */
+
+       return (HZ / 16); /* ~60 ms */
+}
+
+static void
+ath_scan_next_channel(struct ath_softc *sc)
+{
+       struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+       struct ieee80211_channel *chan;
+
+       if (sc->offchannel.scan_idx >= req->n_channels) {
+               sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+               ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
+                                  NULL);
+               return;
+       }
+
+       chan = req->channels[sc->offchannel.scan_idx++];
+       sc->offchannel.duration = ath_scan_channel_duration(sc, chan);
+       sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND;
+       ath_chanctx_offchan_switch(sc, chan);
+}
+
+static void ath_offchannel_next(struct ath_softc *sc)
+{
+       struct ieee80211_vif *vif;
+
+       if (sc->offchannel.scan_req) {
+               vif = sc->offchannel.scan_vif;
+               sc->offchannel.chan.txpower = vif->bss_conf.txpower;
+               ath_scan_next_channel(sc);
+       } else if (sc->offchannel.roc_vif) {
+               vif = sc->offchannel.roc_vif;
+               sc->offchannel.chan.txpower = vif->bss_conf.txpower;
+               sc->offchannel.duration = sc->offchannel.roc_duration;
+               sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
+               ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
+       } else {
+               ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
+                                  NULL);
+               sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+               if (sc->ps_idle)
+                       ath_cancel_work(sc);
+       }
+}
+
+static void ath_roc_complete(struct ath_softc *sc, bool abort)
+{
+       sc->offchannel.roc_vif = NULL;
+       sc->offchannel.roc_chan = NULL;
+       if (!abort)
+               ieee80211_remain_on_channel_expired(sc->hw);
+       ath_offchannel_next(sc);
+       ath9k_ps_restore(sc);
+}
+
+static void ath_scan_complete(struct ath_softc *sc, bool abort)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+       sc->offchannel.scan_req = NULL;
+       sc->offchannel.scan_vif = NULL;
+       sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+       ieee80211_scan_completed(sc->hw, abort);
+       clear_bit(ATH_OP_SCANNING, &common->op_flags);
+       ath_offchannel_next(sc);
+       ath9k_ps_restore(sc);
+}
+
+static void ath_scan_send_probe(struct ath_softc *sc,
+                               struct cfg80211_ssid *ssid)
+{
+       struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+       struct ieee80211_vif *vif = sc->offchannel.scan_vif;
+       struct ath_tx_control txctl = {};
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *info;
+       int band = sc->offchannel.chan.chandef.chan->band;
+
+       skb = ieee80211_probereq_get(sc->hw, vif,
+                       ssid->ssid, ssid->ssid_len, req->ie_len);
+       if (!skb)
+               return;
+
+       info = IEEE80211_SKB_CB(skb);
+       if (req->no_cck)
+               info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
+
+       if (req->ie_len)
+               memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
+
+       skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+
+       if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
+               goto error;
+
+       txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
+       txctl.force_channel = true;
+       if (ath_tx_start(sc->hw, skb, &txctl))
+               goto error;
+
+       return;
+
+error:
+       ieee80211_free_txskb(sc->hw, skb);
+}
+
+static void ath_scan_channel_start(struct ath_softc *sc)
+{
+       struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+       int i;
+
+       if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) &&
+           req->n_ssids) {
+               for (i = 0; i < req->n_ssids; i++)
+                       ath_scan_send_probe(sc, &req->ssids[i]);
+
+       }
+
+       sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT;
+       mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration);
+}
+
+void ath_offchannel_channel_change(struct ath_softc *sc)
+{
+       switch (sc->offchannel.state) {
+       case ATH_OFFCHANNEL_PROBE_SEND:
+               if (!sc->offchannel.scan_req)
+                       return;
+
+               if (sc->cur_chan->chandef.chan !=
+                   sc->offchannel.chan.chandef.chan)
+                       return;
+
+               ath_scan_channel_start(sc);
+               break;
+       case ATH_OFFCHANNEL_IDLE:
+               if (!sc->offchannel.scan_req)
+                       return;
+
+               ath_scan_complete(sc, false);
+               break;
+       case ATH_OFFCHANNEL_ROC_START:
+               if (sc->cur_chan != &sc->offchannel.chan)
+                       break;
+
+               sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT;
+               mod_timer(&sc->offchannel.timer, jiffies +
+                         msecs_to_jiffies(sc->offchannel.duration));
+               ieee80211_ready_on_channel(sc->hw);
+               break;
+       case ATH_OFFCHANNEL_ROC_DONE:
+               ath_roc_complete(sc, false);
+               break;
+       default:
+               break;
+       }
+}
+
+void ath_offchannel_timer(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *)data;
+       struct ath_chanctx *ctx;
+
+       switch (sc->offchannel.state) {
+       case ATH_OFFCHANNEL_PROBE_WAIT:
+               if (!sc->offchannel.scan_req)
+                       return;
+
+               /* get first active channel context */
+               ctx = ath_chanctx_get_oper_chan(sc, true);
+               if (ctx->active) {
+                       sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
+                       ath_chanctx_switch(sc, ctx, NULL);
+                       mod_timer(&sc->offchannel.timer, jiffies + HZ / 10);
+                       break;
+               }
+               /* fall through */
+       case ATH_OFFCHANNEL_SUSPEND:
+               if (!sc->offchannel.scan_req)
+                       return;
+
+               ath_scan_next_channel(sc);
+               break;
+       case ATH_OFFCHANNEL_ROC_START:
+       case ATH_OFFCHANNEL_ROC_WAIT:
+               ctx = ath_chanctx_get_oper_chan(sc, false);
+               sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE;
+               ath_chanctx_switch(sc, ctx, NULL);
+               break;
+       default:
+               break;
+       }
+}
+
+static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        struct ieee80211_scan_request *hw_req)
+{
+       struct cfg80211_scan_request *req = &hw_req->req;
+       struct ath_softc *sc = hw->priv;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       int ret = 0;
+
+       mutex_lock(&sc->mutex);
+
+       if (WARN_ON(sc->offchannel.scan_req)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ath9k_ps_wakeup(sc);
+       set_bit(ATH_OP_SCANNING, &common->op_flags);
+       sc->offchannel.scan_vif = vif;
+       sc->offchannel.scan_req = req;
+       sc->offchannel.scan_idx = 0;
+
+       if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
+               ath_offchannel_next(sc);
+
+out:
+       mutex_unlock(&sc->mutex);
+
+       return ret;
+}
+
+static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif)
+{
+       struct ath_softc *sc = hw->priv;
+
+       mutex_lock(&sc->mutex);
+       del_timer_sync(&sc->offchannel.timer);
+       ath_scan_complete(sc, true);
+       mutex_unlock(&sc->mutex);
+}
+
+static int ath9k_remain_on_channel(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  struct ieee80211_channel *chan, int duration,
+                                  enum ieee80211_roc_type type)
+{
+       struct ath_softc *sc = hw->priv;
+       int ret = 0;
+
+       mutex_lock(&sc->mutex);
+
+       if (WARN_ON(sc->offchannel.roc_vif)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ath9k_ps_wakeup(sc);
+       sc->offchannel.roc_vif = vif;
+       sc->offchannel.roc_chan = chan;
+       sc->offchannel.roc_duration = duration;
+
+       if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
+               ath_offchannel_next(sc);
+
+out:
+       mutex_unlock(&sc->mutex);
+
+       return ret;
+}
+
+static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+       struct ath_softc *sc = hw->priv;
+
+       mutex_lock(&sc->mutex);
+
+       del_timer_sync(&sc->offchannel.timer);
+
+       if (sc->offchannel.roc_vif) {
+               if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
+                       ath_roc_complete(sc, true);
+       }
+
+       mutex_unlock(&sc->mutex);
+
+       return 0;
+}
+
+static int ath9k_add_chanctx(struct ieee80211_hw *hw,
+                            struct ieee80211_chanctx_conf *conf)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_chanctx *ctx, **ptr;
+       int pos;
+
+       mutex_lock(&sc->mutex);
+
+       ath_for_each_chanctx(sc, ctx) {
+               if (ctx->assigned)
+                       continue;
+
+               ptr = (void *) conf->drv_priv;
+               *ptr = ctx;
+               ctx->assigned = true;
+               pos = ctx - &sc->chanctx[0];
+               ctx->hw_queue_base = pos * IEEE80211_NUM_ACS;
+               ath_chanctx_set_channel(sc, ctx, &conf->def);
+               mutex_unlock(&sc->mutex);
+               return 0;
+       }
+       mutex_unlock(&sc->mutex);
+       return -ENOSPC;
+}
+
+
+static void ath9k_remove_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_chanctx_conf *conf)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+       mutex_lock(&sc->mutex);
+       ctx->assigned = false;
+       ctx->hw_queue_base = -1;
+       ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN);
+       mutex_unlock(&sc->mutex);
+}
+
+static void ath9k_change_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_chanctx_conf *conf,
+                                u32 changed)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+       mutex_lock(&sc->mutex);
+       ath_chanctx_set_channel(sc, ctx, &conf->def);
+       mutex_unlock(&sc->mutex);
+}
+
+static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_chanctx_conf *conf)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_chanctx *ctx = ath_chanctx_get(conf);
+       int i;
+
+       mutex_lock(&sc->mutex);
+       avp->chanctx = ctx;
+       list_add_tail(&avp->list, &ctx->vifs);
+       ath9k_calculate_summary_state(sc, ctx);
+       for (i = 0; i < IEEE80211_NUM_ACS; i++)
+               vif->hw_queue[i] = ctx->hw_queue_base + i;
+       mutex_unlock(&sc->mutex);
+
+       return 0;
+}
+
+static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_chanctx_conf *conf)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath_vif *avp = (void *)vif->drv_priv;
+       struct ath_chanctx *ctx = ath_chanctx_get(conf);
+       int ac;
+
+       mutex_lock(&sc->mutex);
+       avp->chanctx = NULL;
+       list_del(&avp->list);
+       ath9k_calculate_summary_state(sc, ctx);
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
+       mutex_unlock(&sc->mutex);
+}
+
+void ath9k_fill_chanctx_ops(void)
+{
+       if (!ath9k_use_chanctx)
+               return;
+
+       ath9k_ops.hw_scan = ath9k_hw_scan;
+       ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
+       ath9k_ops.remain_on_channel  = ath9k_remain_on_channel;
+       ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel;
+       ath9k_ops.add_chanctx        = ath9k_add_chanctx;
+       ath9k_ops.remove_chanctx     = ath9k_remove_chanctx;
+       ath9k_ops.change_chanctx     = ath9k_change_chanctx;
+       ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx;
+       ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx;
+       ath9k_ops.mgd_prepare_tx = ath9k_chanctx_force_active;
+}
+
 struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,
index a0dbcc4123840cbeab93e56939ce6b095f3e885d..3f7a11edb82a77dedcfdc5f38fa4c6c832e9579e 100644 (file)
@@ -706,7 +706,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
                return;
 
        if (setchannel) {
-               struct ath9k_hw_cal_data *caldata = &sc->caldata;
+               struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata;
                if (IS_CHAN_HT40PLUS(ah->curchan) &&
                    (ah->curchan->channel > caldata->channel) &&
                    (ah->curchan->channel <= caldata->channel + 20))
@@ -720,7 +720,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
                mci_hw->concur_tx = concur_tx;
 
        if (old_concur_tx != mci_hw->concur_tx)
-               ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+               ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
 }
 
 static void ath9k_mci_stomp_audio(struct ath_softc *sc)
index 4dec09e565ed865470ff6bc5738e86c21d523906..7a2b2c5caced25aa7919dad9528fd4bf4a64717b 100644 (file)
@@ -843,6 +843,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                return -ENODEV;
        }
 
+       ath9k_fill_chanctx_ops();
        hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
        if (!hw) {
                dev_err(&pdev->dev, "No memory for ieee80211_hw\n");
index 9105a92364f78241fa565b661a017ce59f7400f5..74ab1d02013bf002bf04c0cfd197a2ecd8cdc29a 100644 (file)
@@ -259,7 +259,7 @@ static void ath_edma_start_recv(struct ath_softc *sc)
        ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP);
        ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP);
        ath_opmode_init(sc);
-       ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+       ath9k_hw_startpcureceive(sc->sc_ah, sc->cur_chan->offchannel);
 }
 
 static void ath_edma_stop_recv(struct ath_softc *sc)
@@ -374,6 +374,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
 
 u32 ath_calcrxfilter(struct ath_softc *sc)
 {
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        u32 rfilt;
 
        if (config_enabled(CONFIG_ATH9K_TX99))
@@ -424,6 +425,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
        if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
                rfilt |= ATH9K_RX_FILTER_4ADDRESS;
 
+       if (ath9k_use_chanctx &&
+           test_bit(ATH_OP_SCANNING, &common->op_flags))
+               rfilt |= ATH9K_RX_FILTER_BEACON;
+
        return rfilt;
 
 }
@@ -457,7 +462,7 @@ int ath_startrecv(struct ath_softc *sc)
 
 start_recv:
        ath_opmode_init(sc);
-       ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+       ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel);
 
        return 0;
 }
@@ -540,7 +545,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
                sc->ps_flags &= ~PS_BEACON_SYNC;
                ath_dbg(common, PS,
                        "Reconfigure beacon timers based on synchronized timestamp\n");
-               if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0)))
+               if (!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0)))
                        ath9k_set_beacon(sc);
                if (sc->p2p_ps_vif)
                        ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif);
@@ -887,6 +892,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
                return -EINVAL;
        }
 
+       if (rx_stats->is_mybeacon) {
+               sc->sched.next_tbtt = rx_stats->rs_tstamp;
+               ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_BEACON_RECEIVED);
+       }
+
        ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status);
 
        rx_status->band = ah->curchan->chan->band;
index f1bbce3f7774ee16a9e29b8bb62f72ade1a9d99f..a1499700bcf26d243fd159f0d6d918b459c8239f 100644 (file)
 #define AR_SREV_VERSION_9531            0x500
 #define AR_SREV_REVISION_9531_10        0
 #define AR_SREV_REVISION_9531_11        1
+#define AR_SREV_REVISION_9531_20        2
 
 #define AR_SREV_5416(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \
 #define AR_SREV_9531_11(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
         ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_11))
+#define AR_SREV_9531_20(_ah) \
+       (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
+        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_20))
 
 /* NOTE: When adding chips newer than Peacock, add chip check here */
 #define AR_SREV_9580_10_OR_LATER(_ah) \
index 99f4de95c264b0638108d58820efcb9b8390e8fc..5fe29b9f8fa26e31b05eaab237ac4830aaf21471 100644 (file)
@@ -313,7 +313,7 @@ static ssize_t write_file_spectral_short_repeat(struct file *file,
        if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
-       if (val < 0 || val > 1)
+       if (val > 1)
                return -EINVAL;
 
        sc->spec_config.short_repeat = val;
@@ -361,7 +361,7 @@ static ssize_t write_file_spectral_count(struct file *file,
        if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
-       if (val < 0 || val > 255)
+       if (val > 255)
                return -EINVAL;
 
        sc->spec_config.count = val;
@@ -409,7 +409,7 @@ static ssize_t write_file_spectral_period(struct file *file,
        if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
-       if (val < 0 || val > 255)
+       if (val > 255)
                return -EINVAL;
 
        sc->spec_config.period = val;
@@ -457,7 +457,7 @@ static ssize_t write_file_spectral_fft_period(struct file *file,
        if (kstrtoul(buf, 0, &val))
                return -EINVAL;
 
-       if (val < 0 || val > 15)
+       if (val > 15)
                return -EINVAL;
 
        sc->spec_config.fft_period = val;
index a65cfb91adcae12ea2d196c8e7c0f8658316f6ef..23972924c774becd09f633f0708159594bc1aae1 100644 (file)
@@ -76,7 +76,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
        tx_info = IEEE80211_SKB_CB(skb);
        memset(tx_info, 0, sizeof(*tx_info));
        rate = &tx_info->control.rates[0];
-       tx_info->band = hw->conf.chandef.chan->band;
+       tx_info->band = sc->cur_chan->chandef.chan->band;
        tx_info->flags = IEEE80211_TX_CTL_NO_ACK;
        tx_info->control.vif = sc->tx99_vif;
        rate->count = 1;
index 2879887f56912dea38e2568ddb748f25790e7bb0..a4f4f0da81f6e2a1cc71349a0c68185a2f07e546 100644 (file)
@@ -193,6 +193,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
        u32 wow_triggers_enabled = 0;
        int ret = 0;
 
+       cancel_work_sync(&sc->chanctx_work);
        mutex_lock(&sc->mutex);
 
        ath_cancel_work(sc);
index 7c28cb55610b377a1a3651b54c901f9c9ec97f97..704fcbcbe20b5a59723063c77ac0d98ee482a99f 100644 (file)
@@ -103,9 +103,16 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
                ieee80211_tx_status(sc->hw, skb);
 }
 
-static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
+static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq,
+                            struct ath_atx_tid *tid)
 {
        struct ath_atx_ac *ac = tid->ac;
+       struct list_head *list;
+       struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv;
+       struct ath_chanctx *ctx = avp->chanctx;
+
+       if (!ctx)
+               return;
 
        if (tid->sched)
                return;
@@ -117,7 +124,9 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
                return;
 
        ac->sched = true;
-       list_add_tail(&ac->list, &txq->axq_acq);
+
+       list = &ctx->acq[TID_TO_WME_AC(tid->tidno)];
+       list_add_tail(&ac->list, list);
 }
 
 static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
@@ -147,21 +156,22 @@ static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
 static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
                             struct sk_buff *skb)
 {
-       int q;
-
-       q = skb_get_queue_mapping(skb);
-       if (txq == sc->tx.uapsdq)
-               txq = sc->tx.txq_map[q];
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ath_frame_info *fi = get_frame_info(skb);
+       int hw_queue;
+       int q = fi->txq;
 
-       if (txq != sc->tx.txq_map[q])
+       if (q < 0)
                return;
 
+       txq = sc->tx.txq_map[q];
        if (WARN_ON(--txq->pending_frames < 0))
                txq->pending_frames = 0;
 
+       hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
        if (txq->stopped &&
            txq->pending_frames < sc->tx.txq_max_pending[q]) {
-               ieee80211_wake_queue(sc->hw, q);
+               ieee80211_wake_queue(sc->hw, hw_queue);
                txq->stopped = false;
        }
 }
@@ -626,7 +636,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
 
                skb_queue_splice_tail(&bf_pending, &tid->retry_q);
                if (!an->sleeping) {
-                       ath_tx_queue_tid(txq, tid);
+                       ath_tx_queue_tid(sc, txq, tid);
 
                        if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY))
                                tid->ac->clear_ps_filter = true;
@@ -1492,7 +1502,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
                ac->clear_ps_filter = true;
 
                if (ath_tid_has_buffered(tid)) {
-                       ath_tx_queue_tid(txq, tid);
+                       ath_tx_queue_tid(sc, txq, tid);
                        ath_txq_schedule(sc, txq);
                }
 
@@ -1516,7 +1526,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
        tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
 
        if (ath_tid_has_buffered(tid)) {
-               ath_tx_queue_tid(txq, tid);
+               ath_tx_queue_tid(sc, txq, tid);
                ath_txq_schedule(sc, txq);
        }
 
@@ -1651,7 +1661,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
                txq->axq_link = NULL;
                __skb_queue_head_init(&txq->complete_q);
                INIT_LIST_HEAD(&txq->axq_q);
-               INIT_LIST_HEAD(&txq->axq_acq);
                spin_lock_init(&txq->axq_lock);
                txq->axq_depth = 0;
                txq->axq_ampdu_depth = 0;
@@ -1695,7 +1704,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
 int ath_cabq_update(struct ath_softc *sc)
 {
        struct ath9k_tx_queue_info qi;
-       struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+       struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
        int qnum = sc->beacon.cabq->axq_qnum;
 
        ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
@@ -1813,7 +1822,7 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
        sc->tx.txqsetup &= ~(1<<txq->axq_qnum);
 }
 
-/* For each axq_acq entry, for each tid, try to schedule packets
+/* For each acq entry, for each tid, try to schedule packets
  * for transmit until ampdu_depth has reached min Q depth.
  */
 void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
@@ -1821,19 +1830,31 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_atx_ac *ac, *last_ac;
        struct ath_atx_tid *tid, *last_tid;
+       struct list_head *ac_list;
        bool sent = false;
 
+       if (txq->mac80211_qnum < 0)
+               return;
+
+       spin_lock_bh(&sc->chan_lock);
+       ac_list = &sc->cur_chan->acq[txq->mac80211_qnum];
+       spin_unlock_bh(&sc->chan_lock);
+
        if (test_bit(ATH_OP_HW_RESET, &common->op_flags) ||
-           list_empty(&txq->axq_acq))
+           list_empty(ac_list))
                return;
 
+       spin_lock_bh(&sc->chan_lock);
        rcu_read_lock();
 
-       last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list);
-       while (!list_empty(&txq->axq_acq)) {
+       last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list);
+       while (!list_empty(ac_list)) {
                bool stop = false;
 
-               ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
+               if (sc->cur_chan->stopped)
+                       break;
+
+               ac = list_first_entry(ac_list, struct ath_atx_ac, list);
                last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list);
                list_del(&ac->list);
                ac->sched = false;
@@ -1853,7 +1874,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
                         * are pending for the tid
                         */
                        if (ath_tid_has_buffered(tid))
-                               ath_tx_queue_tid(txq, tid);
+                               ath_tx_queue_tid(sc, txq, tid);
 
                        if (stop || tid == last_tid)
                                break;
@@ -1861,7 +1882,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
 
                if (!list_empty(&ac->tid_q) && !ac->sched) {
                        ac->sched = true;
-                       list_add_tail(&ac->list, &txq->axq_acq);
+                       list_add_tail(&ac->list, ac_list);
                }
 
                if (stop)
@@ -1872,12 +1893,27 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
                                break;
 
                        sent = false;
-                       last_ac = list_entry(txq->axq_acq.prev,
+                       last_ac = list_entry(ac_list->prev,
                                             struct ath_atx_ac, list);
                }
        }
 
        rcu_read_unlock();
+       spin_unlock_bh(&sc->chan_lock);
+}
+
+void ath_txq_schedule_all(struct ath_softc *sc)
+{
+       struct ath_txq *txq;
+       int i;
+
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               txq = sc->tx.txq_map[i];
+
+               spin_lock_bh(&txq->axq_lock);
+               ath_txq_schedule(sc, txq);
+               spin_unlock_bh(&txq->axq_lock);
+       }
 }
 
 /***********/
@@ -2008,6 +2044,7 @@ static void setup_frame_info(struct ieee80211_hw *hw,
                an = (struct ath_node *) sta->drv_priv;
 
        memset(fi, 0, sizeof(*fi));
+       fi->txq = -1;
        if (hw_key)
                fi->keyix = hw_key->hw_key_idx;
        else if (an && ieee80211_is_data(hdr->frame_control) && an->ps_key > 0)
@@ -2159,13 +2196,22 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_sta *sta = txctl->sta;
        struct ieee80211_vif *vif = info->control.vif;
+       struct ath_frame_info *fi = get_frame_info(skb);
+       struct ath_vif *avp = NULL;
        struct ath_softc *sc = hw->priv;
        struct ath_txq *txq = txctl->txq;
        struct ath_atx_tid *tid = NULL;
        struct ath_buf *bf;
-       int q;
+       bool queue;
+       int q, hw_queue;
        int ret;
 
+       if (vif)
+               avp = (void *)vif->drv_priv;
+
+       if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+               txctl->force_channel = true;
+
        ret = ath_tx_prepare(hw, skb, txctl);
        if (ret)
            return ret;
@@ -2177,24 +2223,41 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
         */
 
        q = skb_get_queue_mapping(skb);
+       hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
 
        ath_txq_lock(sc, txq);
-       if (txq == sc->tx.txq_map[q] &&
-           ++txq->pending_frames > sc->tx.txq_max_pending[q] &&
-           !txq->stopped) {
-               ieee80211_stop_queue(sc->hw, q);
-               txq->stopped = true;
+       if (txq == sc->tx.txq_map[q]) {
+               fi->txq = q;
+               if (++txq->pending_frames > sc->tx.txq_max_pending[q] &&
+                   !txq->stopped) {
+                       ieee80211_stop_queue(sc->hw, hw_queue);
+                       txq->stopped = true;
+               }
+       }
+
+       queue = ieee80211_is_data_present(hdr->frame_control);
+
+       /* Force queueing of all frames that belong to a virtual interface on
+        * a different channel context, to ensure that they are sent on the
+        * correct channel.
+        */
+       if (((avp && avp->chanctx != sc->cur_chan) ||
+            sc->cur_chan->stopped) && !txctl->force_channel) {
+               if (!txctl->an)
+                       txctl->an = &avp->mcast_node;
+               info->flags &= ~IEEE80211_TX_CTL_PS_RESPONSE;
+               queue = true;
        }
 
-       if (txctl->an && ieee80211_is_data_present(hdr->frame_control))
+       if (txctl->an && queue)
                tid = ath_get_skb_tid(sc, txctl->an, skb);
 
-       if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
+       if (info->flags & (IEEE80211_TX_CTL_PS_RESPONSE |
+                          IEEE80211_TX_CTL_TX_OFFCHAN)) {
                ath_txq_unlock(sc, txq);
                txq = sc->tx.uapsdq;
                ath_txq_lock(sc, txq);
-       } else if (txctl->an &&
-                  ieee80211_is_data_present(hdr->frame_control)) {
+       } else if (txctl->an && queue) {
                WARN_ON(tid->ac->txq != txctl->txq);
 
                if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
@@ -2207,7 +2270,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
                TX_STAT_INC(txq->axq_qnum, a_queued_sw);
                __skb_queue_tail(&tid->buf_q, skb);
                if (!txctl->an->sleeping)
-                       ath_tx_queue_tid(txq, tid);
+                       ath_tx_queue_tid(sc, txq, tid);
 
                ath_txq_schedule(sc, txq);
                goto out;
@@ -2253,8 +2316,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        int max_duration;
 
        max_duration =
-               sc->cur_beacon_conf.beacon_interval * 1000 *
-               sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
+               sc->cur_chan->beacon.beacon_interval * 1000 *
+               sc->cur_chan->beacon.dtim_period / ATH_BCBUF;
 
        do {
                struct ath_frame_info *fi = get_frame_info(skb);
@@ -2569,6 +2632,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
                        sc->beacon.tx_processed = true;
                        sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
 
+                       ath_chanctx_event(sc, NULL,
+                                         ATH_CHANCTX_EVENT_BEACON_SENT);
                        ath9k_csa_update(sc);
                        continue;
                }
index 820d4ebd93222b6e3a7b28dd68d1d590e2b97a7b..4ac2c208c9ba3ff109e8c8942b1b9637e9b7cf0d 100644 (file)
@@ -104,8 +104,8 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
        return -EOPNOTSUPP;
 }
 
-static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
-                             struct station_info *sinfo)
+int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+                      struct station_info *sinfo)
 {
        struct wmi_notify_req_cmd cmd = {
                .cid = cid,
@@ -287,6 +287,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
                return -EBUSY;
        }
 
+       wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
        wil->scan_request = request;
        mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
 
@@ -443,15 +444,15 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
        return rc;
 }
 
-static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
-                               struct wireless_dev *wdev,
-                               struct cfg80211_mgmt_tx_params *params,
-                               u64 *cookie)
+int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        struct cfg80211_mgmt_tx_params *params,
+                        u64 *cookie)
 {
        const u8 *buf = params->buf;
        size_t len = params->len;
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
        int rc;
+       bool tx_status = false;
        struct ieee80211_mgmt *mgmt_frame = (void *)buf;
        struct wmi_sw_tx_req_cmd *cmd;
        struct {
@@ -460,8 +461,10 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
        } __packed evt;
 
        cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
-       if (!cmd)
-               return -ENOMEM;
+       if (!cmd) {
+               rc = -ENOMEM;
+               goto out;
+       }
 
        memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
        cmd->len = cpu_to_le16(len);
@@ -470,10 +473,12 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
        rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
                      WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
        if (rc == 0)
-               rc = evt.evt.status;
+               tx_status = !evt.evt.status;
 
        kfree(cmd);
-
+ out:
+       cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
+                               tx_status, GFP_KERNEL);
        return rc;
 }
 
@@ -562,6 +567,34 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
        return rc;
 }
 
+static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
+{
+       print_hex_dump_bytes("head     ", DUMP_PREFIX_OFFSET,
+                            b->head, b->head_len);
+       print_hex_dump_bytes("tail     ", DUMP_PREFIX_OFFSET,
+                            b->tail, b->tail_len);
+       print_hex_dump_bytes("BCON IE  ", DUMP_PREFIX_OFFSET,
+                            b->beacon_ies, b->beacon_ies_len);
+       print_hex_dump_bytes("PROBE    ", DUMP_PREFIX_OFFSET,
+                            b->probe_resp, b->probe_resp_len);
+       print_hex_dump_bytes("PROBE IE ", DUMP_PREFIX_OFFSET,
+                            b->proberesp_ies, b->proberesp_ies_len);
+       print_hex_dump_bytes("ASSOC IE ", DUMP_PREFIX_OFFSET,
+                            b->assocresp_ies, b->assocresp_ies_len);
+}
+
+static void wil_print_crypto(struct wil6210_priv *wil,
+                            struct cfg80211_crypto_settings *c)
+{
+       wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
+                    c->wpa_versions, c->cipher_group);
+       wil_dbg_misc(wil, "Pairwise ciphers [%d]\n", c->n_ciphers_pairwise);
+       wil_dbg_misc(wil, "AKM suites [%d]\n", c->n_akm_suites);
+       wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
+                    c->control_port, be16_to_cpu(c->control_port_ethertype),
+                    c->control_port_no_encrypt);
+}
+
 static int wil_fix_bcon(struct wil6210_priv *wil,
                        struct cfg80211_beacon_data *bcon)
 {
@@ -595,8 +628,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
        struct wireless_dev *wdev = ndev->ieee80211_ptr;
        struct ieee80211_channel *channel = info->chandef.chan;
        struct cfg80211_beacon_data *bcon = &info->beacon;
+       struct cfg80211_crypto_settings *crypto = &info->crypto;
        u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
 
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        if (!channel) {
                wil_err(wil, "AP: No channel???\n");
                return -EINVAL;
@@ -604,11 +640,19 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
 
        wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
                     channel->center_freq, info->privacy ? "secure" : "open");
+       wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
+                    info->privacy, info->auth_type);
+       wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
+                    info->dtim_period);
        print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
                             info->ssid, info->ssid_len);
+       wil_print_bcon_data(bcon);
+       wil_print_crypto(wil, crypto);
 
-       if (wil_fix_bcon(wil, bcon))
+       if (wil_fix_bcon(wil, bcon)) {
                wil_dbg_misc(wil, "Fixed bcon\n");
+               wil_print_bcon_data(bcon);
+       }
 
        mutex_lock(&wil->mutex);
 
@@ -663,6 +707,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
        int rc = 0;
        struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 
+       wil_dbg_misc(wil, "%s()\n", __func__);
+
        mutex_lock(&wil->mutex);
 
        rc = wmi_pcp_stop(wil);
index 8d4bc4bfb6643a64609b2e06c3b303fa72fc27fa..8f66186adb8c59d67103ad75ddb759063736bbaa 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/seq_file.h>
 #include <linux/pci.h>
 #include <linux/rtnetlink.h>
+#include <linux/power_supply.h>
 
 #include "wil6210.h"
 #include "txrx.h"
@@ -69,14 +70,32 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
 
        for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
                struct vring *vring = &(wil->vring_tx[i]);
+               struct vring_tx_data *txdata = &wil->vring_tx_data[i];
+
                if (vring->va) {
                        int cid = wil->vring2cid_tid[i][0];
                        int tid = wil->vring2cid_tid[i][1];
+                       u32 swhead = vring->swhead;
+                       u32 swtail = vring->swtail;
+                       int used = (vring->size + swhead - swtail)
+                                  % vring->size;
+                       int avail = vring->size - used - 1;
                        char name[10];
+                       /* performance monitoring */
+                       cycles_t now = get_cycles();
+                       cycles_t idle = txdata->idle * 100;
+                       cycles_t total = now - txdata->begin;
+
+                       do_div(idle, total);
+                       txdata->begin = now;
+                       txdata->idle = 0ULL;
+
                        snprintf(name, sizeof(name), "tx_%2d", i);
 
-                       seq_printf(s, "\n%pM CID %d TID %d\n",
-                                  wil->sta[cid].addr, cid, tid);
+                       seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d] idle %3d%%\n",
+                                  wil->sta[cid].addr, cid, tid, used, avail,
+                                  (int)idle);
+
                        wil_print_vring(s, wil, name, vring, '_', 'H');
                }
        }
@@ -231,6 +250,26 @@ static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
                                   &fops_iomem_x32);
 }
 
+static int wil_debugfs_ulong_set(void *data, u64 val)
+{
+       *(ulong *)data = val;
+       return 0;
+}
+static int wil_debugfs_ulong_get(void *data, u64 *val)
+{
+       *val = *(ulong *)data;
+       return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get,
+                       wil_debugfs_ulong_set, "%llu\n");
+
+static struct dentry *wil_debugfs_create_ulong(const char *name, umode_t mode,
+                                              struct dentry *parent,
+                                              ulong *value)
+{
+       return debugfs_create_file(name, mode, parent, value, &wil_fops_ulong);
+}
+
 static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
                                      const char *name,
                                      struct dentry *parent, u32 off)
@@ -284,11 +323,11 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
        if (IS_ERR_OR_NULL(d))
                return -ENODEV;
 
-       wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr +
+       wil_debugfs_create_iomem_x32("TRSH", S_IRUGO | S_IWUSR, d, wil->csr +
                                     HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
-       wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr +
+       wil_debugfs_create_iomem_x32("DATA", S_IRUGO | S_IWUSR, d, wil->csr +
                                     HOSTADDR(RGF_DMA_ITR_CNT_DATA));
-       wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr +
+       wil_debugfs_create_iomem_x32("CTL", S_IRUGO | S_IWUSR, d, wil->csr +
                                     HOSTADDR(RGF_DMA_ITR_CNT_CRL));
 
        return 0;
@@ -397,6 +436,126 @@ static const struct file_operations fops_reset = {
        .write = wil_write_file_reset,
        .open  = simple_open,
 };
+/*---write channel 1..4 to rxon for it, 0 to rxoff---*/
+static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
+                                  size_t len, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       int rc;
+       long channel;
+       bool on;
+
+       char *kbuf = kmalloc(len + 1, GFP_KERNEL);
+       if (!kbuf)
+               return -ENOMEM;
+       if (copy_from_user(kbuf, buf, len)) {
+               kfree(kbuf);
+               return -EIO;
+       }
+
+       kbuf[len] = '\0';
+       rc = kstrtol(kbuf, 0, &channel);
+       kfree(kbuf);
+       if (rc)
+               return rc;
+
+       if ((channel < 0) || (channel > 4)) {
+               wil_err(wil, "Invalid channel %ld\n", channel);
+               return -EINVAL;
+       }
+       on = !!channel;
+
+       if (on) {
+               rc = wmi_set_channel(wil, (int)channel);
+               if (rc)
+                       return rc;
+       }
+
+       rc = wmi_rxon(wil, on);
+       if (rc)
+               return rc;
+
+       return len;
+}
+
+static const struct file_operations fops_rxon = {
+       .write = wil_write_file_rxon,
+       .open  = simple_open,
+};
+/*---tx_mgmt---*/
+/* Write mgmt frame to this file to send it */
+static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
+                                    size_t len, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       struct wiphy *wiphy = wil_to_wiphy(wil);
+       struct wireless_dev *wdev = wil_to_wdev(wil);
+       struct cfg80211_mgmt_tx_params params;
+       int rc;
+
+       void *frame = kmalloc(len, GFP_KERNEL);
+       if (!frame)
+               return -ENOMEM;
+
+       if (copy_from_user(frame, buf, len))
+               return -EIO;
+
+       params.buf = frame;
+       params.len = len;
+       params.chan = wdev->preset_chandef.chan;
+
+       rc = wil_cfg80211_mgmt_tx(wiphy, wdev, &params, NULL);
+
+       kfree(frame);
+       wil_info(wil, "%s() -> %d\n", __func__, rc);
+
+       return len;
+}
+
+static const struct file_operations fops_txmgmt = {
+       .write = wil_write_file_txmgmt,
+       .open  = simple_open,
+};
+
+/* Write WMI command (w/o mbox header) to this file to send it
+ * WMI starts from wil6210_mbox_hdr_wmi header
+ */
+static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
+                                 size_t len, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       struct wil6210_mbox_hdr_wmi *wmi;
+       void *cmd;
+       int cmdlen = len - sizeof(struct wil6210_mbox_hdr_wmi);
+       u16 cmdid;
+       int rc, rc1;
+
+       if (cmdlen <= 0)
+               return -EINVAL;
+
+       wmi = kmalloc(len, GFP_KERNEL);
+       if (!wmi)
+               return -ENOMEM;
+
+       rc = simple_write_to_buffer(wmi, len, ppos, buf, len);
+       if (rc < 0)
+               return rc;
+
+       cmd = &wmi[1];
+       cmdid = le16_to_cpu(wmi->id);
+
+       rc1 = wmi_send(wil, cmdid, cmd, cmdlen);
+       kfree(wmi);
+
+       wil_info(wil, "%s(0x%04x[%d]) -> %d\n", __func__, cmdid, cmdlen, rc1);
+
+       return rc;
+}
+
+static const struct file_operations fops_wmi = {
+       .write = wil_write_file_wmi,
+       .open  = simple_open,
+};
 
 static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
                            const char *prefix)
@@ -600,8 +759,8 @@ static int wil_temp_debugfs_show(struct seq_file *s, void *data)
                return 0;
        }
 
-       print_temp(s, "MAC temperature   :", t_m);
-       print_temp(s, "Radio temperature :", t_r);
+       print_temp(s, "T_mac   =", t_m);
+       print_temp(s, "T_radio =", t_r);
 
        return 0;
 }
@@ -618,6 +777,130 @@ static const struct file_operations fops_temp = {
        .llseek         = seq_lseek,
 };
 
+/*---------freq------------*/
+static int wil_freq_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       struct wireless_dev *wdev = wil_to_wdev(wil);
+       u16 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0;
+
+       seq_printf(s, "Freq = %d\n", freq);
+
+       return 0;
+}
+
+static int wil_freq_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_freq_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_freq = {
+       .open           = wil_freq_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
+/*---------link------------*/
+static int wil_link_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       struct station_info sinfo;
+       int i, rc;
+
+       for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+               struct wil_sta_info *p = &wil->sta[i];
+               char *status = "unknown";
+               switch (p->status) {
+               case wil_sta_unused:
+                       status = "unused   ";
+                       break;
+               case wil_sta_conn_pending:
+                       status = "pending  ";
+                       break;
+               case wil_sta_connected:
+                       status = "connected";
+                       break;
+               }
+               seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status,
+                          (p->data_port_open ? " data_port_open" : ""));
+
+               if (p->status == wil_sta_connected) {
+                       rc = wil_cid_fill_sinfo(wil, i, &sinfo);
+                       if (rc)
+                               return rc;
+
+                       seq_printf(s, "  Tx_mcs = %d\n", sinfo.txrate.mcs);
+                       seq_printf(s, "  Rx_mcs = %d\n", sinfo.rxrate.mcs);
+                       seq_printf(s, "  SQ     = %d\n", sinfo.signal);
+               }
+       }
+
+       return 0;
+}
+
+static int wil_link_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_link_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_link = {
+       .open           = wil_link_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
+/*---------info------------*/
+static int wil_info_debugfs_show(struct seq_file *s, void *data)
+{
+       struct wil6210_priv *wil = s->private;
+       struct net_device *ndev = wil_to_ndev(wil);
+       int is_ac = power_supply_is_system_supplied();
+       int rx = atomic_xchg(&wil->isr_count_rx, 0);
+       int tx = atomic_xchg(&wil->isr_count_tx, 0);
+       static ulong rxf_old, txf_old;
+       ulong rxf = ndev->stats.rx_packets;
+       ulong txf = ndev->stats.tx_packets;
+       unsigned int i;
+
+       /* >0 : AC; 0 : battery; <0 : error */
+       seq_printf(s, "AC powered : %d\n", is_ac);
+       seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old);
+       seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old);
+       rxf_old = rxf;
+       txf_old = txf;
+
+
+#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \
+       " " __stringify(x) : ""
+
+       for (i = 0; i < ndev->num_tx_queues; i++) {
+               struct netdev_queue *txq = netdev_get_tx_queue(ndev, i);
+               unsigned long state = txq->state;
+
+               seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state,
+                          CHECK_QSTATE(DRV_XOFF),
+                          CHECK_QSTATE(STACK_XOFF),
+                          CHECK_QSTATE(FROZEN)
+                         );
+       }
+#undef CHECK_QSTATE
+       return 0;
+}
+
+static int wil_info_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, wil_info_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_info = {
+       .open           = wil_info_seq_open,
+       .release        = single_release,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+};
+
 /*---------Station matrix------------*/
 static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
 {
@@ -630,7 +913,7 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
                else
                        seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');
        }
-       seq_puts(s, "]\n");
+       seq_printf(s, "] last drop 0x%03x\n", r->ssn_last_drop);
 }
 
 static int wil_sta_debugfs_show(struct seq_file *s, void *data)
@@ -682,6 +965,26 @@ static const struct file_operations fops_sta = {
 };
 
 /*----------------*/
+static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
+                                      struct dentry *dbg)
+{
+       int i;
+       char name[32];
+
+       for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
+               struct debugfs_blob_wrapper *blob = &wil->blobs[i];
+               const struct fw_map *map = &fw_mapping[i];
+
+               if (!map->name)
+                       continue;
+
+               blob->data = (void * __force)wil->csr + HOSTADDR(map->host);
+               blob->size = map->to - map->from;
+               snprintf(name, sizeof(name), "blob_%s", map->name);
+               wil_debugfs_create_ioblob(name, S_IRUGO, dbg, blob);
+       }
+}
+
 int wil6210_debugfs_init(struct wil6210_priv *wil)
 {
        struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
@@ -703,6 +1006,10 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
        debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
        debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
                           &wil->secure_pcp);
+       wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg,
+                                &wil->status);
+       debugfs_create_u32("fw_version", S_IRUGO, dbg, &wil->fw_version);
+       debugfs_create_x32("hw_version", S_IRUGO, dbg, &wil->hw_version);
 
        wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg,
                                   HOSTADDR(RGF_USER_USER_ICR));
@@ -715,40 +1022,22 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
        wil6210_debugfs_create_pseudo_ISR(wil, dbg);
        wil6210_debugfs_create_ITR_CNT(wil, dbg);
 
+       wil_debugfs_create_iomem_x32("RGF_USER_USAGE_1", S_IRUGO, dbg,
+                                    wil->csr +
+                                    HOSTADDR(RGF_USER_USAGE_1));
        debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr);
        debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
 
        debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
+       debugfs_create_file("rxon", S_IWUSR, dbg, wil, &fops_rxon);
+       debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt);
+       debugfs_create_file("wmi_send", S_IWUSR, dbg, wil, &fops_wmi);
        debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp);
+       debugfs_create_file("freq", S_IRUGO, dbg, wil, &fops_freq);
+       debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link);
+       debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info);
 
-       wil->rgf_blob.data = (void * __force)wil->csr + 0;
-       wil->rgf_blob.size = 0xa000;
-       wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob);
-
-       wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000;
-       wil->fw_code_blob.size = 0x40000;
-       wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg,
-                                 &wil->fw_code_blob);
-
-       wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000;
-       wil->fw_data_blob.size = 0x8000;
-       wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg,
-                                 &wil->fw_data_blob);
-
-       wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000;
-       wil->fw_peri_blob.size = 0x18000;
-       wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg,
-                                 &wil->fw_peri_blob);
-
-       wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000;
-       wil->uc_code_blob.size = 0x10000;
-       wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg,
-                                 &wil->uc_code_blob);
-
-       wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000;
-       wil->uc_data_blob.size = 0x4000;
-       wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg,
-                                 &wil->uc_data_blob);
+       wil6210_debugfs_init_blobs(wil, dbg);
 
        return 0;
 }
index 73593aa3cd9813e2bd6849b969427f83f1a9a578..67f1002a03a13a88d7224a3a8abea8f234aa9ae2 100644 (file)
@@ -208,6 +208,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
 
        /* Rx IRQ will be enabled when NAPI processing finished */
 
+       atomic_inc(&wil->isr_count_rx);
        return IRQ_HANDLED;
 }
 
@@ -246,6 +247,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
 
        /* Tx IRQ will be enabled when NAPI processing finished */
 
+       atomic_inc(&wil->isr_count_tx);
        return IRQ_HANDLED;
 }
 
@@ -257,6 +259,7 @@ static void wil_notify_fw_error(struct wil6210_priv *wil)
                [1] = "EVENT=FW_ERROR",
                [2] = NULL,
        };
+       wil_err(wil, "Notify about firmware error\n");
        kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 }
 
index 11e6d9d22eae47faa34a51c813238f2acd1a1eea..3704d2a434f340b4374f26e5bde083b9830e09a3 100644 (file)
@@ -61,11 +61,24 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
 static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
 {
        uint i;
+       struct net_device *ndev = wil_to_ndev(wil);
+       struct wireless_dev *wdev = wil->wdev;
        struct wil_sta_info *sta = &wil->sta[cid];
+       wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
+                    sta->status);
 
        sta->data_port_open = false;
        if (sta->status != wil_sta_unused) {
                wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
+               switch (wdev->iftype) {
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_P2P_GO:
+                       /* AP-like interface */
+                       cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL);
+                       break;
+               default:
+                       break;
+               }
                sta->status = wil_sta_unused;
        }
 
@@ -119,11 +132,6 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
                clear_bit(wil_status_fwconnecting, &wil->status);
                break;
        default:
-               /* AP-like interface and monitor:
-                * never scan, always connected
-                */
-               if (bssid)
-                       cfg80211_del_sta(ndev, bssid, GFP_KERNEL);
                break;
        }
 }
@@ -306,8 +314,9 @@ static void wil_target_reset(struct wil6210_priv *wil)
        int delay = 0;
        u32 hw_state;
        u32 rev_id;
+       bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW);
 
-       wil_dbg_misc(wil, "Resetting...\n");
+       wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name);
 
        /* register read */
 #define R(a) ioread32(wil->csr + HOSTADDR(a))
@@ -320,35 +329,59 @@ static void wil_target_reset(struct wil6210_priv *wil)
 
        wil->hw_version = R(RGF_USER_FW_REV_ID);
        rev_id = wil->hw_version & 0xff;
+
+       /* Clear MAC link up */
+       S(RGF_HP_CTRL, BIT(15));
        /* hpal_perst_from_pad_src_n_mask */
        S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
        /* car_perst_rst_src_n_mask */
        S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
        wmb(); /* order is important here */
 
+       if (is_sparrow) {
+               W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
+               wmb(); /* order is important here */
+       }
+
        W(RGF_USER_MAC_CPU_0,  BIT(1)); /* mac_cpu_man_rst */
        W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
        wmb(); /* order is important here */
 
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
-       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
+       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000B0 : 0x00000170);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
        wmb(); /* order is important here */
 
+       if (is_sparrow) {
+               W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0);
+               wmb(); /* order is important here */
+       }
+
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
        wmb(); /* order is important here */
 
-       W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
-       if (rev_id == 1) {
-               W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
-       } else {
-               W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
+       if (is_sparrow) {
+               W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003);
+               /* reset A2 PCIE AHB */
                W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
+
+       } else {
+               W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
+               if (rev_id == 1) {
+                       /* reset A1 BOTH PCIE AHB & PCIE RGF */
+                       W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
+               } else {
+                       W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8));
+                       W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000);
+               }
+
        }
+
+       /* TODO: check order here!!! Erez code is different */
        W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
        wmb(); /* order is important here */
 
@@ -363,7 +396,8 @@ static void wil_target_reset(struct wil6210_priv *wil)
                }
        } while (hw_state != HW_MACHINE_BOOT_DONE);
 
-       if (rev_id == 2)
+       /* TODO: Erez check rev_id != 1 */
+       if (!is_sparrow && (rev_id != 1))
                W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8));
 
        C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD);
@@ -465,6 +499,7 @@ void wil_link_on(struct wil6210_priv *wil)
        wil_dbg_misc(wil, "%s()\n", __func__);
 
        netif_carrier_on(ndev);
+       wil_dbg_misc(wil, "netif_tx_wake : link on\n");
        netif_tx_wake_all_queues(ndev);
 }
 
@@ -475,6 +510,7 @@ void wil_link_off(struct wil6210_priv *wil)
        wil_dbg_misc(wil, "%s()\n", __func__);
 
        netif_tx_stop_all_queues(ndev);
+       wil_dbg_misc(wil, "netif_tx_stop : link off\n");
        netif_carrier_off(ndev);
 }
 
@@ -552,6 +588,8 @@ static int __wil_down(struct wil6210_priv *wil)
        napi_disable(&wil->napi_tx);
 
        if (wil->scan_request) {
+               wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
+                            wil->scan_request);
                del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, true);
                wil->scan_request = NULL;
index 1e2e07b9d13d9d6b2957849b4792679c77a6dcf5..d3fbfa28db62c8af2c95f824f9801b516635abff 100644 (file)
@@ -15,7 +15,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/debugfs.h>
 #include <linux/pci.h>
 #include <linux/moduleparam.h>
 
@@ -27,11 +26,22 @@ MODULE_PARM_DESC(use_msi,
                 " Use MSI interrupt: "
                 "0 - don't, 1 - (default) - single, or 3");
 
+static bool debug_fw; /* = false; */
+module_param(debug_fw, bool, S_IRUGO);
+MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug");
+
 /* Bus ops */
 static int wil_if_pcie_enable(struct wil6210_priv *wil)
 {
        struct pci_dev *pdev = wil->pdev;
        int rc;
+       /* on platforms with buggy ACPI, pdev->msi_enabled may be set to
+        * allow pci_enable_device to work. This indicates INTx was not routed
+        * and only MSI should be used
+        */
+       int msi_only = pdev->msi_enabled;
+
+       pdev->msi_enabled = 0;
 
        pci_set_master(pdev);
 
@@ -63,6 +73,12 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
 
        wil->n_msi = use_msi;
 
+       if ((wil->n_msi == 0) && msi_only) {
+               wil_err(wil, "Interrupt pin not routed, unable to use INTx\n");
+               rc = -ENODEV;
+               goto stop_master;
+       }
+
        rc = wil6210_init_irq(wil, pdev->irq);
        if (rc)
                goto stop_master;
@@ -71,6 +87,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
        mutex_lock(&wil->mutex);
        rc = wil_reset(wil);
        mutex_unlock(&wil->mutex);
+       if (debug_fw)
+               rc = 0;
        if (rc)
                goto release_irq;
 
@@ -104,10 +122,12 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        struct wil6210_priv *wil;
        struct device *dev = &pdev->dev;
        void __iomem *csr;
+       struct wil_board *board = (struct wil_board *)id->driver_data;
        int rc;
 
        /* check HW */
-       dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n",
+       dev_info(&pdev->dev, WIL_NAME
+                " \"%s\" device found [%04x:%04x] (rev %x)\n", board->name,
                 (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
 
        if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
@@ -119,9 +139,16 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        rc = pci_enable_device(pdev);
        if (rc) {
-               dev_err(&pdev->dev, "pci_enable_device failed\n");
-               return -ENODEV;
+               dev_err(&pdev->dev,
+                       "pci_enable_device failed, retry with MSI only\n");
+               /* Work around for platforms that can't allocate IRQ:
+                * retry with MSI only
+                */
+               pdev->msi_enabled = 1;
+               rc = pci_enable_device(pdev);
        }
+       if (rc)
+               return -ENODEV;
        /* rollback to err_disable_pdev */
 
        rc = pci_request_region(pdev, 0, WIL_NAME);
@@ -150,6 +177,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        pci_set_drvdata(pdev, wil);
        wil->pdev = pdev;
+       wil->board = board;
 
        wil6210_clear_irq(wil);
        /* FW should raise IRQ when ready */
@@ -200,8 +228,21 @@ static void wil_pcie_remove(struct pci_dev *pdev)
        pci_disable_device(pdev);
 }
 
-static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = {
-       { PCI_DEVICE(0x1ae9, 0x0301) },
+static const struct wil_board wil_board_marlon = {
+       .board = WIL_BOARD_MARLON,
+       .name = "marlon",
+};
+
+static const struct wil_board wil_board_sparrow = {
+       .board = WIL_BOARD_SPARROW,
+       .name = "sparrow",
+};
+
+static const struct pci_device_id wil6210_pcie_ids[] = {
+       { PCI_DEVICE(0x1ae9, 0x0301),
+         .driver_data = (kernel_ulong_t)&wil_board_marlon },
+       { PCI_DEVICE(0x1ae9, 0x0310),
+         .driver_data = (kernel_ulong_t)&wil_board_sparrow },
        { /* end: all zeroes */ },
 };
 MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
index 747ae127587763d0bbdd652f674749960cadb1f8..180ca4793904a37d7723198f5652b02e95142430 100644 (file)
@@ -116,6 +116,7 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
 
        /* frame with out of date sequence number */
        if (seq_less(seq, r->head_seq_num)) {
+               r->ssn_last_drop = seq;
                dev_kfree_skb(skb);
                goto out;
        }
index 0784ef3d4ce2795b68091cef9353e47d7712239b..d3467943d39db60972bae0ce0413b4e5eaa04585 100644 (file)
@@ -525,6 +525,17 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
                ndev->stats.rx_bytes += len;
                stats->rx_bytes += len;
        }
+       {
+               static const char * const gro_res_str[] = {
+                       [GRO_MERGED]            = "GRO_MERGED",
+                       [GRO_MERGED_FREE]       = "GRO_MERGED_FREE",
+                       [GRO_HELD]              = "GRO_HELD",
+                       [GRO_NORMAL]            = "GRO_NORMAL",
+                       [GRO_DROP]              = "GRO_DROP",
+               };
+               wil_dbg_txrx(wil, "Rx complete %d bytes => %s,\n",
+                            len, gro_res_str[rc]);
+       }
 }
 
 /**
@@ -760,7 +771,7 @@ static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
                goto found;
        }
 
-       wil_err(wil, "Tx while no vrings active?\n");
+       wil_dbg_txrx(wil, "Tx while no vrings active?\n");
 
        return NULL;
 
@@ -881,6 +892,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        int nr_frags = skb_shinfo(skb)->nr_frags;
        uint f = 0;
        int vring_index = vring - wil->vring_tx;
+       struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index];
        uint i = swhead;
        dma_addr_t pa;
 
@@ -953,6 +965,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
        wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
                          (const void *)d, sizeof(*d), false);
 
+       if (wil_vring_is_empty(vring)) /* performance monitoring */
+               txdata->idle += get_cycles() - txdata->last_idle;
+
        /* advance swhead */
        wil_vring_advance_head(vring, nr_frags + 1);
        wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
@@ -1016,15 +1031,17 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
                vring = wil_tx_bcast(wil, skb);
        }
        if (!vring) {
-               wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest);
+               wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
                goto drop;
        }
        /* set up vring entry */
        rc = wil_tx_vring(wil, vring, skb);
 
        /* do we still have enough room in the vring? */
-       if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))
+       if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) {
                netif_tx_stop_all_queues(wil_to_ndev(wil));
+               wil_dbg_txrx(wil, "netif_tx_stop : ring full\n");
+       }
 
        switch (rc) {
        case 0:
@@ -1091,8 +1108,10 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                while (vring->swtail != new_swtail) {
                        struct vring_tx_desc dd, *d = &dd;
                        u16 dmalen;
-                       struct wil_ctx *ctx = &vring->ctx[vring->swtail];
-                       struct sk_buff *skb = ctx->skb;
+                       struct sk_buff *skb;
+
+                       ctx = &vring->ctx[vring->swtail];
+                       skb = ctx->skb;
                        _d = &vring->va[vring->swtail].tx;
 
                        *d = *_d;
@@ -1132,8 +1151,16 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
                        done++;
                }
        }
-       if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring))
+
+       if (wil_vring_is_empty(vring)) { /* performance monitoring */
+               wil_dbg_txrx(wil, "Ring[%2d] empty\n", ringid);
+               txdata->last_idle = get_cycles();
+       }
+
+       if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) {
+               wil_dbg_txrx(wil, "netif_tx_wake : ring not full\n");
                netif_tx_wake_all_queues(wil_to_ndev(wil));
+       }
 
        return done;
 }
index e25edc52398fed91bce54cb0b3fb723a9d1ff443..67e9624f7111b8842492d81d07665c5ec0973705 100644 (file)
 #include <linux/netdevice.h>
 #include <linux/wireless.h>
 #include <net/cfg80211.h>
+#include <linux/timex.h>
 
 #define WIL_NAME "wil6210"
 
+struct wil_board {
+       int board;
+#define WIL_BOARD_MARLON       (1)
+#define WIL_BOARD_SPARROW      (2)
+       const char * const name;
+};
+
 /**
  * extract bits [@b0:@b1] (inclusive) from the value @x
  * it should be @b0 <= @b1, or result is incorrect
@@ -77,6 +85,7 @@ struct RGF_ICR {
 } __packed;
 
 /* registers - FW addresses */
+#define RGF_USER_USAGE_1               (0x880004)
 #define RGF_USER_HW_MACHINE_STATE      (0x8801dc)
        #define HW_MACHINE_BOOT_DONE    (0x3fffffd)
 #define RGF_USER_USER_CPU_0            (0x8801e0)
@@ -92,6 +101,7 @@ struct RGF_ICR {
 #define RGF_USER_CLKS_CTL_SW_RST_MASK_0        (0x880b14)
 #define RGF_USER_USER_ICR              (0x880b4c) /* struct RGF_ICR */
        #define BIT_USER_USER_ICR_SW_INT_2      BIT(18)
+#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0     (0x880c18)
 
 #define RGF_DMA_EP_TX_ICR              (0x881bb4) /* struct RGF_ICR */
        #define BIT_DMA_EP_TX_ICR_TX_DONE       BIT(0)
@@ -120,6 +130,7 @@ struct RGF_ICR {
        #define BIT_DMA_PSEUDO_CAUSE_TX         BIT(1)
        #define BIT_DMA_PSEUDO_CAUSE_MISC       BIT(2)
 
+#define RGF_HP_CTRL                    (0x88265c)
 #define RGF_PCIE_LOS_COUNTER_CTL       (0x882dc4)
 
 /* popular locations */
@@ -134,6 +145,14 @@ struct RGF_ICR {
 #define ISR_MISC_FW_ERROR      BIT_DMA_EP_MISC_ICR_FW_INT(3)
 
 /* Hardware definitions end */
+struct fw_map {
+       u32 from; /* linker address - from, inclusive */
+       u32 to;   /* linker address - to, exclusive */
+       u32 host; /* PCI/Host address - BAR0 + 0x880000 */
+       const char *name; /* for debugfs */
+};
+/* array size should be in sync with actual definition in the wmi.c */
+extern const struct fw_map fw_mapping[7];
 
 /**
  * mk_cidxtid - construct @cidxtid field
@@ -251,7 +270,7 @@ struct vring {
  */
 struct vring_tx_data {
        int enabled;
-
+       cycles_t idle, last_idle, begin;
 };
 
 enum { /* for wil6210_priv.status */
@@ -303,6 +322,7 @@ struct wil_tid_ampdu_rx {
        u16 ssn;
        u16 buf_size;
        u16 timeout;
+       u16 ssn_last_drop;
        u8 dialog_token;
        bool first_time; /* is it 1-st time this buffer used? */
 };
@@ -363,6 +383,7 @@ struct wil6210_priv {
        ulong status;
        u32 fw_version;
        u32 hw_version;
+       struct wil_board *board;
        u8 n_mids; /* number of additional MIDs as reported by FW */
        int recovery_count; /* num of FW recovery attempts in a short time */
        unsigned long last_fw_recovery; /* jiffies of last fw recovery */
@@ -410,14 +431,10 @@ struct wil6210_priv {
        struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
        /* statistics */
        struct wil6210_stats stats;
+       atomic_t isr_count_rx, isr_count_tx;
        /* debugfs */
        struct dentry *debug;
-       struct debugfs_blob_wrapper fw_code_blob;
-       struct debugfs_blob_wrapper fw_data_blob;
-       struct debugfs_blob_wrapper fw_peri_blob;
-       struct debugfs_blob_wrapper uc_code_blob;
-       struct debugfs_blob_wrapper uc_data_blob;
-       struct debugfs_blob_wrapper rgf_blob;
+       struct debugfs_blob_wrapper blobs[ARRAY_SIZE(fw_mapping)];
 };
 
 #define wil_to_wiphy(i) (i->wdev->wiphy)
@@ -504,9 +521,14 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq);
 void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
 void wil6210_disable_irq(struct wil6210_priv *wil);
 void wil6210_enable_irq(struct wil6210_priv *wil);
+int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                        struct cfg80211_mgmt_tx_params *params,
+                        u64 *cookie);
 
 int wil6210_debugfs_init(struct wil6210_priv *wil);
 void wil6210_debugfs_remove(struct wil6210_priv *wil);
+int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+                      struct station_info *sinfo);
 
 struct wireless_dev *wil_cfg80211_init(struct device *dev);
 void wil_wdev_free(struct wil6210_priv *wil);
index 6cc0e182cc703c1f0c8ad243a008f8f6129590a6..1d1d0afdd2e195c6856372299e3fc635b213df23 100644 (file)
 
 /**
  * @fw_mapping provides memory remapping table
+ *
+ * array size should be in sync with the declaration in the wil6210.h
  */
-static const struct {
-       u32 from; /* linker address - from, inclusive */
-       u32 to;   /* linker address - to, exclusive */
-       u32 host; /* PCI/Host address - BAR0 + 0x880000 */
-} fw_mapping[] = {
-       {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */
-       {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
-       {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
-       {0x880000, 0x88a000, 0x880000}, /* various RGF */
-       {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
+const struct fw_map fw_mapping[] = {
+       {0x000000, 0x040000, 0x8c0000, "fw_code"}, /* FW code RAM      256k */
+       {0x800000, 0x808000, 0x900000, "fw_data"}, /* FW data RAM       32k */
+       {0x840000, 0x860000, 0x908000, "fw_peri"}, /* periph. data RAM 128k */
+       {0x880000, 0x88a000, 0x880000, "rgf"},     /* various RGF       40k */
+       {0x88a000, 0x88b000, 0x88a000, "AGC_tbl"}, /* AGC table          4k */
+       {0x88b000, 0x88c000, 0x88b000, "rgf_ext"}, /* Pcie_ext_rgf       4k */
+       {0x8c0000, 0x949000, 0x8c0000, "upper"},   /* upper area       548k */
        /*
         * 920000..930000 ucode code RAM
         * 930000..932000 ucode data RAM
@@ -327,6 +327,17 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
 
        if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
                struct cfg80211_bss *bss;
+               u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
+               u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
+               u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
+               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, "TSF : 0x%016llx\n", tsf);
+               wil_dbg_wmi(wil, "Beacon interval : %d\n", bi);
+               wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf,
+                                ie_len, true);
 
                bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
                                                d_len, signal, GFP_KERNEL);
@@ -351,6 +362,9 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
                bool aborted = (data->status != WMI_SCAN_SUCCESS);
 
                wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+               wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n",
+                            wil->scan_request, aborted);
+
                del_timer_sync(&wil->scan_timer);
                cfg80211_scan_done(wil->scan_request, aborted);
                wil->scan_request = NULL;
@@ -668,14 +682,12 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
 
        for (n = 0;; n++) {
                u16 len;
+               bool q;
 
                r->head = ioread32(wil->csr + HOST_MBOX +
                                   offsetof(struct wil6210_mbox_ctl, rx.head));
-               if (r->tail == r->head) {
-                       if (n == 0)
-                               wil_dbg_wmi(wil, "No events?\n");
-                       return;
-               }
+               if (r->tail == r->head)
+                       break;
 
                wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n",
                            r->head, r->tail);
@@ -684,14 +696,14 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                                     sizeof(struct wil6210_mbox_ring_desc));
                if (d_tail.sync == 0) {
                        wil_err(wil, "Mbox evt not owned by FW?\n");
-                       return;
+                       break;
                }
 
                /* read cmd header from descriptor */
                if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
                        wil_err(wil, "Mbox evt at 0x%08x?\n",
                                le32_to_cpu(d_tail.addr));
-                       return;
+                       break;
                }
                len = le16_to_cpu(hdr.len);
                wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
@@ -705,7 +717,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                                             event.wmi) + len, 4),
                              GFP_KERNEL);
                if (!evt)
-                       return;
+                       break;
 
                evt->event.hdr = hdr;
                cmd = (void *)&evt->event.wmi;
@@ -737,14 +749,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
                spin_lock_irqsave(&wil->wmi_ev_lock, flags);
                list_add_tail(&evt->list, &wil->pending_wmi_ev);
                spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
-               {
-                       int q = queue_work(wil->wmi_wq,
-                                          &wil->wmi_event_worker);
-                       wil_dbg_wmi(wil, "queue_work -> %d\n", q);
-               }
+               q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
+               wil_dbg_wmi(wil, "queue_work -> %d\n", q);
        }
-       if (n > 1)
-               wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n);
+       /* normally, 1 event per IRQ should be processed */
+       wil_dbg_wmi(wil, "%s -> %d events queued\n", __func__, n);
 }
 
 int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
index 40fd9b7b14269eb52d2440b09b1d6c6bac17f946..d4c6ae3a92107c86c2417fd064fe09af04310f0f 100644 (file)
@@ -122,6 +122,15 @@ config B43_PIO
        select SSB_BLOCKIO
        default y
 
+config B43_PHY_G
+       bool "Support for G-PHY (802.11g) devices"
+       depends on B43 && B43_SSB
+       default y
+       ---help---
+         This PHY type can be found in the following chipsets:
+         PCI: BCM4306, BCM4311, BCM4318
+         SoC: BCM4712, BCM5352E
+
 config B43_PHY_N
        bool "Support for 802.11n (N-PHY) devices"
        depends on B43
index 098fe9ee7096958a73093c2cbe18d7b37216e556..6e00b8804ada1f2a1274a5af33aef5540aa85ecc 100644 (file)
@@ -1,13 +1,11 @@
 b43-y                          += main.o
 b43-y                          += bus.o
-b43-y                          += tables.o
+b43-$(CONFIG_B43_PHY_G)                += phy_a.o phy_g.o tables.o lo.o wa.o
 b43-$(CONFIG_B43_PHY_N)                += tables_nphy.o
 b43-$(CONFIG_B43_PHY_N)                += radio_2055.o
 b43-$(CONFIG_B43_PHY_N)                += radio_2056.o
 b43-$(CONFIG_B43_PHY_N)                += radio_2057.o
 b43-y                          += phy_common.o
-b43-y                          += phy_g.o
-b43-y                          += phy_a.o
 b43-$(CONFIG_B43_PHY_N)                += phy_n.o
 b43-$(CONFIG_B43_PHY_LP)       += phy_lp.o
 b43-$(CONFIG_B43_PHY_LP)       += tables_lpphy.o
@@ -17,8 +15,6 @@ b43-$(CONFIG_B43_PHY_HT)      += radio_2059.o
 b43-$(CONFIG_B43_PHY_LCN)      += phy_lcn.o tables_phy_lcn.o
 b43-y                          += sysfs.o
 b43-y                          += xmit.o
-b43-y                          += lo.o
-b43-y                          += wa.o
 b43-y                          += dma.o
 b43-y                          += pio.o
 b43-y                          += rfkill.o
index 0d6a0bb1f876c3089c13448921b34339da3b8b67..d7055febe1195406e4a6f8823e64ec7bba2bfb76 100644 (file)
@@ -122,7 +122,11 @@ static const struct bcma_device_id b43_bcma_tbl[] = {
        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS),
        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS),
        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS),
+       BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS),
        BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS),
+       BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1E, BCMA_ANY_CLASS),
+       BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x28, BCMA_ANY_CLASS),
+       BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x2A, BCMA_ANY_CLASS),
        BCMA_CORETABLE_END
 };
 MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl);
@@ -206,6 +210,9 @@ static struct ieee80211_channel b43_2ghz_chantable[] = {
        CHAN2G(13, 2472, 0),
        CHAN2G(14, 2484, 0),
 };
+
+/* No support for the last 3 channels (12, 13, 14) */
+#define b43_2ghz_chantable_limited_size                11
 #undef CHAN2G
 
 #define CHAN4G(_channel, _flags) {                             \
@@ -283,6 +290,14 @@ static struct ieee80211_channel b43_5ghz_nphy_chantable[] = {
        CHAN5G(182, 0),
 };
 
+static struct ieee80211_channel b43_5ghz_nphy_chantable_limited[] = {
+       CHAN5G(36, 0),          CHAN5G(40, 0),
+       CHAN5G(44, 0),          CHAN5G(48, 0),
+       CHAN5G(149, 0),         CHAN5G(153, 0),
+       CHAN5G(157, 0),         CHAN5G(161, 0),
+       CHAN5G(165, 0),
+};
+
 static struct ieee80211_channel b43_5ghz_aphy_chantable[] = {
        CHAN5G(34, 0),          CHAN5G(36, 0),
        CHAN5G(38, 0),          CHAN5G(40, 0),
@@ -315,6 +330,14 @@ static struct ieee80211_supported_band b43_band_5GHz_nphy = {
        .n_bitrates     = b43_a_ratetable_size,
 };
 
+static struct ieee80211_supported_band b43_band_5GHz_nphy_limited = {
+       .band           = IEEE80211_BAND_5GHZ,
+       .channels       = b43_5ghz_nphy_chantable_limited,
+       .n_channels     = ARRAY_SIZE(b43_5ghz_nphy_chantable_limited),
+       .bitrates       = b43_a_ratetable,
+       .n_bitrates     = b43_a_ratetable_size,
+};
+
 static struct ieee80211_supported_band b43_band_5GHz_aphy = {
        .band           = IEEE80211_BAND_5GHZ,
        .channels       = b43_5ghz_aphy_chantable,
@@ -331,6 +354,14 @@ static struct ieee80211_supported_band b43_band_2GHz = {
        .n_bitrates     = b43_g_ratetable_size,
 };
 
+static struct ieee80211_supported_band b43_band_2ghz_limited = {
+       .band           = IEEE80211_BAND_2GHZ,
+       .channels       = b43_2ghz_chantable,
+       .n_channels     = b43_2ghz_chantable_limited_size,
+       .bitrates       = b43_g_ratetable,
+       .n_bitrates     = b43_g_ratetable_size,
+};
+
 static void b43_wireless_core_exit(struct b43_wldev *dev);
 static int b43_wireless_core_init(struct b43_wldev *dev);
 static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev);
@@ -2201,52 +2232,82 @@ err_format:
        return -EPROTO;
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/Init/Firmware */
 static int b43_try_request_fw(struct b43_request_fw_context *ctx)
 {
        struct b43_wldev *dev = ctx->dev;
        struct b43_firmware *fw = &ctx->dev->fw;
+       struct b43_phy *phy = &dev->phy;
        const u8 rev = ctx->dev->dev->core_rev;
        const char *filename;
-       u32 tmshigh;
        int err;
 
-       /* Files for HT and LCN were found by trying one by one */
-
        /* Get microcode */
-       if ((rev >= 5) && (rev <= 10)) {
-               filename = "ucode5";
-       } else if ((rev >= 11) && (rev <= 12)) {
-               filename = "ucode11";
-       } else if (rev == 13) {
-               filename = "ucode13";
-       } else if (rev == 14) {
-               filename = "ucode14";
-       } else if (rev == 15) {
+       filename = NULL;
+       switch (rev) {
+       case 42:
+               if (phy->type == B43_PHYTYPE_AC)
+                       filename = "ucode42";
+               break;
+       case 40:
+               if (phy->type == B43_PHYTYPE_AC)
+                       filename = "ucode40";
+               break;
+       case 33:
+               if (phy->type == B43_PHYTYPE_LCN40)
+                       filename = "ucode33_lcn40";
+               break;
+       case 30:
+               if (phy->type == B43_PHYTYPE_N)
+                       filename = "ucode30_mimo";
+               break;
+       case 29:
+               if (phy->type == B43_PHYTYPE_HT)
+                       filename = "ucode29_mimo";
+               break;
+       case 26:
+               if (phy->type == B43_PHYTYPE_HT)
+                       filename = "ucode26_mimo";
+               break;
+       case 28:
+       case 25:
+               if (phy->type == B43_PHYTYPE_N)
+                       filename = "ucode25_mimo";
+               else if (phy->type == B43_PHYTYPE_LCN)
+                       filename = "ucode25_lcn";
+               break;
+       case 24:
+               if (phy->type == B43_PHYTYPE_LCN)
+                       filename = "ucode24_lcn";
+               break;
+       case 23:
+               if (phy->type == B43_PHYTYPE_N)
+                       filename = "ucode16_mimo";
+               break;
+       case 16 ... 19:
+               if (phy->type == B43_PHYTYPE_N)
+                       filename = "ucode16_mimo";
+               else if (phy->type == B43_PHYTYPE_LP)
+                       filename = "ucode16_lp";
+               break;
+       case 15:
                filename = "ucode15";
-       } else {
-               switch (dev->phy.type) {
-               case B43_PHYTYPE_N:
-                       if (rev >= 16)
-                               filename = "ucode16_mimo";
-                       else
-                               goto err_no_ucode;
-                       break;
-               case B43_PHYTYPE_HT:
-                       if (rev == 29)
-                               filename = "ucode29_mimo";
-                       else
-                               goto err_no_ucode;
-                       break;
-               case B43_PHYTYPE_LCN:
-                       if (rev == 24)
-                               filename = "ucode24_mimo";
-                       else
-                               goto err_no_ucode;
-                       break;
-               default:
-                       goto err_no_ucode;
-               }
+               break;
+       case 14:
+               filename = "ucode14";
+               break;
+       case 13:
+               filename = "ucode13";
+               break;
+       case 11 ... 12:
+               filename = "ucode11";
+               break;
+       case 5 ... 10:
+               filename = "ucode5";
+               break;
        }
+       if (!filename)
+               goto err_no_ucode;
        err = b43_do_request_fw(ctx, filename, &fw->ucode, true);
        if (err)
                goto err_load;
@@ -2268,117 +2329,121 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
                goto err_load;
 
        /* Get initvals */
+       filename = NULL;
        switch (dev->phy.type) {
-       case B43_PHYTYPE_A:
-               if ((rev >= 5) && (rev <= 10)) {
-                       tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
-                       if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
-                               filename = "a0g1initvals5";
-                       else
-                               filename = "a0g0initvals5";
-               } else
-                       goto err_no_initvals;
-               break;
        case B43_PHYTYPE_G:
-               if ((rev >= 5) && (rev <= 10))
-                       filename = "b0g0initvals5";
-               else if (rev >= 13)
+               if (rev == 13)
                        filename = "b0g0initvals13";
-               else
-                       goto err_no_initvals;
+               else if (rev >= 5 && rev <= 10)
+                       filename = "b0g0initvals5";
                break;
        case B43_PHYTYPE_N:
-               if (rev >= 16)
+               if (rev == 30)
+                       filename = "n16initvals30";
+               else if (rev == 28 || rev == 25)
+                       filename = "n0initvals25";
+               else if (rev == 24)
+                       filename = "n0initvals24";
+               else if (rev == 23)
+                       filename = "n0initvals16"; /* What about n0initvals22? */
+               else if (rev >= 16 && rev <= 18)
                        filename = "n0initvals16";
-               else if ((rev >= 11) && (rev <= 12))
+               else if (rev >= 11 && rev <= 12)
                        filename = "n0initvals11";
-               else
-                       goto err_no_initvals;
                break;
        case B43_PHYTYPE_LP:
-               if (rev == 13)
-                       filename = "lp0initvals13";
+               if (rev >= 16 && rev <= 18)
+                       filename = "lp0initvals16";
+               else if (rev == 15)
+                       filename = "lp0initvals15";
                else if (rev == 14)
                        filename = "lp0initvals14";
-               else if (rev >= 15)
-                       filename = "lp0initvals15";
-               else
-                       goto err_no_initvals;
+               else if (rev == 13)
+                       filename = "lp0initvals13";
                break;
        case B43_PHYTYPE_HT:
                if (rev == 29)
                        filename = "ht0initvals29";
-               else
-                       goto err_no_initvals;
+               else if (rev == 26)
+                       filename = "ht0initvals26";
                break;
        case B43_PHYTYPE_LCN:
                if (rev == 24)
                        filename = "lcn0initvals24";
-               else
-                       goto err_no_initvals;
                break;
-       default:
-               goto err_no_initvals;
+       case B43_PHYTYPE_LCN40:
+               if (rev == 33)
+                       filename = "lcn400initvals33";
+               break;
+       case B43_PHYTYPE_AC:
+               if (rev == 42)
+                       filename = "ac1initvals42";
+               else if (rev == 40)
+                       filename = "ac0initvals40";
+               break;
        }
+       if (!filename)
+               goto err_no_initvals;
        err = b43_do_request_fw(ctx, filename, &fw->initvals, false);
        if (err)
                goto err_load;
 
        /* Get bandswitch initvals */
+       filename = NULL;
        switch (dev->phy.type) {
-       case B43_PHYTYPE_A:
-               if ((rev >= 5) && (rev <= 10)) {
-                       tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
-                       if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
-                               filename = "a0g1bsinitvals5";
-                       else
-                               filename = "a0g0bsinitvals5";
-               } else if (rev >= 11)
-                       filename = NULL;
-               else
-                       goto err_no_initvals;
-               break;
        case B43_PHYTYPE_G:
-               if ((rev >= 5) && (rev <= 10))
+               if (rev == 13)
+                       filename = "b0g0bsinitvals13";
+               else if (rev >= 5 && rev <= 10)
                        filename = "b0g0bsinitvals5";
-               else if (rev >= 11)
-                       filename = NULL;
-               else
-                       goto err_no_initvals;
                break;
        case B43_PHYTYPE_N:
-               if (rev >= 16)
+               if (rev == 30)
+                       filename = "n16bsinitvals30";
+               else if (rev == 28 || rev == 25)
+                       filename = "n0bsinitvals25";
+               else if (rev == 24)
+                       filename = "n0bsinitvals24";
+               else if (rev == 23)
+                       filename = "n0bsinitvals16"; /* What about n0bsinitvals22? */
+               else if (rev >= 16 && rev <= 18)
                        filename = "n0bsinitvals16";
-               else if ((rev >= 11) && (rev <= 12))
+               else if (rev >= 11 && rev <= 12)
                        filename = "n0bsinitvals11";
-               else
-                       goto err_no_initvals;
                break;
        case B43_PHYTYPE_LP:
-               if (rev == 13)
-                       filename = "lp0bsinitvals13";
+               if (rev >= 16 && rev <= 18)
+                       filename = "lp0bsinitvals16";
+               else if (rev == 15)
+                       filename = "lp0bsinitvals15";
                else if (rev == 14)
                        filename = "lp0bsinitvals14";
-               else if (rev >= 15)
-                       filename = "lp0bsinitvals15";
-               else
-                       goto err_no_initvals;
+               else if (rev == 13)
+                       filename = "lp0bsinitvals13";
                break;
        case B43_PHYTYPE_HT:
                if (rev == 29)
                        filename = "ht0bsinitvals29";
-               else
-                       goto err_no_initvals;
+               else if (rev == 26)
+                       filename = "ht0bsinitvals26";
                break;
        case B43_PHYTYPE_LCN:
                if (rev == 24)
                        filename = "lcn0bsinitvals24";
-               else
-                       goto err_no_initvals;
                break;
-       default:
-               goto err_no_initvals;
+       case B43_PHYTYPE_LCN40:
+               if (rev == 33)
+                       filename = "lcn400bsinitvals33";
+               break;
+       case B43_PHYTYPE_AC:
+               if (rev == 42)
+                       filename = "ac1bsinitvals42";
+               else if (rev == 40)
+                       filename = "ac0bsinitvals40";
+               break;
        }
+       if (!filename)
+               goto err_no_initvals;
        err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false);
        if (err)
                goto err_load;
@@ -2915,6 +2980,45 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on)
        }
 }
 
+/* brcms_b_switch_macfreq */
+void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode)
+{
+       u16 chip_id = dev->dev->chip_id;
+
+       if (chip_id == BCMA_CHIP_ID_BCM43217 ||
+           chip_id == BCMA_CHIP_ID_BCM43222 ||
+           chip_id == BCMA_CHIP_ID_BCM43224 ||
+           chip_id == BCMA_CHIP_ID_BCM43225 ||
+           chip_id == BCMA_CHIP_ID_BCM43227 ||
+           chip_id == BCMA_CHIP_ID_BCM43228) {
+               switch (spurmode) {
+               case 2: /* 126 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+                       break;
+               case 1: /* 123 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+                       break;
+               default: /* 120 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+                       break;
+               }
+       } else if (dev->phy.type == B43_PHYTYPE_LCN) {
+               switch (spurmode) {
+               case 1: /* 82 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
+                       break;
+               default: /* 80 Mhz */
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD);
+                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
+                       break;
+               }
+       }
+}
+
 static void b43_adjust_opmode(struct b43_wldev *dev)
 {
        struct b43_wl *wl = dev->wl;
@@ -3798,38 +3902,29 @@ static void b43_set_retry_limits(struct b43_wldev *dev,
 static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct b43_wl *wl = hw_to_b43_wl(hw);
-       struct b43_wldev *dev;
-       struct b43_phy *phy;
+       struct b43_wldev *dev = wl->current_dev;
+       struct b43_phy *phy = &dev->phy;
        struct ieee80211_conf *conf = &hw->conf;
        int antenna;
        int err = 0;
-       bool reload_bss = false;
 
        mutex_lock(&wl->mutex);
-
-       dev = wl->current_dev;
-
        b43_mac_suspend(dev);
 
-       /* Switch the band (if necessary). This might change the active core. */
-       err = b43_switch_band(dev, conf->chandef.chan);
-       if (err)
-               goto out_unlock_mutex;
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               phy->chandef = &conf->chandef;
+               phy->channel = conf->chandef.chan->hw_value;
 
-       /* Need to reload all settings if the core changed */
-       if (dev != wl->current_dev) {
-               dev = wl->current_dev;
-               changed = ~0;
-               reload_bss = true;
-       }
-
-       phy = &dev->phy;
+               /* Switch the band (if necessary). */
+               err = b43_switch_band(dev, conf->chandef.chan);
+               if (err)
+                       goto out_mac_enable;
 
-       if (conf_is_ht(conf))
-               phy->is_40mhz =
-                       (conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf));
-       else
-               phy->is_40mhz = false;
+               /* Switch to the requested channel.
+                * The firmware takes care of races with the TX handler.
+                */
+               b43_switch_channel(dev, phy->channel);
+       }
 
        if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
                b43_set_retry_limits(dev, conf->short_frame_max_tx_count,
@@ -3838,11 +3933,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
        if (!changed)
                goto out_mac_enable;
 
-       /* Switch to the requested channel.
-        * The firmware takes care of races with the TX handler. */
-       if (conf->chandef.chan->hw_value != phy->channel)
-               b43_switch_channel(dev, conf->chandef.chan->hw_value);
-
        dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR);
 
        /* Adjust the desired TX power level. */
@@ -3878,12 +3968,8 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
 
 out_mac_enable:
        b43_mac_enable(dev);
-out_unlock_mutex:
        mutex_unlock(&wl->mutex);
 
-       if (wl->vif && reload_bss)
-               b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0);
-
        return err;
 }
 
@@ -4309,13 +4395,15 @@ static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type)
 static int b43_phy_versioning(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
+       const u8 core_rev = dev->dev->core_rev;
        u32 tmp;
        u8 analog_type;
        u8 phy_type;
        u8 phy_rev;
        u16 radio_manuf;
-       u16 radio_ver;
+       u16 radio_id;
        u16 radio_rev;
+       u8 radio_ver;
        int unsupported = 0;
 
        /* Get PHY versioning */
@@ -4323,23 +4411,23 @@ static int b43_phy_versioning(struct b43_wldev *dev)
        analog_type = (tmp & B43_PHYVER_ANALOG) >> B43_PHYVER_ANALOG_SHIFT;
        phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT;
        phy_rev = (tmp & B43_PHYVER_VERSION);
+
+       /* LCNXN is continuation of N which run out of revisions */
+       if (phy_type == B43_PHYTYPE_LCNXN) {
+               phy_type = B43_PHYTYPE_N;
+               phy_rev += 16;
+       }
+
        switch (phy_type) {
-       case B43_PHYTYPE_A:
-               if (phy_rev >= 4)
-                       unsupported = 1;
-               break;
-       case B43_PHYTYPE_B:
-               if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6
-                   && phy_rev != 7)
-                       unsupported = 1;
-               break;
+#ifdef CONFIG_B43_PHY_G
        case B43_PHYTYPE_G:
                if (phy_rev > 9)
                        unsupported = 1;
                break;
+#endif
 #ifdef CONFIG_B43_PHY_N
        case B43_PHYTYPE_N:
-               if (phy_rev > 9)
+               if (phy_rev >= 19)
                        unsupported = 1;
                break;
 #endif
@@ -4374,7 +4462,17 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev);
 
        /* Get RADIO versioning */
-       if (dev->dev->core_rev >= 24) {
+       if (core_rev == 40 || core_rev == 42) {
+               radio_manuf = 0x17F;
+
+               b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 0);
+               radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+
+               b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1);
+               radio_id = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+
+               radio_ver = 0; /* Is there version somewhere? */
+       } else if (core_rev >= 24) {
                u16 radio24[3];
 
                for (tmp = 0; tmp < 3; tmp++) {
@@ -4382,12 +4480,10 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                        radio24[tmp] = b43_read16(dev, B43_MMIO_RADIO24_DATA);
                }
 
-               /* Broadcom uses "id" for our "ver" and has separated "ver" */
-               /* radio_ver = (radio24[0] & 0xF0) >> 4; */
-
                radio_manuf = 0x17F;
-               radio_ver = (radio24[2] << 8) | radio24[1];
+               radio_id = (radio24[2] << 8) | radio24[1];
                radio_rev = (radio24[0] & 0xF);
+               radio_ver = (radio24[0] & 0xF0) >> 4;
        } else {
                if (dev->dev->chip_id == 0x4317) {
                        if (dev->dev->chip_rev == 0)
@@ -4406,15 +4502,16 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                                << 16;
                }
                radio_manuf = (tmp & 0x00000FFF);
-               radio_ver = (tmp & 0x0FFFF000) >> 12;
+               radio_id = (tmp & 0x0FFFF000) >> 12;
                radio_rev = (tmp & 0xF0000000) >> 28;
+               radio_ver = 0; /* Probably not available on old hw */
        }
 
        if (radio_manuf != 0x17F /* Broadcom */)
                unsupported = 1;
        switch (phy_type) {
        case B43_PHYTYPE_A:
-               if (radio_ver != 0x2060)
+               if (radio_id != 0x2060)
                        unsupported = 1;
                if (radio_rev != 1)
                        unsupported = 1;
@@ -4422,43 +4519,49 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                        unsupported = 1;
                break;
        case B43_PHYTYPE_B:
-               if ((radio_ver & 0xFFF0) != 0x2050)
+               if ((radio_id & 0xFFF0) != 0x2050)
                        unsupported = 1;
                break;
        case B43_PHYTYPE_G:
-               if (radio_ver != 0x2050)
+               if (radio_id != 0x2050)
                        unsupported = 1;
                break;
        case B43_PHYTYPE_N:
-               if (radio_ver != 0x2055 && radio_ver != 0x2056)
+               if (radio_id != 0x2055 && radio_id != 0x2056 &&
+                   radio_id != 0x2057)
+                       unsupported = 1;
+               if (radio_id == 0x2057 &&
+                   !(radio_rev == 9 || radio_rev == 14))
                        unsupported = 1;
                break;
        case B43_PHYTYPE_LP:
-               if (radio_ver != 0x2062 && radio_ver != 0x2063)
+               if (radio_id != 0x2062 && radio_id != 0x2063)
                        unsupported = 1;
                break;
        case B43_PHYTYPE_HT:
-               if (radio_ver != 0x2059)
+               if (radio_id != 0x2059)
                        unsupported = 1;
                break;
        case B43_PHYTYPE_LCN:
-               if (radio_ver != 0x2064)
+               if (radio_id != 0x2064)
                        unsupported = 1;
                break;
        default:
                B43_WARN_ON(1);
        }
        if (unsupported) {
-               b43err(dev->wl, "FOUND UNSUPPORTED RADIO "
-                      "(Manuf 0x%X, Version 0x%X, Revision %u)\n",
-                      radio_manuf, radio_ver, radio_rev);
+               b43err(dev->wl,
+                      "FOUND UNSUPPORTED RADIO (Manuf 0x%X, ID 0x%X, Revision %u, Version %u)\n",
+                      radio_manuf, radio_id, radio_rev, radio_ver);
                return -EOPNOTSUPP;
        }
-       b43dbg(dev->wl, "Found Radio: Manuf 0x%X, Version 0x%X, Revision %u\n",
-              radio_manuf, radio_ver, radio_rev);
+       b43info(dev->wl,
+               "Found Radio: Manuf 0x%X, ID 0x%X, Revision %u, Version %u\n",
+               radio_manuf, radio_id, radio_rev, radio_ver);
 
+       /* FIXME: b43 treats "id" as "ver" and ignores the real "ver" */
        phy->radio_manuf = radio_manuf;
-       phy->radio_ver = radio_ver;
+       phy->radio_ver = radio_id;
        phy->radio_rev = radio_rev;
 
        phy->analog = analog_type;
@@ -5066,12 +5169,24 @@ static int b43_setup_bands(struct b43_wldev *dev,
                           bool have_2ghz_phy, bool have_5ghz_phy)
 {
        struct ieee80211_hw *hw = dev->wl->hw;
+       struct b43_phy *phy = &dev->phy;
+       bool limited_2g;
+       bool limited_5g;
+
+       /* We don't support all 2 GHz channels on some devices */
+       limited_2g = phy->radio_ver == 0x2057 &&
+                    (phy->radio_rev == 9 || phy->radio_rev == 14);
+       limited_5g = phy->radio_ver == 0x2057 &&
+                    phy->radio_rev == 9;
 
        if (have_2ghz_phy)
-               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43_band_2GHz;
+               hw->wiphy->bands[IEEE80211_BAND_2GHZ] = limited_2g ?
+                       &b43_band_2ghz_limited : &b43_band_2GHz;
        if (dev->phy.type == B43_PHYTYPE_N) {
                if (have_5ghz_phy)
-                       hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_nphy;
+                       hw->wiphy->bands[IEEE80211_BAND_5GHZ] = limited_5g ?
+                               &b43_band_5GHz_nphy_limited :
+                               &b43_band_5GHz_nphy;
        } else {
                if (have_5ghz_phy)
                        hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_aphy;
@@ -5219,14 +5334,15 @@ static int b43_wireless_core_attach(struct b43_wldev *dev)
        b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy);
 
        /* We don't support 5 GHz on some PHYs yet */
-       switch (dev->phy.type) {
-       case B43_PHYTYPE_A:
-       case B43_PHYTYPE_G:
-       case B43_PHYTYPE_N:
-       case B43_PHYTYPE_LP:
-       case B43_PHYTYPE_HT:
-               b43warn(wl, "5 GHz band is unsupported on this PHY\n");
-               have_5ghz_phy = false;
+       if (have_5ghz_phy) {
+               switch (dev->phy.type) {
+               case B43_PHYTYPE_A:
+               case B43_PHYTYPE_G:
+               case B43_PHYTYPE_LP:
+               case B43_PHYTYPE_HT:
+                       b43warn(wl, "5 GHz band is unsupported on this PHY\n");
+                       have_5ghz_phy = false;
+               }
        }
 
        if (!have_2ghz_phy && !have_5ghz_phy) {
index f476fc337d64c8098ff7397bff7fedd2a9bcc4b0..9f22e4b4c13297207c309d7ea6a1fd884d061655 100644 (file)
@@ -99,6 +99,7 @@ void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags);
 void b43_mac_suspend(struct b43_wldev *dev);
 void b43_mac_enable(struct b43_wldev *dev);
 void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on);
+void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode);
 
 
 struct b43_request_fw_context;
index a6c38104693d4f60276cc087aedb67aab1fc0c99..25e40432d68b9c0c9dcc25ff44f2386b56e6ca68 100644 (file)
@@ -573,7 +573,7 @@ static void b43_aphy_op_pwork_60sec(struct b43_wldev *dev)
 {//TODO
 }
 
-const struct b43_phy_operations b43_phyops_a = {
+static const struct b43_phy_operations b43_phyops_a = {
        .allocate               = b43_aphy_op_allocate,
        .free                   = b43_aphy_op_free,
        .prepare_structs        = b43_aphy_op_prepare_structs,
index 5cfaab7b16ee8c1de47bec7838aa1ab0a679f485..f7d0d929a37479ae0915655691eeb3846928d80b 100644 (file)
@@ -123,8 +123,4 @@ struct b43_phy_a {
  */
 void b43_phy_inita(struct b43_wldev *dev);
 
-
-struct b43_phy_operations;
-extern const struct b43_phy_operations b43_phyops_a;
-
 #endif /* LINUX_B43_PHY_A_H_ */
index 08244b3b327e5f98f06b5d5db8943d8e624bb3a8..3cbef21b4726dc106c5746f79689a10d0db6c4e0 100644 (file)
@@ -45,11 +45,10 @@ int b43_phy_allocate(struct b43_wldev *dev)
        phy->ops = NULL;
 
        switch (phy->type) {
-       case B43_PHYTYPE_A:
-               phy->ops = &b43_phyops_a;
-               break;
        case B43_PHYTYPE_G:
+#ifdef CONFIG_B43_PHY_G
                phy->ops = &b43_phyops_g;
+#endif
                break;
        case B43_PHYTYPE_N:
 #ifdef CONFIG_B43_PHY_N
@@ -94,7 +93,13 @@ int b43_phy_init(struct b43_wldev *dev)
        const struct b43_phy_operations *ops = phy->ops;
        int err;
 
-       phy->channel = ops->get_default_chan(dev);
+       /* During PHY init we need to use some channel. On the first init this
+        * function is called *before* b43_op_config, so our pointer is NULL.
+        */
+       if (!phy->chandef) {
+               phy->chandef = &dev->wl->hw->conf.chandef;
+               phy->channel = phy->chandef->chan->hw_value;
+       }
 
        phy->ops->switch_analog(dev, true);
        b43_software_rfkill(dev, false);
@@ -106,9 +111,7 @@ int b43_phy_init(struct b43_wldev *dev)
        }
        phy->do_full_init = false;
 
-       /* Make sure to switch hardware and firmware (SHM) to
-        * the default channel. */
-       err = b43_switch_channel(dev, ops->get_default_chan(dev));
+       err = b43_switch_channel(dev, phy->channel);
        if (err) {
                b43err(dev->wl, "PHY init: Channel switch to default failed\n");
                goto err_phy_exit;
@@ -408,9 +411,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
        u16 channelcookie, savedcookie;
        int err;
 
-       if (new_channel == B43_DEFAULT_CHANNEL)
-               new_channel = phy->ops->get_default_chan(dev);
-
        /* First we set the channel radio code to prevent the
         * firmware from sending ghost packets.
         */
@@ -428,7 +428,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
        if (err)
                goto err_restore_cookie;
 
-       dev->phy.channel = new_channel;
        /* Wait for the radio to tune to the channel and stabilize. */
        msleep(8);
 
@@ -547,10 +546,9 @@ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on)
 }
 
 
-bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type)
+bool b43_is_40mhz(struct b43_wldev *dev)
 {
-       return (channel_type == NL80211_CHAN_HT40MINUS ||
-               channel_type == NL80211_CHAN_HT40PLUS);
+       return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40;
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */
index 4ad6240d9ff40e53557b4d29b27788b3eb9602d5..3912274f71e30b7040ade6fc9c7d455ab1192d6a 100644 (file)
@@ -228,9 +228,6 @@ struct b43_phy {
        bool supports_2ghz;
        bool supports_5ghz;
 
-       /* HT info */
-       bool is_40mhz;
-
        /* Is GMODE (2 GHz mode) bit enabled? */
        bool gmode;
 
@@ -267,9 +264,8 @@ struct b43_phy {
        unsigned long next_txpwr_check_time;
 
        /* Current channel */
+       struct cfg80211_chan_def *chandef;
        unsigned int channel;
-       u16 channel_freq;
-       enum nl80211_channel_type channel_type;
 
        /* PHY TX errors counter. */
        atomic_t txerr_cnt;
@@ -400,10 +396,6 @@ void b43_phy_take_out_of_reset(struct b43_wldev *dev);
  * b43_switch_channel - Switch to another channel
  */
 int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel);
-/**
- * B43_DEFAULT_CHANNEL - Switch to the default channel.
- */
-#define B43_DEFAULT_CHANNEL    UINT_MAX
 
 /**
  * b43_software_rfkill - Turn the radio ON or OFF in software.
@@ -454,7 +446,7 @@ int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset);
  */
 void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on);
 
-bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type);
+bool b43_is_40mhz(struct b43_wldev *dev);
 
 void b43_phy_force_clock(struct b43_wldev *dev, bool force);
 
index 5d6833f184982f67eeb79977dbb49d221ddbd216..f2974c6b1c01fa3ddd11753a74badac91bc9b43e 100644 (file)
@@ -596,7 +596,7 @@ static void b43_phy_ht_tx_power_ctl_setup(struct b43_wldev *dev)
        u8 target[3];
        s16 a1[3], b0[3], b1[3];
 
-       u16 freq = dev->phy.channel_freq;
+       u16 freq = dev->phy.chandef->chan->center_freq;
        int i, c;
 
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
index 0bafa3b17035db8d5767da2632da343dd1ae70c7..e76bbdf3247e9b095af94bf907b2439f3f25f5f3 100644 (file)
@@ -54,39 +54,6 @@ enum lcn_sense_type {
        B43_SENSE_VBAT,
 };
 
-/* In theory it's PHY common function, move if needed */
-/* brcms_b_switch_macfreq */
-static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode)
-{
-       if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) {
-               switch (spurmode) {
-               case 2:         /* 126 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
-                       break;
-               case 1:         /* 123 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
-                       break;
-               default:        /* 120 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
-                       break;
-               }
-       } else if (dev->phy.type == B43_PHYTYPE_LCN) {
-               switch (spurmode) {
-               case 1:         /* 82 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
-                       break;
-               default:        /* 80 Mhz */
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
-                       break;
-               }
-       }
-}
-
 /**************************************************
  * Radio 2064.
  **************************************************/
@@ -609,7 +576,7 @@ static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev,
                b43_phy_write(dev, 0x93b, ((0 << 13) + 23));
                b43_phy_write(dev, 0x93c, ((0 << 13) + 1989));
        }
-       b43_phy_switch_macfreq(dev, enable);
+       b43_mac_switch_freq(dev, enable);
 }
 
 /**************************************************
index 86569f6a870507c1723d95ccc6d39a2c13f449a2..d269fbb27b9ebabbc66db7b41263cf316aee463d 100644 (file)
@@ -36,6 +36,7 @@
 #include "main.h"
 
 struct nphy_txgains {
+       u16 tx_lpf[2];
        u16 txgm[2];
        u16 pga[2];
        u16 pad[2];
@@ -43,6 +44,7 @@ struct nphy_txgains {
 };
 
 struct nphy_iqcal_params {
+       u16 tx_lpf;
        u16 txgm;
        u16 pga;
        u16 pad;
@@ -69,6 +71,14 @@ enum b43_nphy_rf_sequence {
        B43_RFSEQ_UPDATE_GAINU,
 };
 
+enum n_rf_ctl_over_cmd {
+       N_RF_CTL_OVER_CMD_RXRF_PU = 0,
+       N_RF_CTL_OVER_CMD_RX_PU = 1,
+       N_RF_CTL_OVER_CMD_TX_PU = 2,
+       N_RF_CTL_OVER_CMD_RX_GAIN = 3,
+       N_RF_CTL_OVER_CMD_TX_GAIN = 4,
+};
+
 enum n_intc_override {
        N_INTC_OVERRIDE_OFF = 0,
        N_INTC_OVERRIDE_TRSW = 1,
@@ -140,11 +150,19 @@ ok:
        b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode);
 }
 
+static void b43_nphy_rf_ctl_override_rev19(struct b43_wldev *dev, u16 field,
+                                          u16 value, u8 core, bool off,
+                                          u8 override_id)
+{
+       /* TODO */
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverrideRev7 */
 static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field,
                                          u16 value, u8 core, bool off,
                                          u8 override)
 {
+       struct b43_phy *phy = &dev->phy;
        const struct nphy_rf_control_override_rev7 *e;
        u16 en_addrs[3][2] = {
                { 0x0E7, 0x0EC }, { 0x342, 0x343 }, { 0x346, 0x347 }
@@ -154,6 +172,11 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field,
        u16 val_addr;
        u8 i;
 
+       if (phy->rev >= 19 || phy->rev < 3) {
+               B43_WARN_ON(1);
+               return;
+       }
+
        /* Remember: we can get NULL! */
        e = b43_nphy_get_rf_ctl_over_rev7(dev, field, override);
 
@@ -181,6 +204,50 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field,
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverideOneToMany */
+static void b43_nphy_rf_ctl_override_one_to_many(struct b43_wldev *dev,
+                                                enum n_rf_ctl_over_cmd cmd,
+                                                u16 value, u8 core, bool off)
+{
+       struct b43_phy *phy = &dev->phy;
+       u16 tmp;
+
+       B43_WARN_ON(phy->rev < 7);
+
+       switch (cmd) {
+       case N_RF_CTL_OVER_CMD_RXRF_PU:
+               b43_nphy_rf_ctl_override_rev7(dev, 0x20, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x10, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x08, value, core, off, 1);
+               break;
+       case N_RF_CTL_OVER_CMD_RX_PU:
+               b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 2);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x0800, 0, core, off, 1);
+               break;
+       case N_RF_CTL_OVER_CMD_TX_PU:
+               b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 0);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 2);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x0800, 1, core, off, 1);
+               break;
+       case N_RF_CTL_OVER_CMD_RX_GAIN:
+               tmp = value & 0xFF;
+               b43_nphy_rf_ctl_override_rev7(dev, 0x0800, tmp, core, off, 0);
+               tmp = value >> 8;
+               b43_nphy_rf_ctl_override_rev7(dev, 0x6000, tmp, core, off, 0);
+               break;
+       case N_RF_CTL_OVER_CMD_TX_GAIN:
+               tmp = value & 0x7FFF;
+               b43_nphy_rf_ctl_override_rev7(dev, 0x1000, tmp, core, off, 0);
+               tmp = value >> 14;
+               b43_nphy_rf_ctl_override_rev7(dev, 0x4000, tmp, core, off, 0);
+               break;
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */
 static void b43_nphy_rf_ctl_override(struct b43_wldev *dev, u16 field,
                                     u16 value, u8 core, bool off)
@@ -264,6 +331,8 @@ static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev,
        u16 reg, tmp, tmp2, val;
        int core;
 
+       /* TODO: What about rev19+? Revs 3+ and 7+ are a bit similar */
+
        for (core = 0; core < 2; core++) {
                if ((core_sel == 1 && core != 0) ||
                    (core_sel == 2 && core != 1))
@@ -274,6 +343,7 @@ static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev,
                switch (intc_override) {
                case N_INTC_OVERRIDE_OFF:
                        b43_phy_write(dev, reg, 0);
+                       b43_phy_mask(dev, 0x2ff, ~0x2000);
                        b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
                        break;
                case N_INTC_OVERRIDE_TRSW:
@@ -505,6 +575,14 @@ static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable)
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */
+static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset)
+{
+       if (!offset)
+               offset = b43_is_40mhz(dev) ? 0x159 : 0x154;
+       return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7;
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/AdjustLnaGainTbl */
 static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev)
 {
@@ -590,44 +668,270 @@ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
  * Radio 0x2057
  **************************************************/
 
-/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rcal */
+static void b43_radio_2057_chantab_upload(struct b43_wldev *dev,
+                                         const struct b43_nphy_chantabent_rev7 *e_r7,
+                                         const struct b43_nphy_chantabent_rev7_2g *e_r7_2g)
+{
+       if (e_r7_2g) {
+               b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7_2g->radio_vcocal_countval0);
+               b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7_2g->radio_vcocal_countval1);
+               b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7_2g->radio_rfpll_refmaster_sparextalsize);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7_2g->radio_rfpll_loopfilter_r1);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7_2g->radio_rfpll_loopfilter_c2);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7_2g->radio_rfpll_loopfilter_c1);
+               b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7_2g->radio_cp_kpd_idac);
+               b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7_2g->radio_rfpll_mmd0);
+               b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7_2g->radio_rfpll_mmd1);
+               b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7_2g->radio_vcobuf_tune);
+               b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7_2g->radio_logen_mx2g_tune);
+               b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7_2g->radio_logen_indbuf2g_tune);
+               b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7_2g->radio_txmix2g_tune_boost_pu_core0);
+               b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7_2g->radio_pad2g_tune_pus_core0);
+               b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7_2g->radio_lna2g_tune_core0);
+               b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7_2g->radio_txmix2g_tune_boost_pu_core1);
+               b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7_2g->radio_pad2g_tune_pus_core1);
+               b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7_2g->radio_lna2g_tune_core1);
+
+       } else {
+               b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7->radio_vcocal_countval0);
+               b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7->radio_vcocal_countval1);
+               b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7->radio_rfpll_refmaster_sparextalsize);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7->radio_rfpll_loopfilter_r1);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7->radio_rfpll_loopfilter_c2);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7->radio_rfpll_loopfilter_c1);
+               b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7->radio_cp_kpd_idac);
+               b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7->radio_rfpll_mmd0);
+               b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7->radio_rfpll_mmd1);
+               b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7->radio_vcobuf_tune);
+               b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7->radio_logen_mx2g_tune);
+               b43_radio_write(dev, R2057_LOGEN_MX5G_TUNE, e_r7->radio_logen_mx5g_tune);
+               b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7->radio_logen_indbuf2g_tune);
+               b43_radio_write(dev, R2057_LOGEN_INDBUF5G_TUNE, e_r7->radio_logen_indbuf5g_tune);
+               b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7->radio_txmix2g_tune_boost_pu_core0);
+               b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7->radio_pad2g_tune_pus_core0);
+               b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE0, e_r7->radio_pga_boost_tune_core0);
+               b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE0, e_r7->radio_txmix5g_boost_tune_core0);
+               b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE0, e_r7->radio_pad5g_tune_misc_pus_core0);
+               b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7->radio_lna2g_tune_core0);
+               b43_radio_write(dev, R2057_LNA5G_TUNE_CORE0, e_r7->radio_lna5g_tune_core0);
+               b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7->radio_txmix2g_tune_boost_pu_core1);
+               b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7->radio_pad2g_tune_pus_core1);
+               b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE1, e_r7->radio_pga_boost_tune_core1);
+               b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE1, e_r7->radio_txmix5g_boost_tune_core1);
+               b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE1, e_r7->radio_pad5g_tune_misc_pus_core1);
+               b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7->radio_lna2g_tune_core1);
+               b43_radio_write(dev, R2057_LNA5G_TUNE_CORE1, e_r7->radio_lna5g_tune_core1);
+       }
+}
+
+static void b43_radio_2057_setup(struct b43_wldev *dev,
+                                const struct b43_nphy_chantabent_rev7 *tabent_r7,
+                                const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g)
+{
+       struct b43_phy *phy = &dev->phy;
+
+       b43_radio_2057_chantab_upload(dev, tabent_r7, tabent_r7_2g);
+
+       switch (phy->radio_rev) {
+       case 0 ... 4:
+       case 6:
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+                       b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x3f);
+                       b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f);
+                       b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8);
+                       b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8);
+               } else {
+                       b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1f);
+                       b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f);
+                       b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8);
+                       b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8);
+               }
+               break;
+       case 9: /* e.g. PHY rev 16 */
+               b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x20);
+               b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x18);
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x38);
+                       b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x0f);
+
+                       if (b43_is_40mhz(dev)) {
+                               /* TODO */
+                       } else {
+                               b43_radio_write(dev,
+                                               R2057_PAD_BIAS_FILTER_BWS_CORE0,
+                                               0x3c);
+                               b43_radio_write(dev,
+                                               R2057_PAD_BIAS_FILTER_BWS_CORE1,
+                                               0x3c);
+                       }
+               }
+               break;
+       case 14: /* 2 GHz only */
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1b);
+               b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x1f);
+               b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x1f);
+               break;
+       }
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               u16 txmix2g_tune_boost_pu = 0;
+               u16 pad2g_tune_pus = 0;
+
+               if (b43_nphy_ipa(dev)) {
+                       switch (phy->radio_rev) {
+                       case 9:
+                               txmix2g_tune_boost_pu = 0x0041;
+                               /* TODO */
+                               break;
+                       case 14:
+                               txmix2g_tune_boost_pu = 0x21;
+                               pad2g_tune_pus = 0x23;
+                               break;
+                       }
+               }
+
+               if (txmix2g_tune_boost_pu)
+                       b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0,
+                                       txmix2g_tune_boost_pu);
+               if (pad2g_tune_pus)
+                       b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0,
+                                       pad2g_tune_pus);
+               if (txmix2g_tune_boost_pu)
+                       b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1,
+                                       txmix2g_tune_boost_pu);
+               if (pad2g_tune_pus)
+                       b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1,
+                                       pad2g_tune_pus);
+       }
+
+       usleep_range(50, 100);
+
+       /* VCO calibration */
+       b43_radio_mask(dev, R2057_RFPLL_MISC_EN, ~0x01);
+       b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x04);
+       b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x4);
+       b43_radio_set(dev, R2057_RFPLL_MISC_EN, 0x01);
+       usleep_range(300, 600);
+}
+
+/* Calibrate resistors in LPF of PLL?
+ * http://bcm-v4.sipsolutions.net/PHY/radio205x_rcal
+ */
 static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
+       u16 saved_regs_phy[12];
+       u16 saved_regs_phy_rf[6];
+       u16 saved_regs_radio[2] = { };
+       static const u16 phy_to_store[] = {
+               B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2,
+               B43_NPHY_RFCTL_LUT_TRSW_LO1, B43_NPHY_RFCTL_LUT_TRSW_LO2,
+               B43_NPHY_RFCTL_RXG1, B43_NPHY_RFCTL_RXG2,
+               B43_NPHY_RFCTL_TXG1, B43_NPHY_RFCTL_TXG2,
+               B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4,
+               B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6,
+       };
+       static const u16 phy_to_store_rf[] = {
+               B43_NPHY_REV3_RFCTL_OVER0, B43_NPHY_REV3_RFCTL_OVER1,
+               B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4,
+               B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6,
+       };
        u16 tmp;
+       int i;
 
-       if (phy->radio_rev == 5) {
-               b43_phy_mask(dev, 0x342, ~0x2);
+       /* Save */
+       for (i = 0; i < ARRAY_SIZE(phy_to_store); i++)
+               saved_regs_phy[i] = b43_phy_read(dev, phy_to_store[i]);
+       for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++)
+               saved_regs_phy_rf[i] = b43_phy_read(dev, phy_to_store_rf[i]);
+
+       /* Set */
+       for (i = 0; i < ARRAY_SIZE(phy_to_store); i++)
+               b43_phy_write(dev, phy_to_store[i], 0);
+       b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER0, 0x07ff);
+       b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER1, 0x07ff);
+       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x07ff);
+       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0x07ff);
+       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0x007f);
+       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0x007f);
+
+       switch (phy->radio_rev) {
+       case 5:
+               b43_phy_mask(dev, B43_NPHY_REV7_RF_CTL_OVER3, ~0x2);
                udelay(10);
                b43_radio_set(dev, R2057_IQTEST_SEL_PU, 0x1);
-               b43_radio_maskset(dev, 0x1ca, ~0x2, 0x1);
+               b43_radio_maskset(dev, R2057v7_IQTEST_SEL_PU2, ~0x2, 0x1);
+               break;
+       case 9:
+               b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2);
+               b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2);
+               saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU);
+               b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x11);
+               break;
+       case 14:
+               saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU);
+               saved_regs_radio[1] = b43_radio_read(dev, R2057v7_IQTEST_SEL_PU2);
+               b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2);
+               b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2);
+               b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, 0x2);
+               b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x1);
+               break;
        }
 
+       /* Enable */
        b43_radio_set(dev, R2057_RCAL_CONFIG, 0x1);
        udelay(10);
-       b43_radio_set(dev, R2057_RCAL_CONFIG, 0x3);
-       if (!b43_radio_wait_value(dev, R2057_RCCAL_N1_1, 1, 1, 100, 1000000)) {
+
+       /* Start */
+       b43_radio_set(dev, R2057_RCAL_CONFIG, 0x2);
+       usleep_range(100, 200);
+
+       /* Stop */
+       b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2);
+
+       /* Wait and check for result */
+       if (!b43_radio_wait_value(dev, R2057_RCAL_STATUS, 1, 1, 100, 1000000)) {
                b43err(dev->wl, "Radio 0x2057 rcal timeout\n");
                return 0;
        }
-       b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2);
        tmp = b43_radio_read(dev, R2057_RCAL_STATUS) & 0x3E;
+
+       /* Disable */
        b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1);
 
-       if (phy->radio_rev == 5) {
-               b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1);
-               b43_radio_mask(dev, 0x1ca, ~0x2);
-       }
-       if (phy->radio_rev <= 4 || phy->radio_rev == 6) {
+       /* Restore */
+       for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++)
+               b43_phy_write(dev, phy_to_store_rf[i], saved_regs_phy_rf[i]);
+       for (i = 0; i < ARRAY_SIZE(phy_to_store); i++)
+               b43_phy_write(dev, phy_to_store[i], saved_regs_phy[i]);
+
+       switch (phy->radio_rev) {
+       case 0 ... 4:
+       case 6:
                b43_radio_maskset(dev, R2057_TEMPSENSE_CONFIG, ~0x3C, tmp);
                b43_radio_maskset(dev, R2057_BANDGAP_RCAL_TRIM, ~0xF0,
                                  tmp << 2);
+               break;
+       case 5:
+               b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1);
+               b43_radio_mask(dev, R2057v7_IQTEST_SEL_PU2, ~0x2);
+               break;
+       case 9:
+               b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]);
+               break;
+       case 14:
+               b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]);
+               b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, saved_regs_radio[1]);
+               break;
        }
 
        return tmp & 0x3e;
 }
 
-/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal */
+/* Calibrate the internal RC oscillator?
+ * http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal
+ */
 static u16 b43_radio_2057_rccal(struct b43_wldev *dev)
 {
        struct b43_phy *phy = &dev->phy;
@@ -635,49 +939,76 @@ static u16 b43_radio_2057_rccal(struct b43_wldev *dev)
                        phy->radio_rev == 6);
        u16 tmp;
 
+       /* Setup cal */
        if (special) {
                b43_radio_write(dev, R2057_RCCAL_MASTER, 0x61);
                b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0);
        } else {
-               b43_radio_write(dev, 0x1AE, 0x61);
-               b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE1);
+               b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61);
+               b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE9);
        }
        b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
+
+       /* Start, wait, stop */
        b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
-       if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
+       if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
                                  5000000))
                b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n");
+       usleep_range(35, 70);
        b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
+       usleep_range(70, 140);
+
+       /* Setup cal */
        if (special) {
                b43_radio_write(dev, R2057_RCCAL_MASTER, 0x69);
                b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0);
        } else {
-               b43_radio_write(dev, 0x1AE, 0x69);
+               b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x69);
                b43_radio_write(dev, R2057_RCCAL_TRC0, 0xD5);
        }
        b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
+
+       /* Start, wait, stop */
+       usleep_range(35, 70);
        b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
-       if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
+       usleep_range(70, 140);
+       if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
                                  5000000))
                b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n");
+       usleep_range(35, 70);
        b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
+       usleep_range(70, 140);
+
+       /* Setup cal */
        if (special) {
                b43_radio_write(dev, R2057_RCCAL_MASTER, 0x73);
                b43_radio_write(dev, R2057_RCCAL_X1, 0x28);
                b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0);
        } else {
-               b43_radio_write(dev, 0x1AE, 0x73);
+               b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x73);
                b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
                b43_radio_write(dev, R2057_RCCAL_TRC0, 0x99);
        }
+
+       /* Start, wait, stop */
+       usleep_range(35, 70);
        b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
-       if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
+       usleep_range(70, 140);
+       if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
                                  5000000)) {
                b43err(dev->wl, "Radio 0x2057 rcal timeout\n");
                return 0;
        }
        tmp = b43_radio_read(dev, R2057_RCCAL_DONE_OSCCAP);
+       usleep_range(35, 70);
        b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
+       usleep_range(70, 140);
+
+       if (special)
+               b43_radio_mask(dev, R2057_RCCAL_MASTER, ~0x1);
+       else
+               b43_radio_mask(dev, R2057v7_RCCAL_MASTER, ~0x1);
+
        return tmp;
 }
 
@@ -694,6 +1025,9 @@ static void b43_radio_2057_init_post(struct b43_wldev *dev)
 {
        b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x1);
 
+       if (0) /* FIXME: Is this BCM43217 specific? */
+               b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x2);
+
        b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x78);
        b43_radio_set(dev, R2057_XTAL_CONFIG2, 0x80);
        mdelay(2);
@@ -798,6 +1132,7 @@ static void b43_chantab_radio_2056_upload(struct b43_wldev *dev,
 static void b43_radio_2056_setup(struct b43_wldev *dev,
                                const struct b43_nphy_channeltab_entry_rev3 *e)
 {
+       struct b43_phy *phy = &dev->phy;
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
        enum ieee80211_band band = b43_current_band(dev->wl);
        u16 offset;
@@ -895,7 +1230,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                                        offset | B2056_TX_MIXG_BOOST_TUNE,
                                        mixg_boost);
                        } else {
-                               bias = dev->phy.is_40mhz ? 0x40 : 0x20;
+                               bias = b43_is_40mhz(dev) ? 0x40 : 0x20;
                                b43_radio_write(dev,
                                        offset | B2056_TX_INTPAG_IMAIN_STAT,
                                        bias);
@@ -909,7 +1244,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
                        b43_radio_write(dev, offset | B2056_TX_PA_SPARE1, 0xee);
                }
        } else if (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ) {
-               u16 freq = dev->phy.channel_freq;
+               u16 freq = phy->chandef->chan->center_freq;
                if (freq < 5100) {
                        paa_boost = 0xA;
                        pada_boost = 0x77;
@@ -1210,8 +1545,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
        u16 bw, len, rot, angle;
        struct b43_c32 *samples;
 
-
-       bw = (dev->phy.is_40mhz) ? 40 : 20;
+       bw = b43_is_40mhz(dev) ? 40 : 20;
        len = bw << 3;
 
        if (test) {
@@ -1220,7 +1554,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
                else
                        bw = 80;
 
-               if (dev->phy.is_40mhz)
+               if (b43_is_40mhz(dev))
                        bw <<= 1;
 
                len = bw << 1;
@@ -1248,8 +1582,10 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */
 static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
-                                       u16 wait, bool iqmode, bool dac_test)
+                                u16 wait, bool iqmode, bool dac_test,
+                                bool modify_bbmult)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        int i;
        u16 seq_mode;
@@ -1257,17 +1593,35 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
 
        b43_nphy_stay_in_carrier_search(dev, true);
 
+       if (phy->rev >= 7) {
+               bool lpf_bw3, lpf_bw4;
+
+               lpf_bw3 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80;
+               lpf_bw4 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER4) & 0x80;
+
+               if (lpf_bw3 || lpf_bw4) {
+                       /* TODO */
+               } else {
+                       u16 value = b43_nphy_read_lpf_ctl(dev, 0);
+                       if (phy->rev >= 19)
+                               b43_nphy_rf_ctl_override_rev19(dev, 0x80, value,
+                                                              0, false, 1);
+                       else
+                               b43_nphy_rf_ctl_override_rev7(dev, 0x80, value,
+                                                             0, false, 1);
+                       nphy->lpf_bw_overrode_for_sample_play = true;
+               }
+       }
+
        if ((nphy->bb_mult_save & 0x80000000) == 0) {
                tmp = b43_ntab_read(dev, B43_NTAB16(15, 87));
                nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000;
        }
 
-       /* TODO: add modify_bbmult argument */
-       if (!dev->phy.is_40mhz)
-               tmp = 0x6464;
-       else
-               tmp = 0x4747;
-       b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
+       if (modify_bbmult) {
+               tmp = !b43_is_40mhz(dev) ? 0x6464 : 0x4747;
+               b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
+       }
 
        b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1));
 
@@ -1285,10 +1639,8 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
                b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF);
                b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000);
        } else {
-               if (dac_test)
-                       b43_phy_write(dev, B43_NPHY_SAMP_CMD, 5);
-               else
-                       b43_phy_write(dev, B43_NPHY_SAMP_CMD, 1);
+               tmp = dac_test ? 5 : 1;
+               b43_phy_write(dev, B43_NPHY_SAMP_CMD, tmp);
        }
        for (i = 0; i < 100; i++) {
                if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & 1)) {
@@ -1388,6 +1740,12 @@ static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale,
        }
 }
 
+static void b43_nphy_rssi_select_rev19(struct b43_wldev *dev, u8 code,
+                                      enum n_rssi_type rssi_type)
+{
+       /* TODO */
+}
+
 static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code,
                                      enum n_rssi_type rssi_type)
 {
@@ -1457,13 +1815,15 @@ static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code,
                                        enum ieee80211_band band =
                                                b43_current_band(dev->wl);
 
-                                       if (b43_nphy_ipa(dev))
-                                               val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE;
-                                       else
-                                               val = 0x11;
-                                       reg = (i == 0) ? 0x2000 : 0x3000;
-                                       reg |= B2055_PADDRV;
-                                       b43_radio_write(dev, reg, val);
+                                       if (dev->phy.rev < 7) {
+                                               if (b43_nphy_ipa(dev))
+                                                       val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE;
+                                               else
+                                                       val = 0x11;
+                                               reg = (i == 0) ? B2056_TX0 : B2056_TX1;
+                                               reg |= B2056_TX_TX_SSI_MUX;
+                                               b43_radio_write(dev, reg, val);
+                                       }
 
                                        reg = (i == 0) ?
                                                B43_NPHY_AFECTL_OVER1 :
@@ -1550,7 +1910,9 @@ static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code,
 static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code,
                                 enum n_rssi_type type)
 {
-       if (dev->phy.rev >= 3)
+       if (dev->phy.rev >= 19)
+               b43_nphy_rssi_select_rev19(dev, code, type);
+       else if (dev->phy.rev >= 3)
                b43_nphy_rev3_rssi_select(dev, code, type);
        else
                b43_nphy_rev2_rssi_select(dev, code, type);
@@ -1594,6 +1956,8 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type,
        u16 save_regs_phy[9];
        u16 s[2];
 
+       /* TODO: rev7+ is treated like rev3+, what about rev19+? */
+
        if (dev->phy.rev >= 3) {
                save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1);
                save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2);
@@ -1675,6 +2039,7 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type,
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */
 static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
 
        u16 saved_regs_phy_rfctl[2];
@@ -1692,12 +2057,14 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER,
                B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2,
                B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER,
-               0x342, 0x343, 0x346, 0x347,
+               B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4,
+               B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6,
                0x2ff,
                B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1,
                B43_NPHY_RFCTL_CMD,
                B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2,
-               0x340, 0x341, 0x344, 0x345,
+               B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4,
+               B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6,
                B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2
        };
        u16 *regs_to_store;
@@ -1744,9 +2111,24 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
        b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 1, 7);
 
        if (dev->phy.rev >= 7) {
-               /* TODO */
+               b43_nphy_rf_ctl_override_one_to_many(dev,
+                                                    N_RF_CTL_OVER_CMD_RXRF_PU,
+                                                    0, 0, false);
+               b43_nphy_rf_ctl_override_one_to_many(dev,
+                                                    N_RF_CTL_OVER_CMD_RX_PU,
+                                                    1, 0, false);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0);
+               b43_nphy_rf_ctl_override_rev7(dev, 0x40, 1, 0, false, 0);
                if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x20, 0, 0, false,
+                                                     0);
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x10, 1, 0, false,
+                                                     0);
                } else {
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x10, 0, 0, false,
+                                                     0);
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x20, 1, 0, false,
+                                                     0);
                }
        } else {
                b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false);
@@ -1775,7 +2157,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                /* Grab RSSI results for every possible VCM */
                for (vcm = 0; vcm < 8; vcm++) {
                        if (dev->phy.rev >= 7)
-                               ;
+                               b43_radio_maskset(dev,
+                                                 core ? R2057_NB_MASTER_CORE1 :
+                                                        R2057_NB_MASTER_CORE0,
+                                                 ~R2057_VCM_MASK, vcm);
                        else
                                b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC,
                                                  0xE3, vcm << 2);
@@ -1806,7 +2191,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
                /* Select the best VCM */
                if (dev->phy.rev >= 7)
-                       ;
+                       b43_radio_maskset(dev,
+                                         core ? R2057_NB_MASTER_CORE1 :
+                                                R2057_NB_MASTER_CORE0,
+                                         ~R2057_VCM_MASK, vcm);
                else
                        b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC,
                                          0xE3, vcm_final << 2);
@@ -1876,6 +2264,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
                rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G;
        }
        if (dev->phy.rev >= 7) {
+               rssical_radio_regs[0] = b43_radio_read(dev,
+                                                      R2057_NB_MASTER_CORE0);
+               rssical_radio_regs[1] = b43_radio_read(dev,
+                                                      R2057_NB_MASTER_CORE1);
        } else {
                rssical_radio_regs[0] = b43_radio_read(dev, B2056_RX0 |
                                                       B2056_RX_RSSI_MISC);
@@ -1897,9 +2289,9 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
 
        /* Remember for which channel we store configuration */
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
-               nphy->rssical_chanspec_2G.center_freq = dev->phy.channel_freq;
+               nphy->rssical_chanspec_2G.center_freq = phy->chandef->chan->center_freq;
        else
-               nphy->rssical_chanspec_5G.center_freq = dev->phy.channel_freq;
+               nphy->rssical_chanspec_5G.center_freq = phy->chandef->chan->center_freq;
 
        /* End of calibration, restore configuration */
        b43_nphy_classifier(dev, 7, class);
@@ -2076,7 +2468,9 @@ static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, enum n_rssi_type type)
  */
 static void b43_nphy_rssi_cal(struct b43_wldev *dev)
 {
-       if (dev->phy.rev >= 3) {
+       if (dev->phy.rev >= 19) {
+               /* TODO */
+       } else if (dev->phy.rev >= 3) {
                b43_nphy_rev3_rssi_cal(dev);
        } else {
                b43_nphy_rev2_rssi_cal(dev, N_RSSI_NB);
@@ -2089,7 +2483,21 @@ static void b43_nphy_rssi_cal(struct b43_wldev *dev)
  * Workarounds
  **************************************************/
 
-static void b43_nphy_gain_ctl_workarounds_rev3plus(struct b43_wldev *dev)
+static void b43_nphy_gain_ctl_workarounds_rev19(struct b43_wldev *dev)
+{
+       /* TODO */
+}
+
+static void b43_nphy_gain_ctl_workarounds_rev7(struct b43_wldev *dev)
+{
+       struct b43_phy *phy = &dev->phy;
+
+       switch (phy->rev) {
+       /* TODO */
+       }
+}
+
+static void b43_nphy_gain_ctl_workarounds_rev3(struct b43_wldev *dev)
 {
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
@@ -2192,7 +2600,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84);
        b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84);
 
-       if (!dev->phy.is_40mhz) {
+       if (!b43_is_40mhz(dev)) {
                /* Set dwell lengths */
                b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B);
                b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B);
@@ -2206,7 +2614,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
        b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES,
                        ~B43_NPHY_C2_CLIPWBTHRES_CLIP2, 21);
 
-       if (!dev->phy.is_40mhz) {
+       if (!b43_is_40mhz(dev)) {
                b43_phy_maskset(dev, B43_NPHY_C1_CGAINI,
                        ~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1);
                b43_phy_maskset(dev, B43_NPHY_C2_CGAINI,
@@ -2221,12 +2629,12 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
 
        if (nphy->gain_boost) {
                if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ &&
-                       dev->phy.is_40mhz)
+                   b43_is_40mhz(dev))
                        code = 4;
                else
                        code = 5;
        } else {
-               code = dev->phy.is_40mhz ? 6 : 7;
+               code = b43_is_40mhz(dev) ? 6 : 7;
        }
 
        /* Set HPVGA2 index */
@@ -2286,46 +2694,54 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */
 static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev)
 {
-       if (dev->phy.rev >= 7)
-               ; /* TODO */
+       if (dev->phy.rev >= 19)
+               b43_nphy_gain_ctl_workarounds_rev19(dev);
+       else if (dev->phy.rev >= 7)
+               b43_nphy_gain_ctl_workarounds_rev7(dev);
        else if (dev->phy.rev >= 3)
-               b43_nphy_gain_ctl_workarounds_rev3plus(dev);
+               b43_nphy_gain_ctl_workarounds_rev3(dev);
        else
                b43_nphy_gain_ctl_workarounds_rev1_2(dev);
 }
 
-/* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */
-static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset)
-{
-       if (!offset)
-               offset = (dev->phy.is_40mhz) ? 0x159 : 0x154;
-       return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7;
-}
-
 static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
 {
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
        struct b43_phy *phy = &dev->phy;
 
+       /* TX to RX */
+       u8 tx2rx_events[7] = { 4, 3, 5, 2, 1, 8, 31, };
+       u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1, };
+       /* RX to TX */
        u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3,
                                        0x1F };
        u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 };
 
-       u16 ntab7_15e_16e[] = { 0x10f, 0x10f };
+       static const u16 ntab7_15e_16e[] = { 0, 0x10f, 0x10f };
        u8 ntab7_138_146[] = { 0x11, 0x11 };
        u8 ntab7_133[] = { 0x77, 0x11, 0x11 };
 
-       u16 lpf_20, lpf_40, lpf_11b;
-       u16 bcap_val, bcap_val_11b, bcap_val_11n_20, bcap_val_11n_40;
-       u16 scap_val, scap_val_11b, scap_val_11n_20, scap_val_11n_40;
+       u16 lpf_ofdm_20mhz[2], lpf_ofdm_40mhz[2], lpf_11b[2];
+       u16 bcap_val;
+       s16 bcap_val_11b[2], bcap_val_11n_20[2], bcap_val_11n_40[2];
+       u16 scap_val;
+       s16 scap_val_11b[2], scap_val_11n_20[2], scap_val_11n_40[2];
        bool rccal_ovrd = false;
 
-       u16 rx2tx_lut_20_11b, rx2tx_lut_20_11n, rx2tx_lut_40_11n;
        u16 bias, conv, filt;
 
+       u32 noise_tbl[2];
+
        u32 tmp32;
        u8 core;
 
+       b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125);
+       b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01b3);
+       b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105);
+       b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016e);
+       b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00cd);
+       b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020);
+
        if (phy->rev == 7) {
                b43_phy_set(dev, B43_NPHY_FINERX2_CGC, 0x10);
                b43_phy_maskset(dev, B43_NPHY_FREQGAIN0, 0xFF80, 0x0020);
@@ -2345,11 +2761,18 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
                b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0xFF80, 0x0040);
                b43_phy_maskset(dev, B43_NPHY_FREQGAIN7, 0x80FF, 0x4000);
        }
-       if (phy->rev <= 8) {
+
+       if (phy->rev >= 16) {
+               b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x7ff);
+               b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x7ff);
+       } else if (phy->rev <= 8) {
                b43_phy_write(dev, B43_NPHY_FORCEFRONT0, 0x1B0);
                b43_phy_write(dev, B43_NPHY_FORCEFRONT1, 0x1B0);
        }
-       if (phy->rev >= 8)
+
+       if (phy->rev >= 16)
+               b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0xa0);
+       else if (phy->rev >= 8)
                b43_phy_maskset(dev, B43_NPHY_TXTAILCNT, ~0xFF, 0x72);
 
        b43_ntab_write(dev, B43_NTAB16(8, 0x00), 2);
@@ -2357,9 +2780,11 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
        tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0));
        tmp32 &= 0xffffff;
        b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32);
-       b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x15e), 2, ntab7_15e_16e);
-       b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x16e), 2, ntab7_15e_16e);
+       b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x15d), 3, ntab7_15e_16e);
+       b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x16d), 3, ntab7_15e_16e);
 
+       b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays,
+                                ARRAY_SIZE(tx2rx_events));
        if (b43_nphy_ipa(dev))
                b43_nphy_set_rf_sequence(dev, 0, rx2tx_events_ipa,
                                rx2tx_delays_ipa, ARRAY_SIZE(rx2tx_events_ipa));
@@ -2367,84 +2792,176 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
        b43_phy_maskset(dev, B43_NPHY_EPS_OVERRIDEI_0, 0x3FFF, 0x4000);
        b43_phy_maskset(dev, B43_NPHY_EPS_OVERRIDEI_1, 0x3FFF, 0x4000);
 
-       lpf_20 = b43_nphy_read_lpf_ctl(dev, 0x154);
-       lpf_40 = b43_nphy_read_lpf_ctl(dev, 0x159);
-       lpf_11b = b43_nphy_read_lpf_ctl(dev, 0x152);
+       for (core = 0; core < 2; core++) {
+               lpf_ofdm_20mhz[core] = b43_nphy_read_lpf_ctl(dev, 0x154 + core * 0x10);
+               lpf_ofdm_40mhz[core] = b43_nphy_read_lpf_ctl(dev, 0x159 + core * 0x10);
+               lpf_11b[core] = b43_nphy_read_lpf_ctl(dev, 0x152 + core * 0x10);
+       }
+
+       bcap_val = b43_radio_read(dev, R2057_RCCAL_BCAP_VAL);
+       scap_val = b43_radio_read(dev, R2057_RCCAL_SCAP_VAL);
+
        if (b43_nphy_ipa(dev)) {
-               if ((phy->radio_rev == 5 && phy->is_40mhz) ||
-                   phy->radio_rev == 7 || phy->radio_rev == 8) {
-                       bcap_val = b43_radio_read(dev, 0x16b);
-                       scap_val = b43_radio_read(dev, 0x16a);
-                       scap_val_11b = scap_val;
-                       bcap_val_11b = bcap_val;
-                       if (phy->radio_rev == 5 && phy->is_40mhz) {
-                               scap_val_11n_20 = scap_val;
-                               bcap_val_11n_20 = bcap_val;
-                               scap_val_11n_40 = bcap_val_11n_40 = 0xc;
+               bool ghz2 = b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ;
+
+               switch (phy->radio_rev) {
+               case 5:
+                       /* Check radio version (to be 0) by PHY rev for now */
+                       if (phy->rev == 8 && b43_is_40mhz(dev)) {
+                               for (core = 0; core < 2; core++) {
+                                       scap_val_11b[core] = scap_val;
+                                       bcap_val_11b[core] = bcap_val;
+                                       scap_val_11n_20[core] = scap_val;
+                                       bcap_val_11n_20[core] = bcap_val;
+                                       scap_val_11n_40[core] = 0xc;
+                                       bcap_val_11n_40[core] = 0xc;
+                               }
+
                                rccal_ovrd = true;
-                       } else { /* Rev 7/8 */
-                               lpf_20 = 4;
-                               lpf_11b = 1;
+                       }
+                       if (phy->rev == 9) {
+                               /* TODO: Radio version 1 (e.g. BCM5357B0) */
+                       }
+                       break;
+               case 7:
+               case 8:
+                       for (core = 0; core < 2; core++) {
+                               scap_val_11b[core] = scap_val;
+                               bcap_val_11b[core] = bcap_val;
+                               lpf_ofdm_20mhz[core] = 4;
+                               lpf_11b[core] = 1;
                                if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
-                                       scap_val_11n_20 = 0xc;
-                                       bcap_val_11n_20 = 0xc;
-                                       scap_val_11n_40 = 0xa;
-                                       bcap_val_11n_40 = 0xa;
+                                       scap_val_11n_20[core] = 0xc;
+                                       bcap_val_11n_20[core] = 0xc;
+                                       scap_val_11n_40[core] = 0xa;
+                                       bcap_val_11n_40[core] = 0xa;
                                } else {
-                                       scap_val_11n_20 = 0x14;
-                                       bcap_val_11n_20 = 0x14;
-                                       scap_val_11n_40 = 0xf;
-                                       bcap_val_11n_40 = 0xf;
+                                       scap_val_11n_20[core] = 0x14;
+                                       bcap_val_11n_20[core] = 0x14;
+                                       scap_val_11n_40[core] = 0xf;
+                                       bcap_val_11n_40[core] = 0xf;
                                }
-                               rccal_ovrd = true;
                        }
+
+                       rccal_ovrd = true;
+                       break;
+               case 9:
+                       for (core = 0; core < 2; core++) {
+                               bcap_val_11b[core] = bcap_val;
+                               scap_val_11b[core] = scap_val;
+                               lpf_11b[core] = 1;
+
+                               if (ghz2) {
+                                       bcap_val_11n_20[core] = bcap_val + 13;
+                                       scap_val_11n_20[core] = scap_val + 15;
+                               } else {
+                                       bcap_val_11n_20[core] = bcap_val + 14;
+                                       scap_val_11n_20[core] = scap_val + 15;
+                               }
+                               lpf_ofdm_20mhz[core] = 4;
+
+                               if (ghz2) {
+                                       bcap_val_11n_40[core] = bcap_val - 7;
+                                       scap_val_11n_40[core] = scap_val - 5;
+                               } else {
+                                       bcap_val_11n_40[core] = bcap_val + 2;
+                                       scap_val_11n_40[core] = scap_val + 4;
+                               }
+                               lpf_ofdm_40mhz[core] = 4;
+                       }
+
+                       rccal_ovrd = true;
+                       break;
+               case 14:
+                       for (core = 0; core < 2; core++) {
+                               bcap_val_11b[core] = bcap_val;
+                               scap_val_11b[core] = scap_val;
+                               lpf_11b[core] = 1;
+                       }
+
+                       bcap_val_11n_20[0] = bcap_val + 20;
+                       scap_val_11n_20[0] = scap_val + 20;
+                       lpf_ofdm_20mhz[0] = 3;
+
+                       bcap_val_11n_20[1] = bcap_val + 16;
+                       scap_val_11n_20[1] = scap_val + 16;
+                       lpf_ofdm_20mhz[1] = 3;
+
+                       bcap_val_11n_40[0] = bcap_val + 20;
+                       scap_val_11n_40[0] = scap_val + 20;
+                       lpf_ofdm_40mhz[0] = 4;
+
+                       bcap_val_11n_40[1] = bcap_val + 10;
+                       scap_val_11n_40[1] = scap_val + 10;
+                       lpf_ofdm_40mhz[1] = 4;
+
+                       rccal_ovrd = true;
+                       break;
                }
        } else {
                if (phy->radio_rev == 5) {
-                       lpf_20 = 1;
-                       lpf_40 = 3;
-                       bcap_val = b43_radio_read(dev, 0x16b);
-                       scap_val = b43_radio_read(dev, 0x16a);
-                       scap_val_11b = scap_val;
-                       bcap_val_11b = bcap_val;
-                       scap_val_11n_20 = 0x11;
-                       scap_val_11n_40 = 0x11;
-                       bcap_val_11n_20 = 0x13;
-                       bcap_val_11n_40 = 0x13;
+                       for (core = 0; core < 2; core++) {
+                               lpf_ofdm_20mhz[core] = 1;
+                               lpf_ofdm_40mhz[core] = 3;
+                               scap_val_11b[core] = scap_val;
+                               bcap_val_11b[core] = bcap_val;
+                               scap_val_11n_20[core] = 0x11;
+                               scap_val_11n_40[core] = 0x11;
+                               bcap_val_11n_20[core] = 0x13;
+                               bcap_val_11n_40[core] = 0x13;
+                       }
+
                        rccal_ovrd = true;
                }
        }
        if (rccal_ovrd) {
-               rx2tx_lut_20_11b = (bcap_val_11b << 8) |
-                                  (scap_val_11b << 3) |
-                                  lpf_11b;
-               rx2tx_lut_20_11n = (bcap_val_11n_20 << 8) |
-                                  (scap_val_11n_20 << 3) |
-                                  lpf_20;
-               rx2tx_lut_40_11n = (bcap_val_11n_40 << 8) |
-                                  (scap_val_11n_40 << 3) |
-                                  lpf_40;
+               u16 rx2tx_lut_20_11b[2], rx2tx_lut_20_11n[2], rx2tx_lut_40_11n[2];
+               u8 rx2tx_lut_extra = 1;
+
+               for (core = 0; core < 2; core++) {
+                       bcap_val_11b[core] = clamp_val(bcap_val_11b[core], 0, 0x1f);
+                       scap_val_11b[core] = clamp_val(scap_val_11b[core], 0, 0x1f);
+                       bcap_val_11n_20[core] = clamp_val(bcap_val_11n_20[core], 0, 0x1f);
+                       scap_val_11n_20[core] = clamp_val(scap_val_11n_20[core], 0, 0x1f);
+                       bcap_val_11n_40[core] = clamp_val(bcap_val_11n_40[core], 0, 0x1f);
+                       scap_val_11n_40[core] = clamp_val(scap_val_11n_40[core], 0, 0x1f);
+
+                       rx2tx_lut_20_11b[core] = (rx2tx_lut_extra << 13) |
+                                                (bcap_val_11b[core] << 8) |
+                                                (scap_val_11b[core] << 3) |
+                                                lpf_11b[core];
+                       rx2tx_lut_20_11n[core] = (rx2tx_lut_extra << 13) |
+                                                (bcap_val_11n_20[core] << 8) |
+                                                (scap_val_11n_20[core] << 3) |
+                                                lpf_ofdm_20mhz[core];
+                       rx2tx_lut_40_11n[core] = (rx2tx_lut_extra << 13) |
+                                                (bcap_val_11n_40[core] << 8) |
+                                                (scap_val_11n_40[core] << 3) |
+                                                lpf_ofdm_40mhz[core];
+               }
+
                for (core = 0; core < 2; core++) {
                        b43_ntab_write(dev, B43_NTAB16(7, 0x152 + core * 16),
-                                      rx2tx_lut_20_11b);
+                                      rx2tx_lut_20_11b[core]);
                        b43_ntab_write(dev, B43_NTAB16(7, 0x153 + core * 16),
-                                      rx2tx_lut_20_11n);
+                                      rx2tx_lut_20_11n[core]);
                        b43_ntab_write(dev, B43_NTAB16(7, 0x154 + core * 16),
-                                      rx2tx_lut_20_11n);
+                                      rx2tx_lut_20_11n[core]);
                        b43_ntab_write(dev, B43_NTAB16(7, 0x155 + core * 16),
-                                      rx2tx_lut_40_11n);
+                                      rx2tx_lut_40_11n[core]);
                        b43_ntab_write(dev, B43_NTAB16(7, 0x156 + core * 16),
-                                      rx2tx_lut_40_11n);
+                                      rx2tx_lut_40_11n[core]);
                        b43_ntab_write(dev, B43_NTAB16(7, 0x157 + core * 16),
-                                      rx2tx_lut_40_11n);
+                                      rx2tx_lut_40_11n[core]);
                        b43_ntab_write(dev, B43_NTAB16(7, 0x158 + core * 16),
-                                      rx2tx_lut_40_11n);
+                                      rx2tx_lut_40_11n[core]);
                        b43_ntab_write(dev, B43_NTAB16(7, 0x159 + core * 16),
-                                      rx2tx_lut_40_11n);
+                                      rx2tx_lut_40_11n[core]);
                }
-               b43_nphy_rf_ctl_override_rev7(dev, 16, 1, 3, false, 2);
        }
+
        b43_phy_write(dev, 0x32F, 0x3);
+
        if (phy->radio_rev == 4 || phy->radio_rev == 6)
                b43_nphy_rf_ctl_override_rev7(dev, 4, 1, 3, false, 0);
 
@@ -2492,7 +3009,8 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
                                                                0x7f);
                                }
                        }
-                       if (phy->radio_rev == 3) {
+                       switch (phy->radio_rev) {
+                       case 3:
                                for (core = 0; core < 2; core++) {
                                        if (core == 0) {
                                                b43_radio_write(dev, 0x64,
@@ -2518,17 +3036,34 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
                                                                0x3E);
                                        }
                                }
-                       } else if (phy->radio_rev == 7 || phy->radio_rev == 8) {
-                               if (!phy->is_40mhz) {
+                               break;
+                       case 7:
+                       case 8:
+                               if (!b43_is_40mhz(dev)) {
                                        b43_radio_write(dev, 0x5F, 0x14);
                                        b43_radio_write(dev, 0xE8, 0x12);
                                } else {
                                        b43_radio_write(dev, 0x5F, 0x16);
                                        b43_radio_write(dev, 0xE8, 0x16);
                                }
+                               break;
+                       case 14:
+                               for (core = 0; core < 2; core++) {
+                                       int o = core ? 0x85 : 0;
+
+                                       b43_radio_write(dev, o + R2057_IPA2G_CASCONV_CORE0, 0x13);
+                                       b43_radio_write(dev, o + R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, 0x21);
+                                       b43_radio_write(dev, o + R2057_IPA2G_BIAS_FILTER_CORE0, 0xff);
+                                       b43_radio_write(dev, o + R2057_PAD2G_IDACS_CORE0, 0x88);
+                                       b43_radio_write(dev, o + R2057_PAD2G_TUNE_PUS_CORE0, 0x23);
+                                       b43_radio_write(dev, o + R2057_IPA2G_IMAIN_CORE0, 0x16);
+                                       b43_radio_write(dev, o + R2057_PAD_BIAS_FILTER_BWS_CORE0, 0x3e);
+                                       b43_radio_write(dev, o + R2057_BACKUP1_CORE0, 0x10);
+                               }
+                               break;
                        }
                } else {
-                       u16 freq = phy->channel_freq;
+                       u16 freq = phy->chandef->chan->center_freq;
                        if ((freq >= 5180 && freq <= 5230) ||
                            (freq >= 5745 && freq <= 5805)) {
                                b43_radio_write(dev, 0x7D, 0xFF);
@@ -2573,8 +3108,8 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
                b43_phy_set(dev, B43_NPHY_AFECTL_OVER1, 0x1);
                b43_phy_mask(dev, B43_NPHY_AFECTL_C2, ~0x1);
                b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x1);
-               b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0x20);
-               b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0x20);
+               b43_ntab_write(dev, B43_NTAB16(8, 0x05), 0);
+               b43_ntab_write(dev, B43_NTAB16(8, 0x15), 0);
 
                b43_phy_mask(dev, B43_NPHY_AFECTL_C1, ~0x4);
                b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x4);
@@ -2585,20 +3120,20 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, 0x2);
 
        b43_ntab_write(dev, B43_NTAB32(16, 0x100), 20);
-       b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x138), 2, ntab7_138_146);
+       b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x138), 2, ntab7_138_146);
        b43_ntab_write(dev, B43_NTAB16(7, 0x141), 0x77);
-       b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x133), 3, ntab7_133);
-       b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x146), 2, ntab7_138_146);
+       b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x133), 3, ntab7_133);
+       b43_ntab_write_bulk(dev, B43_NTAB8(7, 0x146), 2, ntab7_138_146);
        b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77);
        b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77);
 
-       if (!phy->is_40mhz) {
-               b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x18D);
-               b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x18D);
-       } else {
-               b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x14D);
-               b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x14D);
-       }
+       b43_ntab_read_bulk(dev, B43_NTAB32(16, 0x02), 1, noise_tbl);
+       noise_tbl[1] = b43_is_40mhz(dev) ? 0x14D : 0x18D;
+       b43_ntab_write_bulk(dev, B43_NTAB32(16, 0x02), 2, noise_tbl);
+
+       b43_ntab_read_bulk(dev, B43_NTAB32(16, 0x7E), 1, noise_tbl);
+       noise_tbl[1] = b43_is_40mhz(dev) ? 0x14D : 0x18D;
+       b43_ntab_write_bulk(dev, B43_NTAB32(16, 0x7E), 2, noise_tbl);
 
        b43_nphy_gain_ctl_workarounds(dev);
 
@@ -2691,7 +3226,7 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
 
        b43_phy_maskset(dev, B43_NPHY_SGILTRNOFFSET, 0xF0FF, 0x0700);
 
-       if (!dev->phy.is_40mhz) {
+       if (!b43_is_40mhz(dev)) {
                b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D);
                b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D);
        } else {
@@ -2926,6 +3461,7 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
        b43_phy_set(dev, B43_NPHY_IQFLIP,
                    B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
 
+       /* TODO: rev19+ */
        if (dev->phy.rev >= 7)
                b43_nphy_workarounds_rev7plus(dev);
        else if (dev->phy.rev >= 3)
@@ -2946,12 +3482,13 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
  * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone
  */
 static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val,
-                               bool iqmode, bool dac_test)
+                           bool iqmode, bool dac_test, bool modify_bbmult)
 {
        u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test);
        if (samp == 0)
                return -1;
-       b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test);
+       b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test,
+                            modify_bbmult);
        return 0;
 }
 
@@ -2986,6 +3523,7 @@ static void b43_nphy_update_txrx_chain(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/stop-playback */
 static void b43_nphy_stop_playback(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        u16 tmp;
 
@@ -3006,6 +3544,15 @@ static void b43_nphy_stop_playback(struct b43_wldev *dev)
                nphy->bb_mult_save = 0;
        }
 
+       if (phy->rev >= 7 && nphy->lpf_bw_overrode_for_sample_play) {
+               if (phy->rev >= 19)
+                       b43_nphy_rf_ctl_override_rev19(dev, 0x80, 0, 0, true,
+                                                      1);
+               else
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x80, 0, 0, true, 1);
+               nphy->lpf_bw_overrode_for_sample_play = false;
+       }
+
        if (nphy->hang_avoid)
                b43_nphy_stay_in_carrier_search(dev, 0);
 }
@@ -3015,16 +3562,23 @@ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core,
                                        struct nphy_txgains target,
                                        struct nphy_iqcal_params *params)
 {
+       struct b43_phy *phy = &dev->phy;
        int i, j, indx;
        u16 gain;
 
        if (dev->phy.rev >= 3) {
+               params->tx_lpf = target.tx_lpf[core]; /* Rev 7+ */
                params->txgm = target.txgm[core];
                params->pga = target.pga[core];
                params->pad = target.pad[core];
                params->ipa = target.ipa[core];
-               params->cal_gain = (params->txgm << 12) | (params->pga << 8) |
-                                       (params->pad << 4) | (params->ipa);
+               if (phy->rev >= 19) {
+                       /* TODO */
+               } else if (phy->rev >= 7) {
+                       params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 3) | (params->ipa) | (params->tx_lpf << 15);
+               } else {
+                       params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 4) | (params->ipa);
+               }
                for (j = 0; j < 5; j++)
                        params->ncorr[j] = 0x79;
        } else {
@@ -3065,6 +3619,7 @@ static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev,
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlEnable */
 static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        u8 i;
        u16 bmask, val, tmp;
@@ -3114,7 +3669,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
                        b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3,
                                ~B43_NPHY_BPHY_CTL3_SCALE, 0x5A);
 
-               if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+               if (dev->phy.rev < 2 && b43_is_40mhz(dev))
                        b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW);
        } else {
                b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84,
@@ -3134,12 +3689,25 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
                b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~(bmask), val);
 
                if (band == IEEE80211_BAND_5GHZ) {
-                       b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
-                                       ~B43_NPHY_TXPCTL_CMD_INIT, 0x64);
-                       if (dev->phy.rev > 1)
+                       if (phy->rev >= 19) {
+                               /* TODO */
+                       } else if (phy->rev >= 7) {
+                               b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
+                                               ~B43_NPHY_TXPCTL_CMD_INIT,
+                                               0x32);
                                b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT,
                                                ~B43_NPHY_TXPCTL_INIT_PIDXI1,
+                                               0x32);
+                       } else {
+                               b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
+                                               ~B43_NPHY_TXPCTL_CMD_INIT,
                                                0x64);
+                               if (phy->rev > 1)
+                                       b43_phy_maskset(dev,
+                                                       B43_NPHY_TXPCTL_INIT,
+                                                       ~B43_NPHY_TXPCTL_INIT_PIDXI1,
+                                                       0x64);
+                       }
                }
 
                if (dev->phy.rev >= 3) {
@@ -3156,6 +3724,10 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
                        }
                }
 
+               if (phy->rev >= 7) {
+                       /* TODO */
+               }
+
                if (dev->phy.rev >= 3) {
                        b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x100);
                        b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x100);
@@ -3168,7 +3740,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
                else if (dev->phy.rev < 2)
                        b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40);
 
-               if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+               if (dev->phy.rev < 2 && b43_is_40mhz(dev))
                        b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW);
 
                if (b43_nphy_ipa(dev)) {
@@ -3184,18 +3756,20 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrFix */
 static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
        u8 txpi[2], bbmult, i;
        u16 tmp, radio_gain, dac_gain;
-       u16 freq = dev->phy.channel_freq;
+       u16 freq = phy->chandef->chan->center_freq;
        u32 txgain;
        /* u32 gaintbl; rev3+ */
 
        if (nphy->hang_avoid)
                b43_nphy_stay_in_carrier_search(dev, 1);
 
+       /* TODO: rev19+ */
        if (dev->phy.rev >= 7) {
                txpi[0] = txpi[1] = 30;
        } else if (dev->phy.rev >= 3) {
@@ -3234,7 +3808,11 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
        */
 
        for (i = 0; i < 2; i++) {
-               txgain = *(b43_nphy_get_tx_gain_table(dev) + txpi[i]);
+               const u32 *table = b43_nphy_get_tx_gain_table(dev);
+
+               if (!table)
+                       break;
+               txgain = *(table + txpi[i]);
 
                if (dev->phy.rev >= 3)
                        radio_gain = (txgain >> 16) & 0x1FFFF;
@@ -3294,7 +3872,9 @@ static void b43_nphy_ipa_internal_tssi_setup(struct b43_wldev *dev)
        u8 core;
        u16 r; /* routing */
 
-       if (phy->rev >= 7) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
                for (core = 0; core < 2; core++) {
                        r = core ? 0x190 : 0x170;
                        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
@@ -3377,29 +3957,38 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
        u32 tmp;
        s32 rssi[4] = { };
 
-       /* TODO: check if we can transmit */
+       if (phy->chandef->chan->flags & IEEE80211_CHAN_NO_IR)
+               return;
 
        if (b43_nphy_ipa(dev))
                b43_nphy_ipa_internal_tssi_setup(dev);
 
-       if (phy->rev >= 7)
-               b43_nphy_rf_ctl_override_rev7(dev, 0x2000, 0, 3, false, 0);
+       if (phy->rev >= 19)
+               b43_nphy_rf_ctl_override_rev19(dev, 0x1000, 0, 3, false, 0);
+       else if (phy->rev >= 7)
+               b43_nphy_rf_ctl_override_rev7(dev, 0x1000, 0, 3, false, 0);
        else if (phy->rev >= 3)
                b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false);
 
        b43_nphy_stop_playback(dev);
-       b43_nphy_tx_tone(dev, 0xFA0, 0, false, false);
+       b43_nphy_tx_tone(dev, 4000, 0, false, false, false);
        udelay(20);
        tmp = b43_nphy_poll_rssi(dev, N_RSSI_TSSI_2G, rssi, 1);
        b43_nphy_stop_playback(dev);
+
        b43_nphy_rssi_select(dev, 0, N_RSSI_W1);
 
-       if (phy->rev >= 7)
-               b43_nphy_rf_ctl_override_rev7(dev, 0x2000, 0, 3, true, 0);
+       if (phy->rev >= 19)
+               b43_nphy_rf_ctl_override_rev19(dev, 0x1000, 0, 3, true, 0);
+       else if (phy->rev >= 7)
+               b43_nphy_rf_ctl_override_rev7(dev, 0x1000, 0, 3, true, 0);
        else if (phy->rev >= 3)
                b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, true);
 
-       if (phy->rev >= 3) {
+       if (phy->rev >= 19) {
+               /* TODO */
+               return;
+       } else if (phy->rev >= 3) {
                nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 24) & 0xFF;
                nphy->pwr_ctl_info[1].idle_tssi_5g = (tmp >> 8) & 0xFF;
        } else {
@@ -3439,21 +4028,21 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
                delta = 0;
                switch (stf_mode) {
                case 0:
-                       if (dev->phy.is_40mhz && dev->phy.rev >= 5) {
+                       if (b43_is_40mhz(dev) && dev->phy.rev >= 5) {
                                idx = 68;
                        } else {
                                delta = 1;
-                               idx = dev->phy.is_40mhz ? 52 : 4;
+                               idx = b43_is_40mhz(dev) ? 52 : 4;
                        }
                        break;
                case 1:
-                       idx = dev->phy.is_40mhz ? 76 : 28;
+                       idx = b43_is_40mhz(dev) ? 76 : 28;
                        break;
                case 2:
-                       idx = dev->phy.is_40mhz ? 84 : 36;
+                       idx = b43_is_40mhz(dev) ? 84 : 36;
                        break;
                case 3:
-                       idx = dev->phy.is_40mhz ? 92 : 44;
+                       idx = b43_is_40mhz(dev) ? 92 : 44;
                        break;
                }
 
@@ -3474,6 +4063,7 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlSetup */
 static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
@@ -3483,7 +4073,7 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
        s32 num, den, pwr;
        u32 regval[64];
 
-       u16 freq = dev->phy.channel_freq;
+       u16 freq = phy->chandef->chan->center_freq;
        u16 tmp;
        u16 r; /* routing */
        u8 i, c;
@@ -3590,7 +4180,9 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
                udelay(1);
        }
 
-       if (dev->phy.rev >= 7) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
                b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
                                ~B43_NPHY_TXPCTL_CMD_INIT, 0x19);
                b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT,
@@ -3647,27 +4239,36 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev)
        int i;
 
        table = b43_nphy_get_tx_gain_table(dev);
+       if (!table)
+               return;
+
        b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table);
        b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table);
 
-       if (phy->rev >= 3) {
+       if (phy->rev < 3)
+               return;
+
 #if 0
-               nphy->gmval = (table[0] >> 16) & 0x7000;
+       nphy->gmval = (table[0] >> 16) & 0x7000;
 #endif
 
-               for (i = 0; i < 128; i++) {
+       for (i = 0; i < 128; i++) {
+               if (phy->rev >= 19) {
+                       /* TODO */
+                       return;
+               } else if (phy->rev >= 7) {
+                       /* TODO */
+                       return;
+               } else {
                        pga_gain = (table[i] >> 24) & 0xF;
                        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
-                               rfpwr_offset =
-                                b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain];
+                               rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain];
                        else
-                               rfpwr_offset =
-                                0; /* FIXME */
-                       b43_ntab_write(dev, B43_NTAB32(26, 576 + i),
-                                      rfpwr_offset);
-                       b43_ntab_write(dev, B43_NTAB32(27, 576 + i),
-                                      rfpwr_offset);
+                               rfpwr_offset = 0; /* FIXME */
                }
+
+               b43_ntab_write(dev, B43_NTAB32(26, 576 + i), rfpwr_offset);
+               b43_ntab_write(dev, B43_NTAB32(27, 576 + i), rfpwr_offset);
        }
 }
 
@@ -3684,7 +4285,9 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable)
                nphy->rfctrl_intc2_save = b43_phy_read(dev,
                                                       B43_NPHY_RFCTL_INTC2);
                band = b43_current_band(dev->wl);
-               if (dev->phy.rev >= 3) {
+               if (dev->phy.rev >= 7) {
+                       tmp = 0x1480;
+               } else if (dev->phy.rev >= 3) {
                        if (band == IEEE80211_BAND_5GHZ)
                                tmp = 0x600;
                        else
@@ -3705,21 +4308,28 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable)
        }
 }
 
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */
-static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev)
+/*
+ * TX low-pass filter bandwidth setup
+ * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw
+ */
+static void b43_nphy_tx_lpf_bw(struct b43_wldev *dev)
 {
        u16 tmp;
 
-       if (dev->phy.rev >= 3) {
-               if (b43_nphy_ipa(dev)) {
-                       tmp = 4;
-                       b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2,
-                             (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp);
-               }
+       if (dev->phy.rev < 3 || dev->phy.rev >= 7)
+               return;
+
+       if (b43_nphy_ipa(dev))
+               tmp = b43_is_40mhz(dev) ? 5 : 4;
+       else
+               tmp = b43_is_40mhz(dev) ? 3 : 1;
+       b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2,
+                     (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp);
 
-               tmp = 1;
+       if (b43_nphy_ipa(dev)) {
+               tmp = b43_is_40mhz(dev) ? 4 : 1;
                b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2,
-                             (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp);
+                             (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp);
        }
 }
 
@@ -3992,7 +4602,7 @@ static void b43_nphy_spur_workaround(struct b43_wldev *dev)
 
        if (nphy->gband_spurwar_en) {
                /* TODO: N PHY Adjust Analog Pfbw (7) */
-               if (channel == 11 && dev->phy.is_40mhz)
+               if (channel == 11 && b43_is_40mhz(dev))
                        ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/
                else
                        ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
@@ -4124,7 +4734,13 @@ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev)
                rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G;
        }
 
-       if (dev->phy.rev >= 7) {
+       if (dev->phy.rev >= 19) {
+               /* TODO */
+       } else if (dev->phy.rev >= 7) {
+               b43_radio_maskset(dev, R2057_NB_MASTER_CORE0, ~R2057_VCM_MASK,
+                                 rssical_radio_regs[0]);
+               b43_radio_maskset(dev, R2057_NB_MASTER_CORE1, ~R2057_VCM_MASK,
+                                 rssical_radio_regs[1]);
        } else {
                b43_radio_maskset(dev, B2056_RX0 | B2056_RX_RSSI_MISC, 0xE3,
                                  rssical_radio_regs[0]);
@@ -4148,15 +4764,78 @@ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]);
 }
 
+static void b43_nphy_tx_cal_radio_setup_rev19(struct b43_wldev *dev)
+{
+       /* TODO */
+}
+
+static void b43_nphy_tx_cal_radio_setup_rev7(struct b43_wldev *dev)
+{
+       struct b43_phy *phy = &dev->phy;
+       struct b43_phy_n *nphy = dev->phy.n;
+       u16 *save = nphy->tx_rx_cal_radio_saveregs;
+       int core, off;
+       u16 r, tmp;
+
+       for (core = 0; core < 2; core++) {
+               r = core ? 0x20 : 0;
+               off = core * 11;
+
+               save[off + 0] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MASTER);
+               save[off + 1] = b43_radio_read(dev, r + R2057_TX0_IQCAL_VCM_HG);
+               save[off + 2] = b43_radio_read(dev, r + R2057_TX0_IQCAL_IDAC);
+               save[off + 3] = b43_radio_read(dev, r + R2057_TX0_TSSI_VCM);
+               save[off + 4] = 0;
+               save[off + 5] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MUX);
+               if (phy->radio_rev != 5)
+                       save[off + 6] = b43_radio_read(dev, r + R2057_TX0_TSSIA);
+               save[off + 7] = b43_radio_read(dev, r + R2057_TX0_TSSIG);
+               save[off + 8] = b43_radio_read(dev, r + R2057_TX0_TSSI_MISC1);
+
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0xA);
+                       b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43);
+                       b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55);
+                       b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0);
+                       b43_radio_write(dev, r + R2057_TX0_TSSIG, 0);
+                       if (nphy->use_int_tx_iq_lo_cal) {
+                               b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x4);
+                               tmp = true ? 0x31 : 0x21; /* TODO */
+                               b43_radio_write(dev, r + R2057_TX0_TSSIA, tmp);
+                       }
+                       b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0x00);
+               } else {
+                       b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0x6);
+                       b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43);
+                       b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55);
+                       b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0);
+
+                       if (phy->radio_rev != 5)
+                               b43_radio_write(dev, r + R2057_TX0_TSSIA, 0);
+                       if (nphy->use_int_tx_iq_lo_cal) {
+                               b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x6);
+                               tmp = true ? 0x31 : 0x21; /* TODO */
+                               b43_radio_write(dev, r + R2057_TX0_TSSIG, tmp);
+                       }
+                       b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0);
+               }
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */
 static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        u16 *save = nphy->tx_rx_cal_radio_saveregs;
        u16 tmp;
        u8 offset, i;
 
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               b43_nphy_tx_cal_radio_setup_rev19(dev);
+       } else if (phy->rev >= 7) {
+               b43_nphy_tx_cal_radio_setup_rev7(dev);
+       } else if (phy->rev >= 3) {
            for (i = 0; i < 2; i++) {
                tmp = (i == 0) ? 0x2000 : 0x3000;
                offset = i * 11;
@@ -4265,41 +4944,61 @@ static void b43_nphy_update_tx_cal_ladder(struct b43_wldev *dev, u16 core)
        }
 }
 
+static void b43_nphy_pa_set_tx_dig_filter(struct b43_wldev *dev, u16 offset,
+                                         const s16 *filter)
+{
+       int i;
+
+       offset = B43_PHY_N(offset);
+
+       for (i = 0; i < 15; i++, offset++)
+               b43_phy_write(dev, offset, filter[i]);
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ExtPaSetTxDigiFilts */
 static void b43_nphy_ext_pa_set_tx_dig_filters(struct b43_wldev *dev)
 {
-       int i;
-       for (i = 0; i < 15; i++)
-               b43_phy_write(dev, B43_PHY_N(0x2C5 + i),
-                               tbl_tx_filter_coef_rev4[2][i]);
+       b43_nphy_pa_set_tx_dig_filter(dev, 0x2C5,
+                                     tbl_tx_filter_coef_rev4[2]);
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IpaSetTxDigiFilts */
 static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev)
 {
-       int i, j;
        /* B43_NPHY_TXF_20CO_S0A1, B43_NPHY_TXF_40CO_S0A1, unknown */
        static const u16 offset[] = { 0x186, 0x195, 0x2C5 };
+       static const s16 dig_filter_phy_rev16[] = {
+               -375, 136, -407, 208, -1527,
+               956, 93, 186, 93, 230,
+               -44, 230, 201, -191, 201,
+       };
+       int i;
 
        for (i = 0; i < 3; i++)
-               for (j = 0; j < 15; j++)
-                       b43_phy_write(dev, B43_PHY_N(offset[i] + j),
-                                       tbl_tx_filter_coef_rev4[i][j]);
-
-       if (dev->phy.is_40mhz) {
-               for (j = 0; j < 15; j++)
-                       b43_phy_write(dev, B43_PHY_N(offset[0] + j),
-                                       tbl_tx_filter_coef_rev4[3][j]);
-       } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
-               for (j = 0; j < 15; j++)
-                       b43_phy_write(dev, B43_PHY_N(offset[0] + j),
-                                       tbl_tx_filter_coef_rev4[5][j]);
-       }
-
-       if (dev->phy.channel == 14)
-               for (j = 0; j < 15; j++)
-                       b43_phy_write(dev, B43_PHY_N(offset[0] + j),
-                                       tbl_tx_filter_coef_rev4[6][j]);
+               b43_nphy_pa_set_tx_dig_filter(dev, offset[i],
+                                             tbl_tx_filter_coef_rev4[i]);
+
+       /* Verified with BCM43227 and BCM43228 */
+       if (dev->phy.rev == 16)
+               b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16);
+
+       if (dev->dev->chip_id == BCMA_CHIP_ID_BCM43217) {
+               b43_nphy_pa_set_tx_dig_filter(dev, 0x186, dig_filter_phy_rev16);
+               b43_nphy_pa_set_tx_dig_filter(dev, 0x195,
+                                             tbl_tx_filter_coef_rev4[1]);
+       }
+
+       if (b43_is_40mhz(dev)) {
+               b43_nphy_pa_set_tx_dig_filter(dev, 0x186,
+                                             tbl_tx_filter_coef_rev4[3]);
+       } else {
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+                       b43_nphy_pa_set_tx_dig_filter(dev, 0x186,
+                                                     tbl_tx_filter_coef_rev4[5]);
+               if (dev->phy.channel == 14)
+                       b43_nphy_pa_set_tx_dig_filter(dev, 0x186,
+                                                     tbl_tx_filter_coef_rev4[6]);
+       }
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetTxGain */
@@ -4321,7 +5020,13 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
                        b43_nphy_stay_in_carrier_search(dev, false);
 
                for (i = 0; i < 2; ++i) {
-                       if (dev->phy.rev >= 3) {
+                       if (dev->phy.rev >= 7) {
+                               target.ipa[i] = curr_gain[i] & 0x0007;
+                               target.pad[i] = (curr_gain[i] & 0x00F8) >> 3;
+                               target.pga[i] = (curr_gain[i] & 0x0F00) >> 8;
+                               target.txgm[i] = (curr_gain[i] & 0x7000) >> 12;
+                               target.tx_lpf[i] = (curr_gain[i] & 0x8000) >> 15;
+                       } else if (dev->phy.rev >= 3) {
                                target.ipa[i] = curr_gain[i] & 0x000F;
                                target.pad[i] = (curr_gain[i] & 0x00F0) >> 4;
                                target.pga[i] = (curr_gain[i] & 0x0F00) >> 8;
@@ -4345,7 +5050,16 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
 
                for (i = 0; i < 2; ++i) {
                        table = b43_nphy_get_tx_gain_table(dev);
-                       if (dev->phy.rev >= 3) {
+                       if (!table)
+                               break;
+
+                       if (dev->phy.rev >= 7) {
+                               target.ipa[i] = (table[index[i]] >> 16) & 0x7;
+                               target.pad[i] = (table[index[i]] >> 19) & 0x1F;
+                               target.pga[i] = (table[index[i]] >> 24) & 0xF;
+                               target.txgm[i] = (table[index[i]] >> 28) & 0x7;
+                               target.tx_lpf[i] = (table[index[i]] >> 31) & 0x1;
+                       } else if (dev->phy.rev >= 3) {
                                target.ipa[i] = (table[index[i]] >> 16) & 0xF;
                                target.pad[i] = (table[index[i]] >> 20) & 0xF;
                                target.pga[i] = (table[index[i]] >> 24) & 0xF;
@@ -4394,6 +5108,8 @@ static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhySetup */
 static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
+       struct b43_phy_n *nphy = dev->phy.n;
        u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs;
        u16 tmp;
 
@@ -4425,7 +5141,12 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
                regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
                regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
 
-               b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, 1, 3);
+               if (!nphy->use_int_tx_iq_lo_cal)
+                       b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA,
+                                                     1, 3);
+               else
+                       b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA,
+                                                     0, 3);
                b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 2, 1);
                b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 8, 2);
 
@@ -4433,6 +5154,33 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
                regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1);
                b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001);
                b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001);
+
+               tmp = b43_nphy_read_lpf_ctl(dev, 0);
+               if (phy->rev >= 19)
+                       b43_nphy_rf_ctl_override_rev19(dev, 0x80, tmp, 0, false,
+                                                      1);
+               else if (phy->rev >= 7)
+                       b43_nphy_rf_ctl_override_rev7(dev, 0x80, tmp, 0, false,
+                                                     1);
+
+               if (nphy->use_int_tx_iq_lo_cal && true /* FIXME */) {
+                       if (phy->rev >= 19) {
+                               b43_nphy_rf_ctl_override_rev19(dev, 0x8, 0, 0x3,
+                                                              false, 0);
+                       } else if (phy->rev >= 8) {
+                               b43_nphy_rf_ctl_override_rev7(dev, 0x8, 0, 0x3,
+                                                             false, 0);
+                       } else if (phy->rev == 7) {
+                               b43_radio_maskset(dev, R2057_OVR_REG0, 1 << 4, 1 << 4);
+                               if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+                                       b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE0, ~1, 0);
+                                       b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE1, ~1, 0);
+                               } else {
+                                       b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE0, ~1, 0);
+                                       b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE1, ~1, 0);
+                               }
+                       }
+               }
        } else {
                b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, 0xA000);
                b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, 0xA000);
@@ -4461,6 +5209,7 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */
 static void b43_nphy_save_cal(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
 
        struct b43_phy_n_iq_comp *rxcal_coeffs = NULL;
@@ -4485,7 +5234,26 @@ static void b43_nphy_save_cal(struct b43_wldev *dev)
 
        b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs);
        /* TODO use some definitions */
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
+               txcal_radio_regs[0] = b43_radio_read(dev,
+                                                    R2057_TX0_LOFT_FINE_I);
+               txcal_radio_regs[1] = b43_radio_read(dev,
+                                                    R2057_TX0_LOFT_FINE_Q);
+               txcal_radio_regs[4] = b43_radio_read(dev,
+                                                    R2057_TX0_LOFT_COARSE_I);
+               txcal_radio_regs[5] = b43_radio_read(dev,
+                                                    R2057_TX0_LOFT_COARSE_Q);
+               txcal_radio_regs[2] = b43_radio_read(dev,
+                                                    R2057_TX1_LOFT_FINE_I);
+               txcal_radio_regs[3] = b43_radio_read(dev,
+                                                    R2057_TX1_LOFT_FINE_Q);
+               txcal_radio_regs[6] = b43_radio_read(dev,
+                                                    R2057_TX1_LOFT_COARSE_I);
+               txcal_radio_regs[7] = b43_radio_read(dev,
+                                                    R2057_TX1_LOFT_COARSE_Q);
+       } else if (phy->rev >= 3) {
                txcal_radio_regs[0] = b43_radio_read(dev, 0x2021);
                txcal_radio_regs[1] = b43_radio_read(dev, 0x2022);
                txcal_radio_regs[2] = b43_radio_read(dev, 0x3021);
@@ -4500,8 +5268,9 @@ static void b43_nphy_save_cal(struct b43_wldev *dev)
                txcal_radio_regs[2] = b43_radio_read(dev, 0x8D);
                txcal_radio_regs[3] = b43_radio_read(dev, 0xBC);
        }
-       iqcal_chanspec->center_freq = dev->phy.channel_freq;
-       iqcal_chanspec->channel_type = dev->phy.channel_type;
+       iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq;
+       iqcal_chanspec->channel_type =
+                               cfg80211_get_chandef_type(dev->phy.chandef);
        b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table);
 
        if (nphy->hang_avoid)
@@ -4511,6 +5280,7 @@ static void b43_nphy_save_cal(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */
 static void b43_nphy_restore_cal(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
 
        u16 coef[4];
@@ -4558,7 +5328,26 @@ static void b43_nphy_restore_cal(struct b43_wldev *dev)
        }
 
        /* TODO use some definitions */
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
+               b43_radio_write(dev, R2057_TX0_LOFT_FINE_I,
+                               txcal_radio_regs[0]);
+               b43_radio_write(dev, R2057_TX0_LOFT_FINE_Q,
+                               txcal_radio_regs[1]);
+               b43_radio_write(dev, R2057_TX0_LOFT_COARSE_I,
+                               txcal_radio_regs[4]);
+               b43_radio_write(dev, R2057_TX0_LOFT_COARSE_Q,
+                               txcal_radio_regs[5]);
+               b43_radio_write(dev, R2057_TX1_LOFT_FINE_I,
+                               txcal_radio_regs[2]);
+               b43_radio_write(dev, R2057_TX1_LOFT_FINE_Q,
+                               txcal_radio_regs[3]);
+               b43_radio_write(dev, R2057_TX1_LOFT_COARSE_I,
+                               txcal_radio_regs[6]);
+               b43_radio_write(dev, R2057_TX1_LOFT_COARSE_Q,
+                               txcal_radio_regs[7]);
+       } else if (phy->rev >= 3) {
                b43_radio_write(dev, 0x2021, txcal_radio_regs[0]);
                b43_radio_write(dev, 0x2022, txcal_radio_regs[1]);
                b43_radio_write(dev, 0x3021, txcal_radio_regs[2]);
@@ -4581,6 +5370,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                                struct nphy_txgains target,
                                bool full, bool mphase)
 {
+       struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        int i;
        int error = 0;
@@ -4621,7 +5411,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                (dev->phy.rev == 5 && nphy->ipa2g_on &&
                b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ);
        if (phy6or5x) {
-               if (dev->phy.is_40mhz) {
+               if (b43_is_40mhz(dev)) {
                        b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18,
                                        tbl_tx_iqlo_cal_loft_ladder_40);
                        b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18,
@@ -4634,18 +5424,24 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                }
        }
 
-       b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9);
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
+               b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AD9);
+       } else {
+               b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9);
+       }
 
-       if (!dev->phy.is_40mhz)
+       if (!b43_is_40mhz(dev))
                freq = 2500;
        else
                freq = 5000;
 
        if (nphy->mphase_cal_phase_id > 2)
-               b43_nphy_run_samples(dev, (dev->phy.is_40mhz ? 40 : 20) * 8,
-                                       0xFFFF, 0, true, false);
+               b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8,
+                                    0xFFFF, 0, true, false, false);
        else
-               error = b43_nphy_tx_tone(dev, freq, 250, true, false);
+               error = b43_nphy_tx_tone(dev, freq, 250, true, false, false);
 
        if (error == 0) {
                if (nphy->mphase_cal_phase_id > 2) {
@@ -4773,9 +5569,9 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                                                nphy->txiqlocal_bestc);
                        nphy->txiqlocal_coeffsvalid = true;
                        nphy->txiqlocal_chanspec.center_freq =
-                                                       dev->phy.channel_freq;
+                                               phy->chandef->chan->center_freq;
                        nphy->txiqlocal_chanspec.channel_type =
-                                                       dev->phy.channel_type;
+                                       cfg80211_get_chandef_type(phy->chandef);
                } else {
                        length = 11;
                        if (dev->phy.rev < 3)
@@ -4811,8 +5607,8 @@ static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev)
        bool equal = true;
 
        if (!nphy->txiqlocal_coeffsvalid ||
-           nphy->txiqlocal_chanspec.center_freq != dev->phy.channel_freq ||
-           nphy->txiqlocal_chanspec.channel_type != dev->phy.channel_type)
+           nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq ||
+           nphy->txiqlocal_chanspec.channel_type != cfg80211_get_chandef_type(dev->phy.chandef))
                return;
 
        b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
@@ -4968,11 +5764,11 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
                        if (playtone) {
                                ret = b43_nphy_tx_tone(dev, 4000,
                                                (nphy->rxcalparams & 0xFFFF),
-                                               false, false);
+                                               false, false, true);
                                playtone = false;
                        } else {
-                               b43_nphy_run_samples(dev, 160, 0xFFFF, 0,
-                                                       false, false);
+                               b43_nphy_run_samples(dev, 160, 0xFFFF, 0, false,
+                                                    false, true);
                        }
 
                        if (ret == 0) {
@@ -5028,6 +5824,9 @@ static int b43_nphy_rev3_cal_rx_iq(struct b43_wldev *dev,
 static int b43_nphy_cal_rx_iq(struct b43_wldev *dev,
                        struct nphy_txgains target, u8 type, bool debug)
 {
+       if (dev->phy.rev >= 7)
+               type = 0;
+
        if (dev->phy.rev >= 3)
                return b43_nphy_rev3_cal_rx_iq(dev, target, type, debug);
        else
@@ -5114,6 +5913,9 @@ static void b43_nphy_bphy_init(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SuperSwitchInit */
 static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init)
 {
+       if (dev->phy.rev >= 7)
+               return;
+
        if (dev->phy.rev >= 3) {
                if (!init)
                        return;
@@ -5189,6 +5991,10 @@ static int b43_phy_initn(struct b43_wldev *dev)
 #endif
                }
        }
+       nphy->use_int_tx_iq_lo_cal = b43_nphy_ipa(dev) ||
+               phy->rev >= 7 ||
+               (phy->rev >= 5 &&
+                sprom->boardflags2_hi & B43_BFH2_INTERNDET_TXIQCAL);
        nphy->deaf_count = 0;
        b43_nphy_tables_init(dev);
        nphy->crsminpwr_adjusted = false;
@@ -5198,6 +6004,16 @@ static int b43_phy_initn(struct b43_wldev *dev)
        if (dev->phy.rev >= 3) {
                b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0);
                b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0);
+               if (phy->rev >= 7) {
+                       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0);
+                       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0);
+                       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0);
+                       b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0);
+               }
+               if (phy->rev >= 19) {
+                       /* TODO */
+               }
+
                b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, 0);
                b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, 0);
        } else {
@@ -5235,7 +6051,9 @@ static int b43_phy_initn(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x50);
        b43_phy_write(dev, B43_NPHY_TXRIFS_FRDEL, 0x30);
 
-       b43_nphy_update_mimo_config(dev, nphy->preamble_override);
+       if (phy->rev < 8)
+               b43_nphy_update_mimo_config(dev, nphy->preamble_override);
+
        b43_nphy_update_txrx_chain(dev);
 
        if (phy->rev < 2) {
@@ -5267,10 +6085,12 @@ static int b43_phy_initn(struct b43_wldev *dev)
 
        b43_mac_phy_clock_set(dev, true);
 
-       b43_nphy_pa_override(dev, false);
-       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
-       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
-       b43_nphy_pa_override(dev, true);
+       if (phy->rev < 7) {
+               b43_nphy_pa_override(dev, false);
+               b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
+               b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
+               b43_nphy_pa_override(dev, true);
+       }
 
        b43_nphy_classifier(dev, 0, 0);
        b43_nphy_read_clip_detection(dev, clip);
@@ -5344,7 +6164,7 @@ static int b43_phy_initn(struct b43_wldev *dev)
        b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320);
        if (phy->rev >= 3 && phy->rev <= 6)
                b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032);
-       b43_nphy_tx_lp_fbw(dev);
+       b43_nphy_tx_lpf_bw(dev);
        if (phy->rev >= 3)
                b43_nphy_spur_workaround(dev);
 
@@ -5393,23 +6213,23 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev,
        struct b43_phy *phy = &dev->phy;
        struct b43_phy_n *nphy = dev->phy.n;
        int ch = new_channel->hw_value;
-
-       u16 old_band_5ghz;
        u16 tmp16;
 
-       old_band_5ghz =
-               b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ;
-       if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) {
+       if (new_channel->band == IEEE80211_BAND_5GHZ) {
                tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR);
                b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4);
-               b43_phy_set(dev, B43_PHY_B_BBCFG, 0xC000);
+               /* Put BPHY in the reset */
+               b43_phy_set(dev, B43_PHY_B_BBCFG,
+                           B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX);
                b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16);
                b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ);
-       } else if (new_channel->band == IEEE80211_BAND_2GHZ && old_band_5ghz) {
+       } else if (new_channel->band == IEEE80211_BAND_2GHZ) {
                b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ);
                tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR);
                b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4);
-               b43_phy_mask(dev, B43_PHY_B_BBCFG, 0x3FFF);
+               /* Take BPHY out of the reset */
+               b43_phy_mask(dev, B43_PHY_B_BBCFG,
+                            (u16)~(B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX));
                b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16);
        }
 
@@ -5430,35 +6250,49 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev,
        if (dev->phy.rev < 3)
                b43_nphy_adjust_lna_gain_table(dev);
 
-       b43_nphy_tx_lp_fbw(dev);
+       b43_nphy_tx_lpf_bw(dev);
 
        if (dev->phy.rev >= 3 &&
            dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) {
-               bool avoid = false;
+               u8 spuravoid = 0;
+
                if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) {
-                       avoid = true;
-               } else if (!b43_channel_type_is_40mhz(phy->channel_type)) {
-                       if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14)
-                               avoid = true;
-               } else { /* 40MHz */
-                       if (nphy->aband_spurwar_en &&
-                           (ch == 38 || ch == 102 || ch == 118))
-                               avoid = dev->dev->chip_id == 0x4716;
+                       spuravoid = 1;
+               } else if (phy->rev >= 19) {
+                       /* TODO */
+               } else if (phy->rev >= 18) {
+                       /* TODO */
+               } else if (phy->rev >= 17) {
+                       /* TODO: Off for channels 1-11, but check 12-14! */
+               } else if (phy->rev >= 16) {
+                       /* TODO: Off for 2 GHz, but check 5 GHz! */
+               } else if (phy->rev >= 7) {
+                       if (!b43_is_40mhz(dev)) { /* 20MHz */
+                               if (ch == 13 || ch == 14 || ch == 153)
+                                       spuravoid = 1;
+                       } else { /* 40 MHz */
+                               if (ch == 54)
+                                       spuravoid = 1;
+                       }
+               } else {
+                       if (!b43_is_40mhz(dev)) { /* 20MHz */
+                               if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14)
+                                       spuravoid = 1;
+                       } else { /* 40MHz */
+                               if (nphy->aband_spurwar_en &&
+                                   (ch == 38 || ch == 102 || ch == 118))
+                                       spuravoid = dev->dev->chip_id == 0x4716;
+                       }
                }
 
-               b43_nphy_pmu_spur_avoid(dev, avoid);
+               b43_nphy_pmu_spur_avoid(dev, spuravoid);
 
-               if (dev->dev->chip_id == 43222 || dev->dev->chip_id == 43224 ||
-                   dev->dev->chip_id == 43225) {
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW,
-                                   avoid ? 0x5341 : 0x8889);
-                       b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
-               }
+               b43_mac_switch_freq(dev, spuravoid);
 
                if (dev->phy.rev == 3 || dev->phy.rev == 4)
                        ; /* TODO: reset PLL */
 
-               if (avoid)
+               if (spuravoid)
                        b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTRX);
                else
                        b43_phy_mask(dev, B43_NPHY_BBCFG,
@@ -5484,10 +6318,20 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
 
        const struct b43_nphy_channeltab_entry_rev2 *tabent_r2 = NULL;
        const struct b43_nphy_channeltab_entry_rev3 *tabent_r3 = NULL;
+       const struct b43_nphy_chantabent_rev7 *tabent_r7 = NULL;
+       const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g = NULL;
 
        u8 tmp;
 
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               return -ESRCH;
+               /* TODO */
+       } else if (phy->rev >= 7) {
+               r2057_get_chantabent_rev7(dev, channel->center_freq,
+                                         &tabent_r7, &tabent_r7_2g);
+               if (!tabent_r7 && !tabent_r7_2g)
+                       return -ESRCH;
+       } else if (phy->rev >= 3) {
                tabent_r3 = b43_nphy_get_chantabent_rev3(dev,
                                                        channel->center_freq);
                if (!tabent_r3)
@@ -5502,20 +6346,38 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
        /* Channel is set later in common code, but we need to set it on our
           own to let this function's subcalls work properly. */
        phy->channel = channel->hw_value;
-       phy->channel_freq = channel->center_freq;
 
+#if 0
        if (b43_channel_type_is_40mhz(phy->channel_type) !=
                b43_channel_type_is_40mhz(channel_type))
                ; /* TODO: BMAC BW Set (channel_type) */
+#endif
+
+       if (channel_type == NL80211_CHAN_HT40PLUS) {
+               b43_phy_set(dev, B43_NPHY_RXCTL, B43_NPHY_RXCTL_BSELU20);
+               if (phy->rev >= 7)
+                       b43_phy_set(dev, 0x310, 0x8000);
+       } else if (channel_type == NL80211_CHAN_HT40MINUS) {
+               b43_phy_mask(dev, B43_NPHY_RXCTL, ~B43_NPHY_RXCTL_BSELU20);
+               if (phy->rev >= 7)
+                       b43_phy_mask(dev, 0x310, (u16)~0x8000);
+       }
 
-       if (channel_type == NL80211_CHAN_HT40PLUS)
-               b43_phy_set(dev, B43_NPHY_RXCTL,
-                               B43_NPHY_RXCTL_BSELU20);
-       else if (channel_type == NL80211_CHAN_HT40MINUS)
-               b43_phy_mask(dev, B43_NPHY_RXCTL,
-                               ~B43_NPHY_RXCTL_BSELU20);
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 7) {
+               const struct b43_phy_n_sfo_cfg *phy_regs = tabent_r7 ?
+                       &(tabent_r7->phy_regs) : &(tabent_r7_2g->phy_regs);
+
+               if (phy->radio_rev <= 4 || phy->radio_rev == 6) {
+                       tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 2 : 0;
+                       b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE0, ~2, tmp);
+                       b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE1, ~2, tmp);
+               }
 
-       if (dev->phy.rev >= 3) {
+               b43_radio_2057_setup(dev, tabent_r7, tabent_r7_2g);
+               b43_nphy_channel_setup(dev, phy_regs, channel);
+       } else if (phy->rev >= 3) {
                tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 4 : 0;
                b43_radio_maskset(dev, 0x08, 0xFFFB, tmp);
                b43_radio_2056_setup(dev, tabent_r3);
@@ -5656,7 +6518,7 @@ static void b43_nphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
 static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg)
 {
        /* Register 1 is a 32-bit register. */
-       B43_WARN_ON(reg == 1);
+       B43_WARN_ON(dev->phy.rev < 7 && reg == 1);
 
        if (dev->phy.rev >= 7)
                reg |= 0x200; /* Radio 0x2057 */
@@ -5670,7 +6532,7 @@ static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg)
 static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
 {
        /* Register 1 is a 32-bit register. */
-       B43_WARN_ON(reg == 1);
+       B43_WARN_ON(dev->phy.rev < 7 && reg == 1);
 
        b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg);
        b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value);
@@ -5680,15 +6542,23 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
 static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
                                        bool blocked)
 {
+       struct b43_phy *phy = &dev->phy;
+
        if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
                b43err(dev->wl, "MAC not suspended\n");
 
        if (blocked) {
-               b43_phy_mask(dev, B43_NPHY_RFCTL_CMD,
-                               ~B43_NPHY_RFCTL_CMD_CHIP0PU);
-               if (dev->phy.rev >= 7) {
+               if (phy->rev >= 19) {
                        /* TODO */
-               } else if (dev->phy.rev >= 3) {
+               } else if (phy->rev >= 8) {
+                       b43_phy_mask(dev, B43_NPHY_RFCTL_CMD,
+                                    ~B43_NPHY_RFCTL_CMD_CHIP0PU);
+               } else if (phy->rev >= 7) {
+                       /* Nothing needed */
+               } else if (phy->rev >= 3) {
+                       b43_phy_mask(dev, B43_NPHY_RFCTL_CMD,
+                                    ~B43_NPHY_RFCTL_CMD_CHIP0PU);
+
                        b43_radio_mask(dev, 0x09, ~0x2);
 
                        b43_radio_write(dev, 0x204D, 0);
@@ -5706,11 +6576,13 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
                        b43_radio_write(dev, 0x3064, 0);
                }
        } else {
-               if (dev->phy.rev >= 7) {
+               if (phy->rev >= 19) {
+                       /* TODO */
+               } else if (phy->rev >= 7) {
                        if (!dev->phy.radio_on)
                                b43_radio_2057_init(dev);
                        b43_switch_channel(dev, dev->phy.channel);
-               } else if (dev->phy.rev >= 3) {
+               } else if (phy->rev >= 3) {
                        if (!dev->phy.radio_on)
                                b43_radio_init2056(dev);
                        b43_switch_channel(dev, dev->phy.channel);
@@ -5723,10 +6595,13 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/Anacore */
 static void b43_nphy_op_switch_analog(struct b43_wldev *dev, bool on)
 {
+       struct b43_phy *phy = &dev->phy;
        u16 override = on ? 0x0 : 0x7FFF;
        u16 core = on ? 0xD : 0x00FD;
 
-       if (dev->phy.rev >= 3) {
+       if (phy->rev >= 19) {
+               /* TODO */
+       } else if (phy->rev >= 3) {
                if (on) {
                        b43_phy_write(dev, B43_NPHY_AFECTL_C1, core);
                        b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override);
index ecfbf66dbc3b7fdacc8d3e07dbf8d1476355fd16..30bec815b969651ee4ad1cca2c6c74e607cc834f 100644 (file)
 #define B43_NPHY_TXF_40CO_B1S0                 B43_PHY_N(0x0E5) /* TX filter 40 coeff B1 stage 0 */
 #define B43_NPHY_TXF_40CO_B32S1                        B43_PHY_N(0x0E6) /* TX filter 40 coeff B32 stage 1 */
 #define B43_NPHY_TXF_40CO_B1S1                 B43_PHY_N(0x0E7) /* TX filter 40 coeff B1 stage 1 */
+#define B43_NPHY_REV3_RFCTL_OVER0              B43_PHY_N(0x0E7)
 #define B43_NPHY_TXF_40CO_B32S2                        B43_PHY_N(0x0E8) /* TX filter 40 coeff B32 stage 2 */
 #define B43_NPHY_TXF_40CO_B1S2                 B43_PHY_N(0x0E9) /* TX filter 40 coeff B1 stage 2 */
 #define B43_NPHY_BIST_STAT2                    B43_PHY_N(0x0EA) /* BIST status 2 */
 #define B43_NPHY_BIST_STAT3                    B43_PHY_N(0x0EB) /* BIST status 3 */
 #define B43_NPHY_RFCTL_OVER                    B43_PHY_N(0x0EC) /* RF control override */
+#define B43_NPHY_REV3_RFCTL_OVER1              B43_PHY_N(0x0EC)
 #define B43_NPHY_MIMOCFG                       B43_PHY_N(0x0ED) /* MIMO config */
 #define  B43_NPHY_MIMOCFG_GFMIX                        0x0004 /* Greenfield or mixed mode */
 #define  B43_NPHY_MIMOCFG_AUTO                 0x0100 /* Greenfield/mixed mode auto */
 #define B43_NPHY_REV3_C2_CLIP2_GAIN_A          B43_PHY_N(0x2AF)
 #define B43_NPHY_REV3_C2_CLIP2_GAIN_B          B43_PHY_N(0x2B0)
 
+#define B43_NPHY_REV7_RF_CTL_MISC_REG3         B43_PHY_N(0x340)
+#define B43_NPHY_REV7_RF_CTL_MISC_REG4         B43_PHY_N(0x341)
+#define B43_NPHY_REV7_RF_CTL_OVER3             B43_PHY_N(0x342)
+#define B43_NPHY_REV7_RF_CTL_OVER4             B43_PHY_N(0x343)
+#define B43_NPHY_REV7_RF_CTL_MISC_REG5         B43_PHY_N(0x344)
+#define B43_NPHY_REV7_RF_CTL_MISC_REG6         B43_PHY_N(0x345)
+#define B43_NPHY_REV7_RF_CTL_OVER5             B43_PHY_N(0x346)
+#define B43_NPHY_REV7_RF_CTL_OVER6             B43_PHY_N(0x347)
+
 #define B43_PHY_B_BBCFG                                B43_PHY_N_BMODE(0x001) /* BB config */
+#define  B43_PHY_B_BBCFG_RSTCCA                        0x4000 /* Reset CCA */
+#define  B43_PHY_B_BBCFG_RSTRX                 0x8000 /* Reset RX */
 #define B43_PHY_B_TEST                         B43_PHY_N_BMODE(0x00A)
 
 struct b43_wldev;
@@ -935,6 +948,8 @@ struct b43_phy_n {
        bool gain_boost;
        bool elna_gain_config;
        bool band5g_pwrgain;
+       bool use_int_tx_iq_lo_cal;
+       bool lpf_bw_overrode_for_sample_play;
 
        u8 mphase_cal_phase_id;
        u16 mphase_txcal_cmdidx;
index d61d6830c5c77329abbd48aa79631307d0e28bc8..ff1e026a61a131ffce5b0d51b22aaf79619b6be2 100644 (file)
@@ -26,7 +26,7 @@
 #include "radio_2057.h"
 #include "phy_common.h"
 
-static u16 r2057_rev4_init[42][2] = {
+static u16 r2057_rev4_init[][2] = {
        { 0x0E, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 },
        { 0x35, 0x26 }, { 0x3C, 0xff }, { 0x3D, 0xff }, { 0x3E, 0xff },
        { 0x3F, 0xff }, { 0x62, 0x33 }, { 0x8A, 0xf0 }, { 0x8B, 0x10 },
@@ -40,7 +40,7 @@ static u16 r2057_rev4_init[42][2] = {
        { 0x1AB, 0x00 }, { 0x1AC, 0x00 },
 };
 
-static u16 r2057_rev5_init[44][2] = {
+static u16 r2057_rev5_init[][2] = {
        { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 },
        { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 },
        { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f },
@@ -54,7 +54,7 @@ static u16 r2057_rev5_init[44][2] = {
        { 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, { 0x1C2, 0x80 },
 };
 
-static u16 r2057_rev5a_init[45][2] = {
+static u16 r2057_rev5a_init[][2] = {
        { 0x00, 0x15 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 },
        { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 },
        { 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f },
@@ -69,7 +69,7 @@ static u16 r2057_rev5a_init[45][2] = {
        { 0x1C2, 0x80 },
 };
 
-static u16 r2057_rev7_init[54][2] = {
+static u16 r2057_rev7_init[][2] = {
        { 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 },
        { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 },
        { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x13 },
@@ -86,7 +86,8 @@ static u16 r2057_rev7_init[54][2] = {
        { 0x1B7, 0x05 }, { 0x1C2, 0xa0 },
 };
 
-static u16 r2057_rev8_init[54][2] = {
+/* TODO: Which devices should use it?
+static u16 r2057_rev8_init[][2] = {
        { 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 },
        { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 },
        { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f },
@@ -102,6 +103,436 @@ static u16 r2057_rev8_init[54][2] = {
        { 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 },
        { 0x1B7, 0x05 }, { 0x1C2, 0xa0 },
 };
+*/
+
+/* Extracted from MMIO dump of 6.30.223.141 */
+static u16 r2057_rev9_init[][2] = {
+       { 0x27, 0x1f }, { 0x28, 0x0a }, { 0x29, 0x2f }, { 0x42, 0x1f },
+       { 0x48, 0x3f }, { 0x5c, 0x41 }, { 0x63, 0x14 }, { 0x64, 0x12 },
+       { 0x66, 0xff }, { 0x74, 0xa3 }, { 0x7b, 0x14 }, { 0x7c, 0x14 },
+       { 0x7d, 0xee }, { 0x86, 0xc0 }, { 0xc4, 0x10 }, { 0xc9, 0x01 },
+       { 0xe1, 0x41 }, { 0xe8, 0x14 }, { 0xe9, 0x12 }, { 0xeb, 0xff },
+       { 0xf5, 0x0a }, { 0xf8, 0x09 }, { 0xf9, 0xa3 }, { 0x100, 0x14 },
+       { 0x101, 0x10 }, { 0x102, 0xee }, { 0x10b, 0xc0 }, { 0x149, 0x10 },
+       { 0x14e, 0x01 }, { 0x1b7, 0x05 }, { 0x1c2, 0xa0 },
+};
+
+/* Extracted from MMIO dump of 6.30.223.248 */
+static u16 r2057_rev14_init[][2] = {
+       { 0x011, 0xfc }, { 0x030, 0x24 }, { 0x040, 0x1c }, { 0x082, 0x08 },
+       { 0x0b4, 0x44 }, { 0x0c8, 0x01 }, { 0x0c9, 0x01 }, { 0x107, 0x08 },
+       { 0x14d, 0x01 }, { 0x14e, 0x01 }, { 0x1af, 0x40 }, { 0x1b0, 0x40 },
+       { 0x1cc, 0x01 }, { 0x1cf, 0x10 }, { 0x1d0, 0x0f }, { 0x1d3, 0x10 },
+       { 0x1d4, 0x0f },
+};
+
+#define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
+                  r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \
+                  r20, r21, r22, r23, r24, r25, r26, r27) \
+       .radio_vcocal_countval0                 = r00,  \
+       .radio_vcocal_countval1                 = r01,  \
+       .radio_rfpll_refmaster_sparextalsize    = r02,  \
+       .radio_rfpll_loopfilter_r1              = r03,  \
+       .radio_rfpll_loopfilter_c2              = r04,  \
+       .radio_rfpll_loopfilter_c1              = r05,  \
+       .radio_cp_kpd_idac                      = r06,  \
+       .radio_rfpll_mmd0                       = r07,  \
+       .radio_rfpll_mmd1                       = r08,  \
+       .radio_vcobuf_tune                      = r09,  \
+       .radio_logen_mx2g_tune                  = r10,  \
+       .radio_logen_mx5g_tune                  = r11,  \
+       .radio_logen_indbuf2g_tune              = r12,  \
+       .radio_logen_indbuf5g_tune              = r13,  \
+       .radio_txmix2g_tune_boost_pu_core0      = r14,  \
+       .radio_pad2g_tune_pus_core0             = r15,  \
+       .radio_pga_boost_tune_core0             = r16,  \
+       .radio_txmix5g_boost_tune_core0         = r17,  \
+       .radio_pad5g_tune_misc_pus_core0        = r18,  \
+       .radio_lna2g_tune_core0                 = r19,  \
+       .radio_lna5g_tune_core0                 = r20,  \
+       .radio_txmix2g_tune_boost_pu_core1      = r21,  \
+       .radio_pad2g_tune_pus_core1             = r22,  \
+       .radio_pga_boost_tune_core1             = r23,  \
+       .radio_txmix5g_boost_tune_core1         = r24,  \
+       .radio_pad5g_tune_misc_pus_core1        = r25,  \
+       .radio_lna2g_tune_core1                 = r26,  \
+       .radio_lna5g_tune_core1                 = r27
+
+#define RADIOREGS7_2G(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
+                     r10, r11, r12, r13, r14, r15, r16, r17) \
+       .radio_vcocal_countval0                 = r00,  \
+       .radio_vcocal_countval1                 = r01,  \
+       .radio_rfpll_refmaster_sparextalsize    = r02,  \
+       .radio_rfpll_loopfilter_r1              = r03,  \
+       .radio_rfpll_loopfilter_c2              = r04,  \
+       .radio_rfpll_loopfilter_c1              = r05,  \
+       .radio_cp_kpd_idac                      = r06,  \
+       .radio_rfpll_mmd0                       = r07,  \
+       .radio_rfpll_mmd1                       = r08,  \
+       .radio_vcobuf_tune                      = r09,  \
+       .radio_logen_mx2g_tune                  = r10,  \
+       .radio_logen_indbuf2g_tune              = r11,  \
+       .radio_txmix2g_tune_boost_pu_core0      = r12,  \
+       .radio_pad2g_tune_pus_core0             = r13,  \
+       .radio_lna2g_tune_core0                 = r14,  \
+       .radio_txmix2g_tune_boost_pu_core1      = r15,  \
+       .radio_pad2g_tune_pus_core1             = r16,  \
+       .radio_lna2g_tune_core1                 = r17
+
+#define PHYREGS(r0, r1, r2, r3, r4, r5)        \
+       .phy_regs.phy_bw1a      = r0,   \
+       .phy_regs.phy_bw2       = r1,   \
+       .phy_regs.phy_bw3       = r2,   \
+       .phy_regs.phy_bw4       = r3,   \
+       .phy_regs.phy_bw5       = r4,   \
+       .phy_regs.phy_bw6       = r5
+
+/* Copied from brcmsmac (5.75.11): chan_info_nphyrev8_2057_rev5 */
+static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev8_radio_rev5[] = {
+       {
+               .freq                   = 2412,
+               RADIOREGS7_2G(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c,
+                             0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61,
+                             0x03, 0xff),
+               PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+       },
+       {
+               .freq                   = 2417,
+               RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71,
+                             0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61,
+                             0x03, 0xff),
+               PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+       },
+       {
+               .freq                   = 2422,
+               RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76,
+                             0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61,
+                             0x03, 0xef),
+               PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+       },
+       {
+               .freq                   = 2427,
+               RADIOREGS7_2G(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b,
+                             0x09, 0x0c, 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61,
+                             0x03, 0xdf),
+               PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+       },
+       {
+               .freq                   = 2432,
+               RADIOREGS7_2G(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80,
+                             0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61,
+                             0x03, 0xcf),
+               PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+       },
+       {
+               .freq                   = 2437,
+               RADIOREGS7_2G(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85,
+                             0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61,
+                             0x03, 0xbf),
+               PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+       },
+       {
+               .freq                   = 2442,
+               RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a,
+                             0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61,
+                             0x03, 0xaf),
+               PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+       },
+       {
+               .freq                   = 2447,
+               RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f,
+                             0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61,
+                             0x03, 0x9f),
+               PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+       },
+       {
+               .freq                   = 2452,
+               RADIOREGS7_2G(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94,
+                             0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61,
+                             0x03, 0x8f),
+               PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+       },
+       {
+               .freq                   = 2457,
+               RADIOREGS7_2G(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99,
+                             0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61,
+                             0x03, 0x7f),
+               PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+       },
+       {
+               .freq                   = 2462,
+               RADIOREGS7_2G(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e,
+                             0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61,
+                             0x03, 0x6f),
+               PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+       },
+       {
+               .freq                   = 2467,
+               RADIOREGS7_2G(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3,
+                             0x09, 0x0b, 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61,
+                             0x03, 0x5f),
+               PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b),
+       },
+       {
+               .freq                   = 2472,
+               RADIOREGS7_2G(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8,
+                             0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61,
+                             0x03, 0x4f),
+               PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429),
+       },
+       {
+               .freq                   = 2484,
+               RADIOREGS7_2G(0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4,
+                             0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61,
+                             0x03, 0x3f),
+               PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424),
+       }
+};
+
+/* Extracted from MMIO dump of 6.30.223.248 */
+static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev17_radio_rev14[] = {
+       {
+               .freq                   = 2412,
+               RADIOREGS7_2G(0x48, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x6c,
+                             0x09, 0x0d, 0x09, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+       },
+       {
+               .freq                   = 2417,
+               RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x71,
+                             0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+       },
+       {
+               .freq                   = 2422,
+               RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x76,
+                             0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+       },
+       {
+               .freq                   = 2427,
+               RADIOREGS7_2G(0x52, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x7b,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+       },
+       {
+               .freq                   = 2432,
+               RADIOREGS7_2G(0x55, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x80,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+       },
+       {
+               .freq                   = 2437,
+               RADIOREGS7_2G(0x58, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x85,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21,
+                             0x53, 0xff),
+               PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+       },
+       {
+               .freq                   = 2442,
+               RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8a,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21,
+                             0x43, 0xff),
+               PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+       },
+       {
+               .freq                   = 2447,
+               RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8f,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21,
+                             0x43, 0xff),
+               PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+       },
+       {
+               .freq                   = 2452,
+               RADIOREGS7_2G(0x62, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x94,
+                             0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21,
+                             0x43, 0xff),
+               PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+       },
+       {
+               .freq                   = 2457,
+               RADIOREGS7_2G(0x66, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x99,
+                             0x09, 0x0b, 0x07, 0x03, 0x21, 0x43, 0xff, 0x21,
+                             0x43, 0xff),
+               PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+       },
+       {
+               .freq                   = 2462,
+               RADIOREGS7_2G(0x69, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x9e,
+                             0x09, 0x0b, 0x07, 0x03, 0x01, 0x43, 0xff, 0x01,
+                             0x43, 0xff),
+               PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+       },
+};
+
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = {
+       {
+               .freq                   = 2412,
+               RADIOREGS7(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c,
+                          0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443),
+       },
+       {
+               .freq                   = 2417,
+               RADIOREGS7(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71,
+                          0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441),
+       },
+       {
+               .freq                   = 2422,
+               RADIOREGS7(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76,
+                          0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f),
+       },
+       {
+               .freq                   = 2427,
+               RADIOREGS7(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b,
+                          0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d),
+       },
+       {
+               .freq                   = 2432,
+               RADIOREGS7(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80,
+                          0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a),
+       },
+       {
+               .freq                   = 2437,
+               RADIOREGS7(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85,
+                          0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438),
+       },
+       {
+               .freq                   = 2442,
+               RADIOREGS7(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a,
+                          0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436),
+       },
+       {
+               .freq                   = 2447,
+               RADIOREGS7(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f,
+                          0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434),
+       },
+       {
+               .freq                   = 2452,
+               RADIOREGS7(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94,
+                          0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431),
+       },
+       {
+               .freq                   = 2457,
+               RADIOREGS7(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99,
+                          0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f),
+       },
+       {
+               .freq                   = 2462,
+               RADIOREGS7(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e,
+                          0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63,
+                          0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00,
+                          0x00, 0x00, 0xf0, 0x00),
+               PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d),
+       },
+       {
+               .freq                   = 5180,
+               RADIOREGS7(0xbe, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x06,
+                          0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00,
+                          0x9f, 0x2f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x4f,
+                          0x3a, 0x83, 0x00, 0xfc),
+               PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb),
+       },
+       {
+               .freq                   = 5200,
+               RADIOREGS7(0xc5, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x08,
+                          0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00,
+                          0x7f, 0x2f, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x4c,
+                          0x4a, 0x83, 0x00, 0xf8),
+               PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9),
+       },
+       {
+               .freq                   = 5220,
+               RADIOREGS7(0xcc, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0a,
+                          0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00,
+                          0x6d, 0x3d, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x2d,
+                          0x2a, 0x73, 0x00, 0xf8),
+               PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7),
+       },
+       {
+               .freq                   = 5240,
+               RADIOREGS7(0xd2, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0c,
+                          0x02, 0x0d, 0x00, 0x0d, 0x00, 0x8d, 0x00, 0x00,
+                          0x4d, 0x1c, 0x73, 0x00, 0xf8, 0x00, 0x00, 0x4d,
+                          0x2b, 0x73, 0x00, 0xf8),
+               PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5),
+       },
+       {
+               .freq                   = 5745,
+               RADIOREGS7(0x7b, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x7d,
+                          0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00,
+                          0x08, 0x03, 0x03, 0x00, 0x30, 0x00, 0x00, 0x06,
+                          0x02, 0x03, 0x00, 0x30),
+               PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9),
+       },
+       {
+               .freq                   = 5765,
+               RADIOREGS7(0x81, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x81,
+                          0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00,
+                          0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05,
+                          0x02, 0x03, 0x00, 0x00),
+               PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8),
+       },
+       {
+               .freq                   = 5785,
+               RADIOREGS7(0x88, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x85,
+                          0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00,
+                          0x08, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05,
+                          0x21, 0x03, 0x00, 0x00),
+               PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6),
+       },
+       {
+               .freq                   = 5805,
+               RADIOREGS7(0x8f, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x89,
+                          0x04, 0x07, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00,
+                          0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03,
+                          0x00, 0x03, 0x00, 0x00),
+               PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4),
+       },
+       {
+               .freq                   = 5825,
+               RADIOREGS7(0x95, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x8d,
+                          0x04, 0x07, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00,
+                          0x05, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03,
+                          0x00, 0x03, 0x00, 0x00),
+               PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3),
+       },
+};
 
 void r2057_upload_inittabs(struct b43_wldev *dev)
 {
@@ -109,33 +540,98 @@ void r2057_upload_inittabs(struct b43_wldev *dev)
        u16 *table = NULL;
        u16 size, i;
 
-       if (phy->rev == 7) {
+       switch (phy->rev) {
+       case 7:
                table = r2057_rev4_init[0];
                size = ARRAY_SIZE(r2057_rev4_init);
-       } else if (phy->rev == 8 || phy->rev == 9) {
+               break;
+       case 8:
                if (phy->radio_rev == 5) {
-                       if (phy->radio_rev == 8) {
-                               table = r2057_rev5_init[0];
-                               size = ARRAY_SIZE(r2057_rev5_init);
-                       } else {
-                               table = r2057_rev5a_init[0];
-                               size = ARRAY_SIZE(r2057_rev5a_init);
-                       }
+                       table = r2057_rev5_init[0];
+                       size = ARRAY_SIZE(r2057_rev5_init);
                } else if (phy->radio_rev == 7) {
                        table = r2057_rev7_init[0];
                        size = ARRAY_SIZE(r2057_rev7_init);
-               } else if (phy->radio_rev == 9) {
-                       table = r2057_rev8_init[0];
-                       size = ARRAY_SIZE(r2057_rev8_init);
                }
+               break;
+       case 9:
+               if (phy->radio_rev == 5) {
+                       table = r2057_rev5a_init[0];
+                       size = ARRAY_SIZE(r2057_rev5a_init);
+               }
+               break;
+       case 16:
+               if (phy->radio_rev == 9) {
+                       table = r2057_rev9_init[0];
+                       size = ARRAY_SIZE(r2057_rev9_init);
+               }
+               break;
+       case 17:
+               if (phy->radio_rev == 14) {
+                       table = r2057_rev14_init[0];
+                       size = ARRAY_SIZE(r2057_rev14_init);
+               }
+               break;
        }
 
+       B43_WARN_ON(!table);
+
        if (table) {
-               for (i = 0; i < 10; i++) {
-                       pr_info("radio_write 0x%X ", *table);
-                       table++;
-                       pr_info("0x%X\n", *table);
-                       table++;
+               for (i = 0; i < size; i++, table += 2)
+                       b43_radio_write(dev, table[0], table[1]);
+       }
+}
+
+void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq,
+                              const struct b43_nphy_chantabent_rev7 **tabent_r7,
+                              const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g)
+{
+       struct b43_phy *phy = &dev->phy;
+       const struct b43_nphy_chantabent_rev7 *e_r7 = NULL;
+       const struct b43_nphy_chantabent_rev7_2g *e_r7_2g = NULL;
+       unsigned int len, i;
+
+       *tabent_r7 = NULL;
+       *tabent_r7_2g = NULL;
+
+       switch (phy->rev) {
+       case 8:
+               if (phy->radio_rev == 5) {
+                       e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev5;
+                       len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev5);
+               }
+               break;
+       case 16:
+               if (phy->radio_rev == 9) {
+                       e_r7 = b43_nphy_chantab_phy_rev16_radio_rev9;
+                       len = ARRAY_SIZE(b43_nphy_chantab_phy_rev16_radio_rev9);
+               }
+               break;
+       case 17:
+               if (phy->radio_rev == 14) {
+                       e_r7_2g = b43_nphy_chantab_phy_rev17_radio_rev14;
+                       len = ARRAY_SIZE(b43_nphy_chantab_phy_rev17_radio_rev14);
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (e_r7) {
+               for (i = 0; i < len; i++, e_r7++) {
+                       if (e_r7->freq == freq) {
+                               *tabent_r7 = e_r7;
+                               return;
+                       }
+               }
+       } else if (e_r7_2g) {
+               for (i = 0; i < len; i++, e_r7_2g++) {
+                       if (e_r7_2g->freq == freq) {
+                               *tabent_r7_2g = e_r7_2g;
+                               return;
+                       }
                }
+       } else {
+               B43_WARN_ON(1);
        }
 }
index eeebd8fbeb0db19575aad27bba58e5a1763f6a8c..220d080238ff8805816eef0b127d990045fa1082 100644 (file)
@@ -84,6 +84,8 @@
 #define R2057_CMOSBUF_RX_RCCR                  0x04c
 #define R2057_LOGEN_SEL_PKDET                  0x04d
 #define R2057_CMOSBUF_SHAREIQ_PTAT             0x04e
+
+/* MISC core 0 */
 #define R2057_RXTXBIAS_CONFIG_CORE0            0x04f
 #define R2057_TXGM_TXRF_PUS_CORE0              0x050
 #define R2057_TXGM_IDAC_BLEED_CORE0            0x051
 #define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0  0x0d1
 #define R2057_LPF_GAIN_CORE0                   0x0d2
 #define R2057_DACBUF_IDACS_BW_CORE0            0x0d3
+
+/* MISC core 1 */
 #define R2057_RXTXBIAS_CONFIG_CORE1            0x0d4
 #define R2057_TXGM_TXRF_PUS_CORE1              0x0d5
 #define R2057_TXGM_IDAC_BLEED_CORE1            0x0d6
 #define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1  0x156
 #define R2057_LPF_GAIN_CORE1                   0x157
 #define R2057_DACBUF_IDACS_BW_CORE1            0x158
+
 #define R2057_DACBUF_VINCM_CORE1               0x159
 #define R2057_RCCAL_START_R1_Q1_P1             0x15a
 #define R2057_RCCAL_X1                         0x15b
 #define R2057_RCCAL_BCAP_VAL                   0x16b
 #define R2057_RCCAL_HPC_VAL                    0x16c
 #define R2057_RCCAL_OVERRIDES                  0x16d
+
+/* TX core 0 */
 #define R2057_TX0_IQCAL_GAIN_BW                        0x170
 #define R2057_TX0_LOFT_FINE_I                  0x171
 #define R2057_TX0_LOFT_FINE_Q                  0x172
 #define R2057_TX0_TXRXCOUPLE_2G_PWRUP          0x17e
 #define R2057_TX0_TXRXCOUPLE_5G_ATTEN          0x17f
 #define R2057_TX0_TXRXCOUPLE_5G_PWRUP          0x180
+
+/* TX core 1 */
 #define R2057_TX1_IQCAL_GAIN_BW                        0x190
 #define R2057_TX1_LOFT_FINE_I                  0x191
 #define R2057_TX1_LOFT_FINE_Q                  0x192
 #define R2057_TX1_TXRXCOUPLE_2G_PWRUP          0x19e
 #define R2057_TX1_TXRXCOUPLE_5G_ATTEN          0x19f
 #define R2057_TX1_TXRXCOUPLE_5G_PWRUP          0x1a0
+
 #define R2057_AFE_VCM_CAL_MASTER_CORE0         0x1a1
 #define R2057_AFE_SET_VCM_I_CORE0              0x1a2
 #define R2057_AFE_SET_VCM_Q_CORE0              0x1a3
 
 #define R2057_VCM_MASK                         0x7
 
+struct b43_nphy_chantabent_rev7 {
+       /* The channel frequency in MHz */
+       u16 freq;
+       /* Radio regs values on channelswitch */
+       u8 radio_vcocal_countval0;
+       u8 radio_vcocal_countval1;
+       u8 radio_rfpll_refmaster_sparextalsize;
+       u8 radio_rfpll_loopfilter_r1;
+       u8 radio_rfpll_loopfilter_c2;
+       u8 radio_rfpll_loopfilter_c1;
+       u8 radio_cp_kpd_idac;
+       u8 radio_rfpll_mmd0;
+       u8 radio_rfpll_mmd1;
+       u8 radio_vcobuf_tune;
+       u8 radio_logen_mx2g_tune;
+       u8 radio_logen_mx5g_tune;
+       u8 radio_logen_indbuf2g_tune;
+       u8 radio_logen_indbuf5g_tune;
+       u8 radio_txmix2g_tune_boost_pu_core0;
+       u8 radio_pad2g_tune_pus_core0;
+       u8 radio_pga_boost_tune_core0;
+       u8 radio_txmix5g_boost_tune_core0;
+       u8 radio_pad5g_tune_misc_pus_core0;
+       u8 radio_lna2g_tune_core0;
+       u8 radio_lna5g_tune_core0;
+       u8 radio_txmix2g_tune_boost_pu_core1;
+       u8 radio_pad2g_tune_pus_core1;
+       u8 radio_pga_boost_tune_core1;
+       u8 radio_txmix5g_boost_tune_core1;
+       u8 radio_pad5g_tune_misc_pus_core1;
+       u8 radio_lna2g_tune_core1;
+       u8 radio_lna5g_tune_core1;
+       /* PHY res values on channelswitch */
+       struct b43_phy_n_sfo_cfg phy_regs;
+};
+
+struct b43_nphy_chantabent_rev7_2g {
+       /* The channel frequency in MHz */
+       u16 freq;
+       /* Radio regs values on channelswitch */
+       u8 radio_vcocal_countval0;
+       u8 radio_vcocal_countval1;
+       u8 radio_rfpll_refmaster_sparextalsize;
+       u8 radio_rfpll_loopfilter_r1;
+       u8 radio_rfpll_loopfilter_c2;
+       u8 radio_rfpll_loopfilter_c1;
+       u8 radio_cp_kpd_idac;
+       u8 radio_rfpll_mmd0;
+       u8 radio_rfpll_mmd1;
+       u8 radio_vcobuf_tune;
+       u8 radio_logen_mx2g_tune;
+       u8 radio_logen_indbuf2g_tune;
+       u8 radio_txmix2g_tune_boost_pu_core0;
+       u8 radio_pad2g_tune_pus_core0;
+       u8 radio_lna2g_tune_core0;
+       u8 radio_txmix2g_tune_boost_pu_core1;
+       u8 radio_pad2g_tune_pus_core1;
+       u8 radio_lna2g_tune_core1;
+       /* PHY regs values on channelswitch */
+       struct b43_phy_n_sfo_cfg phy_regs;
+};
+
 void r2057_upload_inittabs(struct b43_wldev *dev);
 
+void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq,
+                              const struct b43_nphy_chantabent_rev7 **tabent_r7,
+                              const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g);
+
 #endif /* B43_RADIO_2057_H_ */
index 4047c05e380759f4f0b64bbdc5188f04efcce242..4b5885077b01bd5b223a3d45868adfc52a744882 100644 (file)
@@ -2146,7 +2146,196 @@ static const u16 b43_ntab_antswctl_r3[4][32] = {
        }
 };
 
-/* TX gain tables */
+/* static tables, PHY revision >= 7 */
+
+/* Copied from brcmsmac (5.75.11) */
+static const u32 b43_ntab_tmap_r7[] = {
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0xf1111110, 0x11111111, 0x11f11111, 0x00000111,
+       0x11000000, 0x1111f111, 0x11111111, 0x111111f1,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888,
+       0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+       0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+       0xa2222220, 0x22222222, 0x22c22222, 0x00000222,
+       0x22000000, 0x2222a222, 0x22222222, 0x222222a2,
+       0xf1111110, 0x11111111, 0x11f11111, 0x00011111,
+       0x11110000, 0x1111f111, 0x11111111, 0x111111f1,
+       0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa,
+       0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+       0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88,
+       0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888,
+       0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808,
+       0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08,
+       0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080,
+       0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+       0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888,
+       0x22000000, 0x2222b222, 0x22222222, 0x222222b2,
+       0xb2222220, 0x22222222, 0x22d22222, 0x00000222,
+       0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+       0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+       0x33000000, 0x3333b333, 0x33333333, 0x333333b3,
+       0xb3333330, 0x33333333, 0x33d33333, 0x00000333,
+       0x22000000, 0x2222a222, 0x22222222, 0x222222a2,
+       0xa2222220, 0x22222222, 0x22c22222, 0x00000222,
+       0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+       0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888,
+       0x22222200, 0x2222f222, 0x22222222, 0x222222f2,
+       0x22222222, 0x22222222, 0x22f22222, 0x00000222,
+       0x11000000, 0x1111f111, 0x11111111, 0x11111111,
+       0xf1111111, 0x11111111, 0x11f11111, 0x01111111,
+       0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b,
+       0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb,
+       0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+       0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa,
+       0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+       0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb,
+       0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999,
+       0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88,
+       0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a,
+       0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b,
+       0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909,
+       0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08,
+       0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a,
+       0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090,
+       0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090,
+       0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080,
+       0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0,
+       0x22000000, 0x2222f222, 0x22222222, 0x222222f2,
+       0xf2222220, 0x22222222, 0x22f22222, 0x00000222,
+       0x11000000, 0x1111f111, 0x11111111, 0x111111f1,
+       0xf1111110, 0x11111111, 0x11f11111, 0x00000111,
+       0x33000000, 0x3333f333, 0x33333333, 0x333333f3,
+       0xf3333330, 0x33333333, 0x33f33333, 0x00000333,
+       0x22000000, 0x2222f222, 0x22222222, 0x222222f2,
+       0xf2222220, 0x22222222, 0x22f22222, 0x00000222,
+       0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+       0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888,
+       0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888,
+       0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888,
+       0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+       0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+       0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+       0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+       0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_noisevar_r7[] = {
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+       0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+};
+
+/**************************************************
+ * TX gain tables
+ **************************************************/
+
 static const u32 b43_ntab_tx_gain_rev0_1_2[] = {
        0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42,
        0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44,
@@ -2182,7 +2371,9 @@ static const u32 b43_ntab_tx_gain_rev0_1_2[] = {
        0x03801442, 0x03801344, 0x03801342, 0x00002b00,
 };
 
-static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = {
+/* EPA 2 GHz */
+
+static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = {
        0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e,
        0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037,
        0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e,
@@ -2217,7 +2408,44 @@ static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = {
        0x1041003c, 0x1041003b, 0x10410039, 0x10410037,
 };
 
-static const u32 b43_ntab_tx_gain_rev3_5ghz[] = {
+static const u32 b43_ntab_tx_gain_epa_rev3_hi_pwr_2g[] = {
+       0x0f410044, 0x0f410042, 0x0f410040, 0x0f41003e,
+       0x0f41003c, 0x0f41003b, 0x0f410039, 0x0f410037,
+       0x0e410044, 0x0e410042, 0x0e410040, 0x0e41003e,
+       0x0e41003c, 0x0e41003b, 0x0e410039, 0x0e410037,
+       0x0d410044, 0x0d410042, 0x0d410040, 0x0d41003e,
+       0x0d41003c, 0x0d41003b, 0x0d410039, 0x0d410037,
+       0x0c410044, 0x0c410042, 0x0c410040, 0x0c41003e,
+       0x0c41003c, 0x0c41003b, 0x0c410039, 0x0c410037,
+       0x0b410044, 0x0b410042, 0x0b410040, 0x0b41003e,
+       0x0b41003c, 0x0b41003b, 0x0b410039, 0x0b410037,
+       0x0a410044, 0x0a410042, 0x0a410040, 0x0a41003e,
+       0x0a41003c, 0x0a41003b, 0x0a410039, 0x0a410037,
+       0x09410044, 0x09410042, 0x09410040, 0x0941003e,
+       0x0941003c, 0x0941003b, 0x09410039, 0x09410037,
+       0x08410044, 0x08410042, 0x08410040, 0x0841003e,
+       0x0841003c, 0x0841003b, 0x08410039, 0x08410037,
+       0x07410044, 0x07410042, 0x07410040, 0x0741003e,
+       0x0741003c, 0x0741003b, 0x07410039, 0x07410037,
+       0x06410044, 0x06410042, 0x06410040, 0x0641003e,
+       0x0641003c, 0x0641003b, 0x06410039, 0x06410037,
+       0x05410044, 0x05410042, 0x05410040, 0x0541003e,
+       0x0541003c, 0x0541003b, 0x05410039, 0x05410037,
+       0x04410044, 0x04410042, 0x04410040, 0x0441003e,
+       0x0441003c, 0x0441003b, 0x04410039, 0x04410037,
+       0x03410044, 0x03410042, 0x03410040, 0x0341003e,
+       0x0341003c, 0x0341003b, 0x03410039, 0x03410037,
+       0x02410044, 0x02410042, 0x02410040, 0x0241003e,
+       0x0241003c, 0x0241003b, 0x02410039, 0x02410037,
+       0x01410044, 0x01410042, 0x01410040, 0x0141003e,
+       0x0141003c, 0x0141003b, 0x01410039, 0x01410037,
+       0x00410044, 0x00410042, 0x00410040, 0x0041003e,
+       0x0041003c, 0x0041003b, 0x00410039, 0x00410037
+};
+
+/* EPA 5 GHz */
+
+static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = {
        0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e,
        0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037,
        0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e,
@@ -2252,7 +2480,7 @@ static const u32 b43_ntab_tx_gain_rev3_5ghz[] = {
        0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037,
 };
 
-static const u32 b43_ntab_tx_gain_rev4_5ghz[] = {
+static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = {
        0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e,
        0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037,
        0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e,
@@ -2287,7 +2515,42 @@ static const u32 b43_ntab_tx_gain_rev4_5ghz[] = {
        0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034,
 };
 
-static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = {
+static const u32 b43_ntab_tx_gain_epa_rev4_hi_pwr_5g[] = {
+       0x2ff10044, 0x2ff10042, 0x2ff10040, 0x2ff1003e,
+       0x2ff1003c, 0x2ff1003b, 0x2ff10039, 0x2ff10037,
+       0x2ef10044, 0x2ef10042, 0x2ef10040, 0x2ef1003e,
+       0x2ef1003c, 0x2ef1003b, 0x2ef10039, 0x2ef10037,
+       0x2df10044, 0x2df10042, 0x2df10040, 0x2df1003e,
+       0x2df1003c, 0x2df1003b, 0x2df10039, 0x2df10037,
+       0x2cf10044, 0x2cf10042, 0x2cf10040, 0x2cf1003e,
+       0x2cf1003c, 0x2cf1003b, 0x2cf10039, 0x2cf10037,
+       0x2bf10044, 0x2bf10042, 0x2bf10040, 0x2bf1003e,
+       0x2bf1003c, 0x2bf1003b, 0x2bf10039, 0x2bf10037,
+       0x2af10044, 0x2af10042, 0x2af10040, 0x2af1003e,
+       0x2af1003c, 0x2af1003b, 0x2af10039, 0x2af10037,
+       0x29f10044, 0x29f10042, 0x29f10040, 0x29f1003e,
+       0x29f1003c, 0x29f1003b, 0x29f10039, 0x29f10037,
+       0x28f10044, 0x28f10042, 0x28f10040, 0x28f1003e,
+       0x28f1003c, 0x28f1003b, 0x28f10039, 0x28f10037,
+       0x27f10044, 0x27f10042, 0x27f10040, 0x27f1003e,
+       0x27f1003c, 0x27f1003b, 0x27f10039, 0x27f10037,
+       0x26f10044, 0x26f10042, 0x26f10040, 0x26f1003e,
+       0x26f1003c, 0x26f1003b, 0x26f10039, 0x26f10037,
+       0x25f10044, 0x25f10042, 0x25f10040, 0x25f1003e,
+       0x25f1003c, 0x25f1003b, 0x25f10039, 0x25f10037,
+       0x24f10044, 0x24f10042, 0x24f10040, 0x24f1003e,
+       0x24f1003c, 0x24f1003b, 0x24f10039, 0x24f10038,
+       0x23f10041, 0x23f10040, 0x23f1003f, 0x23f1003e,
+       0x23f1003c, 0x23f1003b, 0x23f10039, 0x23f10037,
+       0x22f10044, 0x22f10042, 0x22f10040, 0x22f1003e,
+       0x22f1003c, 0x22f1003b, 0x22f10039, 0x22f10037,
+       0x21f10044, 0x21f10042, 0x21f10040, 0x21f1003e,
+       0x21f1003c, 0x21f1003b, 0x21f10039, 0x21f10037,
+       0x20d10043, 0x20d10041, 0x20d1003e, 0x20d1003c,
+       0x20d1003a, 0x20d10038, 0x20d10036, 0x20d10034
+};
+
+static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = {
        0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044,
        0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c,
        0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e,
@@ -2322,7 +2585,9 @@ static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = {
        0x0062003b, 0x00620039, 0x00620037, 0x00620035,
 };
 
-static const u32 txpwrctrl_tx_gain_ipa[] = {
+/* IPA 2 GHz */
+
+static const u32 b43_ntab_tx_gain_ipa_rev3_2g[] = {
        0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029,
        0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025,
        0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029,
@@ -2357,7 +2622,7 @@ static const u32 txpwrctrl_tx_gain_ipa[] = {
        0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025,
 };
 
-static const u32 txpwrctrl_tx_gain_ipa_rev5[] = {
+static const u32 b43_ntab_tx_gain_ipa_rev5_2g[] = {
        0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029,
        0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025,
        0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029,
@@ -2392,7 +2657,7 @@ static const u32 txpwrctrl_tx_gain_ipa_rev5[] = {
        0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025,
 };
 
-static const u32 txpwrctrl_tx_gain_ipa_rev6[] = {
+static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = {
        0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029,
        0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025,
        0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029,
@@ -2427,7 +2692,117 @@ static const u32 txpwrctrl_tx_gain_ipa_rev6[] = {
        0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025,
 };
 
-static const u32 txpwrctrl_tx_gain_ipa_5g[] = {
+/* Copied from brcmsmac (5.75.11): nphy_tpc_txgain_ipa_2g_2057rev5 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev5_2g[] = {
+       0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e,
+       0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033,
+       0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e,
+       0x3067002e, 0x305f002f, 0x30570030, 0x3057002d,
+       0x304f002e, 0x30470031, 0x3047002e, 0x3047002c,
+       0x30470029, 0x303f002c, 0x303f0029, 0x3037002d,
+       0x3037002a, 0x30370028, 0x302f002c, 0x302f002a,
+       0x302f0028, 0x302f0026, 0x3027002c, 0x30270029,
+       0x30270027, 0x30270025, 0x30270023, 0x301f002c,
+       0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024,
+       0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b,
+       0x30170028, 0x30170026, 0x30170024, 0x30170022,
+       0x30170020, 0x3017001e, 0x3017001d, 0x3017001b,
+       0x3017001a, 0x30170018, 0x30170017, 0x30170015,
+       0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024,
+       0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d,
+       0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017,
+       0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215,
+       0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+       0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715,
+};
+
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = {
+       0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029,
+       0x60b70029, 0x60a70029, 0x609f002a, 0x6097002b,
+       0x6087002e, 0x60770031, 0x606f0032, 0x60670034,
+       0x60670031, 0x605f0033, 0x605f0031, 0x60570033,
+       0x60570030, 0x6057002d, 0x6057002b, 0x604f002d,
+       0x604f002b, 0x604f0029, 0x604f0026, 0x60470029,
+       0x60470027, 0x603f0029, 0x603f0027, 0x603f0025,
+       0x60370029, 0x60370027, 0x60370024, 0x602f002a,
+       0x602f0028, 0x602f0026, 0x602f0024, 0x6027002a,
+       0x60270028, 0x60270026, 0x60270024, 0x60270022,
+       0x601f002b, 0x601f0029, 0x601f0027, 0x601f0024,
+       0x601f0022, 0x601f0020, 0x601f001f, 0x601f001d,
+       0x60170029, 0x60170027, 0x60170025, 0x60170023,
+       0x60170021, 0x6017001f, 0x6017001d, 0x6017001c,
+       0x6017001a, 0x60170018, 0x60170018, 0x60170016,
+       0x60170015, 0x600f0029, 0x600f0027, 0x600f0025,
+       0x600f0023, 0x600f0021, 0x600f001f, 0x600f001d,
+       0x600f001c, 0x600f001a, 0x600f0019, 0x600f0018,
+       0x600f0016, 0x600f0015, 0x600f0115, 0x600f0215,
+       0x600f0315, 0x600f0415, 0x600f0515, 0x600f0615,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+       0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+};
+
+/* Extracted from MMIO dump of 6.30.223.248 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev14_2g[] = {
+       0x50df002e, 0x50cf002d, 0x50bf002c, 0x50b7002b,
+       0x50af002a, 0x50a70029, 0x509f0029, 0x50970028,
+       0x508f0027, 0x50870027, 0x507f0027, 0x50770027,
+       0x506f0027, 0x50670027, 0x505f0028, 0x50570029,
+       0x504f002b, 0x5047002e, 0x5047002b, 0x50470029,
+       0x503f002c, 0x503f0029, 0x5037002c, 0x5037002a,
+       0x50370028, 0x502f002d, 0x502f002b, 0x502f0028,
+       0x502f0026, 0x5027002d, 0x5027002a, 0x50270028,
+       0x50270026, 0x50270024, 0x501f002e, 0x501f002b,
+       0x501f0029, 0x501f0027, 0x501f0024, 0x501f0022,
+       0x501f0020, 0x501f001f, 0x5017002c, 0x50170029,
+       0x50170027, 0x50170024, 0x50170022, 0x50170021,
+       0x5017001f, 0x5017001d, 0x5017001b, 0x5017001a,
+       0x50170018, 0x50170017, 0x50170015, 0x500f002c,
+       0x500f002a, 0x500f0027, 0x500f0025, 0x500f0023,
+       0x500f0022, 0x500f001f, 0x500f001e, 0x500f001c,
+       0x500f001a, 0x500f0019, 0x500f0018, 0x500f0016,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+       0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015,
+};
+
+/* IPA 2 5Hz */
+
+static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = {
        0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031,
        0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b,
        0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027,
@@ -2462,6 +2837,42 @@ static const u32 txpwrctrl_tx_gain_ipa_5g[] = {
        0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f,
 };
 
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev9_5g[] = {
+       0x7f7f0053, 0x7f7f004b, 0x7f7f0044, 0x7f7f003f,
+       0x7f7f0039, 0x7f7f0035, 0x7f7f0032, 0x7f7f0030,
+       0x7f7f002d, 0x7e7f0030, 0x7e7f002d, 0x7d7f0032,
+       0x7d7f002f, 0x7d7f002c, 0x7c7f0032, 0x7c7f0030,
+       0x7c7f002d, 0x7b7f0030, 0x7b7f002e, 0x7b7f002b,
+       0x7a7f0032, 0x7a7f0030, 0x7a7f002d, 0x7a7f002b,
+       0x797f0030, 0x797f002e, 0x797f002b, 0x797f0029,
+       0x787f0030, 0x787f002d, 0x787f002b, 0x777f0032,
+       0x777f0030, 0x777f002d, 0x777f002b, 0x767f0031,
+       0x767f002f, 0x767f002c, 0x767f002a, 0x757f0031,
+       0x757f002f, 0x757f002c, 0x757f002a, 0x747f0030,
+       0x747f002d, 0x747f002b, 0x737f0032, 0x737f002f,
+       0x737f002c, 0x737f002a, 0x727f0030, 0x727f002d,
+       0x727f002b, 0x727f0029, 0x717f0030, 0x717f002d,
+       0x717f002b, 0x707f0031, 0x707f002f, 0x707f002c,
+       0x707f002a, 0x707f0027, 0x707f0025, 0x707f0023,
+       0x707f0021, 0x707f001f, 0x707f001d, 0x707f001c,
+       0x707f001a, 0x707f0019, 0x707f0017, 0x707f0016,
+       0x707f0015, 0x707f0014, 0x707f0012, 0x707f0012,
+       0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e,
+       0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b,
+       0x707f000a, 0x707f000a, 0x707f0009, 0x707f0008,
+       0x707f0008, 0x707f0008, 0x707f0008, 0x707f0007,
+       0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006,
+       0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004,
+       0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003,
+       0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003,
+       0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003,
+       0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002,
+       0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002,
+       0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001,
+       0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001,
+};
+
 const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = {
        -114, -108, -98, -91, -84, -78, -70, -62,
        -54, -46, -39, -31, -23, -15, -8, 0
@@ -2698,11 +3109,11 @@ static const struct nphy_rf_control_override_rev7
        { 0x0010, 0x07A, 0x07D, 0x0010, 4 },
        { 0x0020, 0x07A, 0x07D, 0x0020, 5 },
        { 0x0040, 0x07A, 0x07D, 0x0040, 6 },
-       { 0x0080, 0x0F8, 0x0FA, 0x0080, 7 },
+       { 0x0080, 0x07A, 0x07D, 0x0080, 7 },
        { 0x0400, 0x0F8, 0x0FA, 0x0070, 4 },
        { 0x0800, 0x07B, 0x07E, 0xFFFF, 0 },
        { 0x1000, 0x07C, 0x07F, 0xFFFF, 0 },
-       { 0x6000, 0x348, 0x349, 0xFFFF, 0 },
+       { 0x6000, 0x348, 0x349, 0x00FF, 0 },
        { 0x2000, 0x348, 0x349, 0x000F, 0 },
 };
 
@@ -3031,6 +3442,91 @@ void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset,
                b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \
        } while (0)
 
+static void b43_nphy_tables_init_shared_lut(struct b43_wldev *dev)
+{
+       ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
+       ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
+       ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
+       ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
+       ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
+       ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
+       ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
+       ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
+       ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
+       ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+}
+
+static void b43_nphy_tables_init_rev7_volatile(struct b43_wldev *dev)
+{
+       struct ssb_sprom *sprom = dev->dev->bus_sprom;
+       u8 antswlut;
+       int core, offset, i;
+
+       const int antswlut0_offsets[] = { 0, 4, 8, }; /* Offsets for values */
+       const u8 antswlut0_values[][3] = {
+               { 0x2, 0x12, 0x8 }, /* Core 0 */
+               { 0x2, 0x18, 0x2 }, /* Core 1 */
+       };
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+               antswlut = sprom->fem.ghz5.antswlut;
+       else
+               antswlut = sprom->fem.ghz2.antswlut;
+
+       switch (antswlut) {
+       case 0:
+               for (core = 0; core < 2; core++) {
+                       for (i = 0; i < ARRAY_SIZE(antswlut0_values[0]); i++) {
+                               offset = core ? 0x20 : 0x00;
+                               offset += antswlut0_offsets[i];
+                               b43_ntab_write(dev, B43_NTAB8(9, offset),
+                                              antswlut0_values[core][i]);
+                       }
+               }
+               break;
+       default:
+               b43err(dev->wl, "Unsupported antswlut: %d\n", antswlut);
+               break;
+       }
+}
+
+static void b43_nphy_tables_init_rev16(struct b43_wldev *dev)
+{
+       /* Static tables */
+       if (dev->phy.do_full_init) {
+               ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7);
+               b43_nphy_tables_init_shared_lut(dev);
+       }
+
+       /* Volatile tables */
+       b43_nphy_tables_init_rev7_volatile(dev);
+}
+
+static void b43_nphy_tables_init_rev7(struct b43_wldev *dev)
+{
+       /* Static tables */
+       if (dev->phy.do_full_init) {
+               ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3);
+               ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3);
+               ntab_upload(dev, B43_NTAB_TMAP_R7, b43_ntab_tmap_r7);
+               ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3);
+               ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3);
+               ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7);
+               ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3);
+               ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3);
+               ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3);
+               ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3);
+               ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3);
+               ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
+               ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
+               ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
+               b43_nphy_tables_init_shared_lut(dev);
+       }
+
+       /* Volatile tables */
+       b43_nphy_tables_init_rev7_volatile(dev);
+}
+
 static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
 {
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
@@ -3057,16 +3553,7 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
                ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
                ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
                ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
-               ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
-               ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
-               ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
-               ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
-               ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
-               ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
-               ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
-               ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
-               ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
-               ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+               b43_nphy_tables_init_shared_lut(dev);
        }
 
        /* Volatile tables */
@@ -3115,7 +3602,11 @@ static void b43_nphy_tables_init_rev0(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */
 void b43_nphy_tables_init(struct b43_wldev *dev)
 {
-       if (dev->phy.rev >= 3)
+       if (dev->phy.rev >= 16)
+               b43_nphy_tables_init_rev16(dev);
+       else if (dev->phy.rev >= 7)
+               b43_nphy_tables_init_rev7(dev);
+       else if (dev->phy.rev >= 3)
                b43_nphy_tables_init_rev3(dev);
        else
                b43_nphy_tables_init_rev0(dev);
@@ -3124,23 +3615,55 @@ void b43_nphy_tables_init(struct b43_wldev *dev)
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */
 static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
+
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
-               if (dev->phy.rev >= 6) {
-                       if (dev->dev->chip_id == 47162)
-                               return txpwrctrl_tx_gain_ipa_rev5;
-                       return txpwrctrl_tx_gain_ipa_rev6;
-               } else if (dev->phy.rev >= 5) {
-                       return txpwrctrl_tx_gain_ipa_rev5;
-               } else {
-                       return txpwrctrl_tx_gain_ipa;
+               switch (phy->rev) {
+               case 17:
+                       if (phy->radio_rev == 14)
+                               return b43_ntab_tx_gain_ipa_2057_rev14_2g;
+                       break;
+               case 16:
+                       if (phy->radio_rev == 9)
+                               return b43_ntab_tx_gain_ipa_2057_rev9_2g;
+                       break;
+               case 8:
+                       if (phy->radio_rev == 5)
+                               return b43_ntab_tx_gain_ipa_2057_rev5_2g;
+                       break;
+               case 6:
+                       if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162)
+                               return b43_ntab_tx_gain_ipa_rev5_2g;
+                       return b43_ntab_tx_gain_ipa_rev6_2g;
+               case 5:
+                       return b43_ntab_tx_gain_ipa_rev5_2g;
+               case 4:
+               case 3:
+                       return b43_ntab_tx_gain_ipa_rev3_2g;
                }
+
+               b43err(dev->wl,
+                      "No 2GHz IPA gain table available for this device\n");
+               return NULL;
        } else {
-               return txpwrctrl_tx_gain_ipa_5g;
+               switch (phy->rev) {
+               case 16:
+                       if (phy->radio_rev == 9)
+                               return b43_ntab_tx_gain_ipa_2057_rev9_5g;
+                       break;
+               case 3 ... 6:
+                       return b43_ntab_tx_gain_ipa_rev3_5g;
+               }
+
+               b43err(dev->wl,
+                      "No 5GHz IPA gain table available for this device\n");
+               return NULL;
        }
 }
 
 const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
 {
+       struct b43_phy *phy = &dev->phy;
        enum ieee80211_band band = b43_current_band(dev->wl);
        struct ssb_sprom *sprom = dev->dev->bus_sprom;
 
@@ -3152,19 +3675,36 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
            (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) {
                return b43_nphy_get_ipa_gain_table(dev);
        } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
-               if (dev->phy.rev == 3)
-                       return b43_ntab_tx_gain_rev3_5ghz;
-               if (dev->phy.rev == 4)
+               switch (phy->rev) {
+               case 6:
+               case 5:
+                       return b43_ntab_tx_gain_epa_rev5_5g;
+               case 4:
                        return sprom->fem.ghz5.extpa_gain == 3 ?
-                               b43_ntab_tx_gain_rev4_5ghz :
-                               b43_ntab_tx_gain_rev4_5ghz; /* FIXME */
-               else
-                       return b43_ntab_tx_gain_rev5plus_5ghz;
+                               b43_ntab_tx_gain_epa_rev4_5g :
+                               b43_ntab_tx_gain_epa_rev4_hi_pwr_5g;
+               case 3:
+                       return b43_ntab_tx_gain_epa_rev3_5g;
+               default:
+                       b43err(dev->wl,
+                              "No 5GHz EPA gain table available for this device\n");
+                       return NULL;
+               }
        } else {
-               if (dev->phy.rev >= 5 && sprom->fem.ghz5.extpa_gain == 3)
-                       return b43_ntab_tx_gain_rev3plus_2ghz; /* FIXME */
-               else
-                       return b43_ntab_tx_gain_rev3plus_2ghz;
+               switch (phy->rev) {
+               case 6:
+               case 5:
+                       if (sprom->fem.ghz5.extpa_gain == 3)
+                               return b43_ntab_tx_gain_epa_rev3_hi_pwr_2g;
+                       /* fall through */
+               case 4:
+               case 3:
+                       return b43_ntab_tx_gain_epa_rev3_2g;
+               default:
+                       b43err(dev->wl,
+                              "No 2GHz EPA gain table available for this device\n");
+                       return NULL;
+               }
        }
 }
 
@@ -3191,7 +3731,7 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
        /* Some workarounds to the workarounds... */
        if (ghz5 && dev->phy.rev >= 6) {
                if (dev->phy.radio_rev == 11 &&
-                   !b43_channel_type_is_40mhz(dev->phy.channel_type))
+                   !b43_is_40mhz(dev))
                        e->cliplo_gain = 0x2d;
        } else if (!ghz5 && dev->phy.rev >= 5) {
                static const int gain_data[] = {0x0062, 0x0064, 0x006a, 0x106a,
index 3a58aee4c4cf714aa72bc22a8c85670b135eb934..3ce2e6f3a2781d168cf0955f6246b24a1d5d1181 100644 (file)
@@ -165,6 +165,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
 #define B43_NTAB_C1_LOFEEDTH_R3                B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */
 #define B43_NTAB_C1_PAPD_COMP_R3       B43_NTAB16(27, 576)
 
+/* Static N-PHY tables, PHY revision >= 7 */
+#define B43_NTAB_TMAP_R7               B43_NTAB32(12,   0) /* TM AP */
+#define B43_NTAB_NOISEVAR_R7           B43_NTAB32(16,   0) /* noise variance */
+
 #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE       18
 #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE       18
 #define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE      18
index 6e6ef3fc2247520ae0859d94ad08df5d9b6cb08a..426dc13c44cd6dff0504151e52b59bcdb87f06a0 100644 (file)
@@ -80,9 +80,10 @@ static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp)
 }
 
 /* Extract the bitrate index out of an OFDM PLCP header. */
-static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool aphy)
+static int b43_plcp_get_bitrate_idx_ofdm(struct b43_plcp_hdr6 *plcp, bool ghz5)
 {
-       int base = aphy ? 0 : 4;
+       /* For 2 GHz band first OFDM rate is at index 4, see main.c */
+       int base = ghz5 ? 0 : 4;
 
        switch (plcp->raw[0] & 0xF) {
        case 0xB:
@@ -767,7 +768,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
 
        if (phystat0 & B43_RX_PHYST0_OFDM)
                rate_idx = b43_plcp_get_bitrate_idx_ofdm(plcp,
-                                               phytype == B43_PHYTYPE_A);
+                                       !!(chanstat & B43_RX_CHAN_5GHZ));
        else
                rate_idx = b43_plcp_get_bitrate_idx_cck(plcp);
        if (unlikely(rate_idx == -1)) {
index 98e67c18f276471d804e751122ae63c2f26662bf..de0cff3df38950bd3194180d3f964990ce36d723 100644 (file)
@@ -34,7 +34,9 @@ brcmfmac-objs += \
                dhd_common.o \
                dhd_linux.o \
                firmware.o \
-               btcoex.o
+               feature.o \
+               btcoex.o \
+               vendor.o
 brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
                dhd_sdio.o \
                bcmsdh.o
index a16e644e7c08826fed5c6d75a6413b8ff5959bdd..f467cafe3e8f0803459807c03a13f9ab0fc19576 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/core.h>
 #include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/platform_device.h>
@@ -979,18 +978,20 @@ out:
        return ret;
 }
 
+#define BRCMF_SDIO_DEVICE(dev_id)      \
+       {SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)}
+
 /* devices we support, null terminated */
 static const struct sdio_device_id brcmf_sdmmc_ids[] = {
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM,
-                    SDIO_DEVICE_ID_BROADCOM_4335_4339)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)},
-       { /* end: all zeroes */ },
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID),
+       BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID),
+       { /* end: all zeroes */ }
 };
 MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
 
index 0cb591b050b3c726a0657ec55043c3c2648b4e9d..a29ac4977b3a12e009f2947c09bd40d3b604ccde 100644 (file)
@@ -157,7 +157,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
                 */
 
                /* save current */
-               brcmf_dbg(TRACE, "new SCO/eSCO coex algo {save & override}\n");
+               brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");
                brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
                brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
                brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
@@ -165,7 +165,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
                brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
 
                btci->saved_regs_part2 = true;
-               brcmf_dbg(TRACE,
+               brcmf_dbg(INFO,
                          "saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
                          btci->reg50, btci->reg51, btci->reg64,
                          btci->reg65, btci->reg71);
@@ -179,21 +179,21 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
 
        } else if (btci->saved_regs_part2) {
                /* restore previously saved bt params */
-               brcmf_dbg(TRACE, "Do new SCO/eSCO coex algo {restore}\n");
+               brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");
                brcmf_btcoex_params_write(ifp, 50, btci->reg50);
                brcmf_btcoex_params_write(ifp, 51, btci->reg51);
                brcmf_btcoex_params_write(ifp, 64, btci->reg64);
                brcmf_btcoex_params_write(ifp, 65, btci->reg65);
                brcmf_btcoex_params_write(ifp, 71, btci->reg71);
 
-               brcmf_dbg(TRACE,
+               brcmf_dbg(INFO,
                          "restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
                          btci->reg50, btci->reg51, btci->reg64,
                          btci->reg65, btci->reg71);
 
                btci->saved_regs_part2 = false;
        } else {
-               brcmf_err("attempted to restore not saved BTCOEX params\n");
+               brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");
        }
 }
 
@@ -219,14 +219,14 @@ static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
                        break;
                }
 
-               brcmf_dbg(TRACE, "sample[%d], btc_params 27:%x\n", i, param27);
+               brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);
 
                if ((param27 & 0x6) == 2) { /* count both sco & esco  */
                        sco_id_cnt++;
                }
 
                if (sco_id_cnt > 2) {
-                       brcmf_dbg(TRACE,
+                       brcmf_dbg(INFO,
                                  "sco/esco detected, pkt id_cnt:%d samples:%d\n",
                                  sco_id_cnt, i);
                        res = true;
@@ -250,7 +250,7 @@ static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
                brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
                brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
                btci->saved_regs_part1 = true;
-               brcmf_dbg(TRACE,
+               brcmf_dbg(INFO,
                          "saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
                          btci->reg66, btci->reg41,
                          btci->reg68);
@@ -270,7 +270,7 @@ static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
                brcmf_btcoex_params_write(ifp, 66, btci->reg66);
                brcmf_btcoex_params_write(ifp, 41, btci->reg41);
                brcmf_btcoex_params_write(ifp, 68, btci->reg68);
-               brcmf_dbg(TRACE,
+               brcmf_dbg(INFO,
                          "restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
                          btci->reg66, btci->reg41,
                          btci->reg68);
@@ -307,7 +307,7 @@ static void brcmf_btcoex_handler(struct work_struct *work)
                /* DHCP started provide OPPORTUNITY window
                   to get DHCP address
                */
-               brcmf_dbg(TRACE, "DHCP started\n");
+               brcmf_dbg(INFO, "DHCP started\n");
                btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
                if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
                        mod_timer(&btci->timer, btci->timer.expires);
@@ -322,12 +322,12 @@ static void brcmf_btcoex_handler(struct work_struct *work)
 
        case BRCMF_BT_DHCP_OPPR_WIN:
                if (btci->dhcp_done) {
-                       brcmf_dbg(TRACE, "DHCP done before T1 expiration\n");
+                       brcmf_dbg(INFO, "DHCP done before T1 expiration\n");
                        goto idle;
                }
 
                /* DHCP is not over yet, start lowering BT priority */
-               brcmf_dbg(TRACE, "DHCP T1:%d expired\n",
+               brcmf_dbg(INFO, "DHCP T1:%d expired\n",
                          BRCMF_BTCOEX_OPPR_WIN_TIME);
                brcmf_btcoex_boost_wifi(btci, true);
 
@@ -339,9 +339,9 @@ static void brcmf_btcoex_handler(struct work_struct *work)
 
        case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
                if (btci->dhcp_done)
-                       brcmf_dbg(TRACE, "DHCP done before T2 expiration\n");
+                       brcmf_dbg(INFO, "DHCP done before T2 expiration\n");
                else
-                       brcmf_dbg(TRACE, "DHCP T2:%d expired\n",
+                       brcmf_dbg(INFO, "DHCP T2:%d expired\n",
                                  BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
 
                goto idle;
@@ -440,13 +440,13 @@ static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
        /* Stop any bt timer because DHCP session is done */
        btci->dhcp_done = true;
        if (btci->timer_on) {
-               brcmf_dbg(TRACE, "disable BT DHCP Timer\n");
+               brcmf_dbg(INFO, "disable BT DHCP Timer\n");
                btci->timer_on = false;
                del_timer_sync(&btci->timer);
 
                /* schedule worker if transition to IDLE is needed */
                if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
-                       brcmf_dbg(TRACE, "bt_state:%d\n",
+                       brcmf_dbg(INFO, "bt_state:%d\n",
                                  btci->bt_state);
                        schedule_work(&btci->work);
                }
@@ -472,7 +472,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
 
        switch (mode) {
        case BRCMF_BTCOEX_DISABLED:
-               brcmf_dbg(TRACE, "DHCP session starts\n");
+               brcmf_dbg(INFO, "DHCP session starts\n");
                if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
                        return -EBUSY;
                /* Start BT timer only for SCO connection */
@@ -484,14 +484,14 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
                break;
 
        case BRCMF_BTCOEX_ENABLED:
-               brcmf_dbg(TRACE, "DHCP session ends\n");
+               brcmf_dbg(INFO, "DHCP session ends\n");
                if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
                    vif == btci->vif) {
                        brcmf_btcoex_dhcp_end(btci);
                }
                break;
        default:
-               brcmf_dbg(TRACE, "Unknown mode, ignored\n");
+               brcmf_dbg(INFO, "Unknown mode, ignored\n");
        }
        return 0;
 }
index c7c9f15c0fe08170ee2f953e9690a13e605a3291..96800db0536b578922cc8bf8aea924ede640cadc 100644 (file)
@@ -482,30 +482,30 @@ static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci)
 static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci)
 {
        switch (ci->pub.chip) {
-       case BCM4329_CHIP_ID:
+       case BRCM_CC_4329_CHIP_ID:
                ci->pub.ramsize = BCM4329_RAMSIZE;
                break;
-       case BCM43143_CHIP_ID:
+       case BRCM_CC_43143_CHIP_ID:
                ci->pub.ramsize = BCM43143_RAMSIZE;
                break;
-       case BCM43241_CHIP_ID:
+       case BRCM_CC_43241_CHIP_ID:
                ci->pub.ramsize = 0x90000;
                break;
-       case BCM4330_CHIP_ID:
+       case BRCM_CC_4330_CHIP_ID:
                ci->pub.ramsize = 0x48000;
                break;
-       case BCM4334_CHIP_ID:
+       case BRCM_CC_4334_CHIP_ID:
                ci->pub.ramsize = 0x80000;
                break;
-       case BCM4335_CHIP_ID:
+       case BRCM_CC_4335_CHIP_ID:
                ci->pub.ramsize = 0xc0000;
                ci->pub.rambase = 0x180000;
                break;
-       case BCM43362_CHIP_ID:
+       case BRCM_CC_43362_CHIP_ID:
                ci->pub.ramsize = 0x3c000;
                break;
-       case BCM4339_CHIP_ID:
-       case BCM4354_CHIP_ID:
+       case BRCM_CC_4339_CHIP_ID:
+       case BRCM_CC_4354_CHIP_ID:
                ci->pub.ramsize = 0xc0000;
                ci->pub.rambase = 0x180000;
                break;
@@ -682,7 +682,7 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci)
                  ci->pub.chiprev);
 
        if (socitype == SOCI_SB) {
-               if (ci->pub.chip != BCM4329_CHIP_ID) {
+               if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) {
                        brcmf_err("SB chip is not supported\n");
                        return -ENODEV;
                }
@@ -1008,13 +1008,13 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
        chip = container_of(pub, struct brcmf_chip_priv, pub);
 
        switch (pub->chip) {
-       case BCM4354_CHIP_ID:
+       case BRCM_CC_4354_CHIP_ID:
                /* explicitly check SR engine enable bit */
                pmu_cc3_mask = BIT(2);
                /* fall-through */
-       case BCM43241_CHIP_ID:
-       case BCM4335_CHIP_ID:
-       case BCM4339_CHIP_ID:
+       case BRCM_CC_43241_CHIP_ID:
+       case BRCM_CC_4335_CHIP_ID:
+       case BRCM_CC_4339_CHIP_ID:
                /* read PMU chipcontrol register 3 */
                addr = CORE_CC_REG(base, chipcontrol_addr);
                chip->ops->write32(chip->ctx, addr, 3);
index 16f9ab2568a8089c1c38eff8f8998e8fc29ee330..7da6441bcfa8e8cb519cf19379b9553a431fe50a 100644 (file)
  */
 #define BRCMF_DRIVER_FIRMWARE_VERSION_LEN      32
 
-/* Bus independent dongle command */
-struct brcmf_dcmd {
-       uint cmd;               /* common dongle cmd definition */
-       void *buf;              /* pointer to user buffer */
-       uint len;               /* length of user buffer */
-       u8 set;                 /* get or set request (optional) */
-       uint used;              /* bytes read or written (optional) */
-       uint needed;            /* bytes needed (optional) */
-};
-
 /**
  * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
  *
@@ -113,6 +103,10 @@ struct brcmf_pub {
 
        struct brcmf_ampdu_rx_reorder
                *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
+
+       u32 feat_flags;
+       u32 chip_quirks;
+
 #ifdef DEBUG
        struct dentry *dbgfs_dir;
 #endif
@@ -185,7 +179,6 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
 void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state);
-u32 brcmf_get_chip_info(struct brcmf_if *ifp);
 void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
                      bool success);
 
index ed3e32ce8c23ee8fd032ad35520c7fee1e3cb18a..d991f8e3d9ece11e6882d4569e58311ec689c0a5 100644 (file)
@@ -282,6 +282,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
        ptr = strrchr(buf, ' ') + 1;
        strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
 
+       /* set mpc */
+       err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
+       if (err) {
+               brcmf_err("failed setting mpc\n");
+               goto done;
+       }
+
        /*
         * Setup timeout if Beacons are lost and roam is off to report
         * link down
index 03fe8aca4d3261c81f704f89853364f0f0bb7507..be9f4f829192cc020294f08950b0fe995fca10dc 100644 (file)
@@ -41,37 +41,12 @@ void brcmf_debugfs_exit(void)
        root_folder = NULL;
 }
 
-static
-ssize_t brcmf_debugfs_chipinfo_read(struct file *f, char __user *data,
-                                  size_t count, loff_t *ppos)
+static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data)
 {
-       struct brcmf_pub *drvr = f->private_data;
-       struct brcmf_bus *bus = drvr->bus_if;
-       char buf[40];
-       int res;
-
-       /* only allow read from start */
-       if (*ppos > 0)
-               return 0;
-
-       res = scnprintf(buf, sizeof(buf), "chip: %x(%u) rev %u\n",
-                       bus->chip, bus->chip, bus->chiprev);
-       return simple_read_from_buffer(data, count, ppos, buf, res);
-}
-
-static const struct file_operations brcmf_debugfs_chipinfo_ops = {
-       .owner = THIS_MODULE,
-       .open = simple_open,
-       .read = brcmf_debugfs_chipinfo_read
-};
-
-static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr)
-{
-       struct dentry *dentry =  drvr->dbgfs_dir;
+       struct brcmf_bus *bus = dev_get_drvdata(seq->private);
 
-       if (!IS_ERR_OR_NULL(dentry))
-               debugfs_create_file("chipinfo", S_IRUGO, dentry, drvr,
-                                   &brcmf_debugfs_chipinfo_ops);
+       seq_printf(seq, "chip: %x(%u) rev %u\n",
+                  bus->chip, bus->chip, bus->chiprev);
        return 0;
 }
 
@@ -83,7 +58,8 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr)
                return -ENODEV;
 
        drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
-       brcmf_debugfs_create_chipinfo(drvr);
+       brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read);
+
        return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
 }
 
@@ -98,148 +74,44 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
        return drvr->dbgfs_dir;
 }
 
-static
-ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data,
-                                       size_t count, loff_t *ppos)
-{
-       struct brcmf_sdio_count *sdcnt = f->private_data;
-       char buf[750];
-       int res;
-
-       /* only allow read from start */
-       if (*ppos > 0)
-               return 0;
-
-       res = scnprintf(buf, sizeof(buf),
-                       "intrcount:    %u\nlastintrs:    %u\n"
-                       "pollcnt:      %u\nregfails:     %u\n"
-                       "tx_sderrs:    %u\nfcqueued:     %u\n"
-                       "rxrtx:        %u\nrx_toolong:   %u\n"
-                       "rxc_errors:   %u\nrx_hdrfail:   %u\n"
-                       "rx_badhdr:    %u\nrx_badseq:    %u\n"
-                       "fc_rcvd:      %u\nfc_xoff:      %u\n"
-                       "fc_xon:       %u\nrxglomfail:   %u\n"
-                       "rxglomframes: %u\nrxglompkts:   %u\n"
-                       "f2rxhdrs:     %u\nf2rxdata:     %u\n"
-                       "f2txdata:     %u\nf1regdata:    %u\n"
-                       "tickcnt:      %u\ntx_ctlerrs:   %lu\n"
-                       "tx_ctlpkts:   %lu\nrx_ctlerrs:   %lu\n"
-                       "rx_ctlpkts:   %lu\nrx_readahead: %lu\n",
-                       sdcnt->intrcount, sdcnt->lastintrs,
-                       sdcnt->pollcnt, sdcnt->regfails,
-                       sdcnt->tx_sderrs, sdcnt->fcqueued,
-                       sdcnt->rxrtx, sdcnt->rx_toolong,
-                       sdcnt->rxc_errors, sdcnt->rx_hdrfail,
-                       sdcnt->rx_badhdr, sdcnt->rx_badseq,
-                       sdcnt->fc_rcvd, sdcnt->fc_xoff,
-                       sdcnt->fc_xon, sdcnt->rxglomfail,
-                       sdcnt->rxglomframes, sdcnt->rxglompkts,
-                       sdcnt->f2rxhdrs, sdcnt->f2rxdata,
-                       sdcnt->f2txdata, sdcnt->f1regdata,
-                       sdcnt->tickcnt, sdcnt->tx_ctlerrs,
-                       sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
-                       sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
-
-       return simple_read_from_buffer(data, count, ppos, buf, res);
-}
-
-static const struct file_operations brcmf_debugfs_sdio_counter_ops = {
-       .owner = THIS_MODULE,
-       .open = simple_open,
-       .read = brcmf_debugfs_sdio_counter_read
+struct brcmf_debugfs_entry {
+       int (*read)(struct seq_file *seq, void *data);
+       struct brcmf_pub *drvr;
 };
 
-void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
-                                    struct brcmf_sdio_count *sdcnt)
+static int brcmf_debugfs_entry_open(struct inode *inode, struct file *f)
 {
-       struct dentry *dentry = drvr->dbgfs_dir;
+       struct brcmf_debugfs_entry *entry = inode->i_private;
 
-       if (!IS_ERR_OR_NULL(dentry))
-               debugfs_create_file("counters", S_IRUGO, dentry,
-                                   sdcnt, &brcmf_debugfs_sdio_counter_ops);
-}
-
-static
-ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
-                                    size_t count, loff_t *ppos)
-{
-       struct brcmf_fws_stats *fwstats = f->private_data;
-       char buf[650];
-       int res;
-
-       /* only allow read from start */
-       if (*ppos > 0)
-               return 0;
-
-       res = scnprintf(buf, sizeof(buf),
-                       "header_pulls:      %u\n"
-                       "header_only_pkt:   %u\n"
-                       "tlv_parse_failed:  %u\n"
-                       "tlv_invalid_type:  %u\n"
-                       "mac_update_fails:  %u\n"
-                       "ps_update_fails:   %u\n"
-                       "if_update_fails:   %u\n"
-                       "pkt2bus:           %u\n"
-                       "generic_error:     %u\n"
-                       "rollback_success:  %u\n"
-                       "rollback_failed:   %u\n"
-                       "delayq_full:       %u\n"
-                       "supprq_full:       %u\n"
-                       "txs_indicate:      %u\n"
-                       "txs_discard:       %u\n"
-                       "txs_suppr_core:    %u\n"
-                       "txs_suppr_ps:      %u\n"
-                       "txs_tossed:        %u\n"
-                       "txs_host_tossed:   %u\n"
-                       "bus_flow_block:    %u\n"
-                       "fws_flow_block:    %u\n"
-                       "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
-                       "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
-                       fwstats->header_pulls,
-                       fwstats->header_only_pkt,
-                       fwstats->tlv_parse_failed,
-                       fwstats->tlv_invalid_type,
-                       fwstats->mac_update_failed,
-                       fwstats->mac_ps_update_failed,
-                       fwstats->if_update_failed,
-                       fwstats->pkt2bus,
-                       fwstats->generic_error,
-                       fwstats->rollback_success,
-                       fwstats->rollback_failed,
-                       fwstats->delayq_full_error,
-                       fwstats->supprq_full_error,
-                       fwstats->txs_indicate,
-                       fwstats->txs_discard,
-                       fwstats->txs_supp_core,
-                       fwstats->txs_supp_ps,
-                       fwstats->txs_tossed,
-                       fwstats->txs_host_tossed,
-                       fwstats->bus_flow_block,
-                       fwstats->fws_flow_block,
-                       fwstats->send_pkts[0], fwstats->send_pkts[1],
-                       fwstats->send_pkts[2], fwstats->send_pkts[3],
-                       fwstats->send_pkts[4],
-                       fwstats->requested_sent[0],
-                       fwstats->requested_sent[1],
-                       fwstats->requested_sent[2],
-                       fwstats->requested_sent[3],
-                       fwstats->requested_sent[4]);
-
-       return simple_read_from_buffer(data, count, ppos, buf, res);
+       return single_open(f, entry->read, entry->drvr->bus_if->dev);
 }
 
-static const struct file_operations brcmf_debugfs_fws_stats_ops = {
+static const struct file_operations brcmf_debugfs_def_ops = {
        .owner = THIS_MODULE,
-       .open = simple_open,
-       .read = brcmf_debugfs_fws_stats_read
+       .open = brcmf_debugfs_entry_open,
+       .release = single_release,
+       .read = seq_read,
+       .llseek = seq_lseek
 };
 
-void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
-                                   struct brcmf_fws_stats *stats)
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+                           int (*read_fn)(struct seq_file *seq, void *data))
 {
        struct dentry *dentry =  drvr->dbgfs_dir;
+       struct brcmf_debugfs_entry *entry;
+
+       if (IS_ERR_OR_NULL(dentry))
+               return -ENOENT;
+
+       entry = devm_kzalloc(drvr->bus_if->dev, sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       entry->read = read_fn;
+       entry->drvr = drvr;
+
+       dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry,
+                                    &brcmf_debugfs_def_ops);
 
-       if (!IS_ERR_OR_NULL(dentry))
-               debugfs_create_file("fws_stats", S_IRUGO, dentry,
-                                   stats, &brcmf_debugfs_fws_stats_ops);
+       return PTR_ERR_OR_ZERO(dentry);
 }
index ef52ed7abc69349640081292f5f862277900607c..6eade7c60c635542d893d8224e614ec88cc81392 100644 (file)
@@ -100,68 +100,6 @@ do {                                                                       \
 
 extern int brcmf_msg_level;
 
-/*
- * hold counter variables used in brcmfmac sdio driver.
- */
-struct brcmf_sdio_count {
-       uint intrcount;         /* Count of device interrupt callbacks */
-       uint lastintrs;         /* Count as of last watchdog timer */
-       uint pollcnt;           /* Count of active polls */
-       uint regfails;          /* Count of R_REG failures */
-       uint tx_sderrs;         /* Count of tx attempts with sd errors */
-       uint fcqueued;          /* Tx packets that got queued */
-       uint rxrtx;             /* Count of rtx requests (NAK to dongle) */
-       uint rx_toolong;        /* Receive frames too long to receive */
-       uint rxc_errors;        /* SDIO errors when reading control frames */
-       uint rx_hdrfail;        /* SDIO errors on header reads */
-       uint rx_badhdr;         /* Bad received headers (roosync?) */
-       uint rx_badseq;         /* Mismatched rx sequence number */
-       uint fc_rcvd;           /* Number of flow-control events received */
-       uint fc_xoff;           /* Number which turned on flow-control */
-       uint fc_xon;            /* Number which turned off flow-control */
-       uint rxglomfail;        /* Failed deglom attempts */
-       uint rxglomframes;      /* Number of glom frames (superframes) */
-       uint rxglompkts;        /* Number of packets from glom frames */
-       uint f2rxhdrs;          /* Number of header reads */
-       uint f2rxdata;          /* Number of frame data reads */
-       uint f2txdata;          /* Number of f2 frame writes */
-       uint f1regdata;         /* Number of f1 register accesses */
-       uint tickcnt;           /* Number of watchdog been schedule */
-       ulong tx_ctlerrs;       /* Err of sending ctrl frames */
-       ulong tx_ctlpkts;       /* Ctrl frames sent to dongle */
-       ulong rx_ctlerrs;       /* Err of processing rx ctrl frames */
-       ulong rx_ctlpkts;       /* Ctrl frames processed from dongle */
-       ulong rx_readahead_cnt; /* packets where header read-ahead was used */
-};
-
-struct brcmf_fws_stats {
-       u32 tlv_parse_failed;
-       u32 tlv_invalid_type;
-       u32 header_only_pkt;
-       u32 header_pulls;
-       u32 pkt2bus;
-       u32 send_pkts[5];
-       u32 requested_sent[5];
-       u32 generic_error;
-       u32 mac_update_failed;
-       u32 mac_ps_update_failed;
-       u32 if_update_failed;
-       u32 packet_request_failed;
-       u32 credit_request_failed;
-       u32 rollback_success;
-       u32 rollback_failed;
-       u32 delayq_full_error;
-       u32 supprq_full_error;
-       u32 txs_indicate;
-       u32 txs_discard;
-       u32 txs_supp_core;
-       u32 txs_supp_ps;
-       u32 txs_tossed;
-       u32 txs_host_tossed;
-       u32 bus_flow_block;
-       u32 fws_flow_block;
-};
-
 struct brcmf_pub;
 #ifdef DEBUG
 void brcmf_debugfs_init(void);
@@ -169,10 +107,8 @@ void brcmf_debugfs_exit(void);
 int brcmf_debugfs_attach(struct brcmf_pub *drvr);
 void brcmf_debugfs_detach(struct brcmf_pub *drvr);
 struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
-void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
-                                    struct brcmf_sdio_count *sdcnt);
-void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
-                                   struct brcmf_fws_stats *stats);
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+                           int (*read_fn)(struct seq_file *seq, void *data));
 #else
 static inline void brcmf_debugfs_init(void)
 {
@@ -187,9 +123,11 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
 static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
 {
 }
-static inline void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr,
-                                                 struct brcmf_fws_stats *stats)
+static inline
+int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
+                           int (*read_fn)(struct seq_file *seq, void *data))
 {
+       return 0;
 }
 #endif
 
index 09dd8c13d8448392372299c6eb8954132fca9c72..bf448081d6fb98d569f1848b0e665db5a8cd9d91 100644 (file)
@@ -30,6 +30,7 @@
 #include "wl_cfg80211.h"
 #include "fwil.h"
 #include "fwsignal.h"
+#include "feature.h"
 #include "proto.h"
 
 MODULE_AUTHOR("Broadcom Corporation");
@@ -936,6 +937,8 @@ int brcmf_bus_start(struct device *dev)
        if (ret < 0)
                goto fail;
 
+       brcmf_feat_attach(drvr);
+
        ret = brcmf_fws_init(drvr);
        if (ret < 0)
                goto fail;
@@ -1073,16 +1076,6 @@ int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
        return !err;
 }
 
-/*
- * return chip id and rev of the device encoded in u32.
- */
-u32 brcmf_get_chip_info(struct brcmf_if *ifp)
-{
-       struct brcmf_bus *bus = ifp->drvr->bus_if;
-
-       return bus->chip << 4 | bus->chiprev;
-}
-
 static void brcmf_driver_register(struct work_struct *work)
 {
 #ifdef CONFIG_BRCMFMAC_SDIO
index 8fa0dbbbda72b6ed1d1dc84020594287cf595a37..67d91d5cc13d85de1e5c0395842c90f13d54cb16 100644 (file)
@@ -391,6 +391,40 @@ struct brcmf_sdio_hdrinfo {
        u16 tail_pad;
 };
 
+/*
+ * hold counter variables
+ */
+struct brcmf_sdio_count {
+       uint intrcount;         /* Count of device interrupt callbacks */
+       uint lastintrs;         /* Count as of last watchdog timer */
+       uint pollcnt;           /* Count of active polls */
+       uint regfails;          /* Count of R_REG failures */
+       uint tx_sderrs;         /* Count of tx attempts with sd errors */
+       uint fcqueued;          /* Tx packets that got queued */
+       uint rxrtx;             /* Count of rtx requests (NAK to dongle) */
+       uint rx_toolong;        /* Receive frames too long to receive */
+       uint rxc_errors;        /* SDIO errors when reading control frames */
+       uint rx_hdrfail;        /* SDIO errors on header reads */
+       uint rx_badhdr;         /* Bad received headers (roosync?) */
+       uint rx_badseq;         /* Mismatched rx sequence number */
+       uint fc_rcvd;           /* Number of flow-control events received */
+       uint fc_xoff;           /* Number which turned on flow-control */
+       uint fc_xon;            /* Number which turned off flow-control */
+       uint rxglomfail;        /* Failed deglom attempts */
+       uint rxglomframes;      /* Number of glom frames (superframes) */
+       uint rxglompkts;        /* Number of packets from glom frames */
+       uint f2rxhdrs;          /* Number of header reads */
+       uint f2rxdata;          /* Number of frame data reads */
+       uint f2txdata;          /* Number of f2 frame writes */
+       uint f1regdata;         /* Number of f1 register accesses */
+       uint tickcnt;           /* Number of watchdog been schedule */
+       ulong tx_ctlerrs;       /* Err of sending ctrl frames */
+       ulong tx_ctlpkts;       /* Ctrl frames sent to dongle */
+       ulong rx_ctlerrs;       /* Err of processing rx ctrl frames */
+       ulong rx_ctlpkts;       /* Ctrl frames processed from dongle */
+       ulong rx_readahead_cnt; /* packets where header read-ahead was used */
+};
+
 /* misc chip info needed by some of the routines */
 /* Private data for SDIO bus interaction */
 struct brcmf_sdio {
@@ -620,40 +654,46 @@ enum brcmf_firmware_type {
        name ## _FIRMWARE_NAME, name ## _NVRAM_NAME
 
 static const struct brcmf_firmware_names brcmf_fwname_data[] = {
-       { BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
-       { BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
-       { BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
-       { BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
-       { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
-       { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
-       { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
-       { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
-       { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
-       { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
+       { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) },
+       { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) },
+       { BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) },
+       { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) },
+       { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
+       { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
+       { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
+       { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
+       { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) },
+       { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) }
 };
 
-static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci,
-                                        enum brcmf_firmware_type type)
+static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci,
+                                 struct brcmf_sdio_dev *sdiodev)
 {
        int i;
 
        for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
                if (brcmf_fwname_data[i].chipid == ci->chip &&
-                   brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) {
-                       switch (type) {
-                       case BRCMF_FIRMWARE_BIN:
-                               return brcmf_fwname_data[i].bin;
-                       case BRCMF_FIRMWARE_NVRAM:
-                               return brcmf_fwname_data[i].nv;
-                       default:
-                               brcmf_err("invalid firmware type (%d)\n", type);
-                               return NULL;
-                       }
-               }
+                   brcmf_fwname_data[i].revmsk & BIT(ci->chiprev))
+                       break;
        }
-       brcmf_err("Unknown chipid %d [%d]\n",
-                 ci->chip, ci->chiprev);
-       return NULL;
+
+       if (i == ARRAY_SIZE(brcmf_fwname_data)) {
+               brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev);
+               return -ENODEV;
+       }
+
+       /* check if firmware path is provided by module parameter */
+       if (brcmf_firmware_path[0] != '\0') {
+               if (brcmf_firmware_path[strlen(brcmf_firmware_path) - 1] != '/')
+                       strcat(brcmf_firmware_path, "/");
+
+               strcpy(sdiodev->fw_name, brcmf_firmware_path);
+               strcpy(sdiodev->nvram_name, brcmf_firmware_path);
+       }
+       strcat(sdiodev->fw_name, brcmf_fwname_data[i].bin);
+       strcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv);
+
+       return 0;
 }
 
 static void pkt_align(struct sk_buff *p, int len, int align)
@@ -2898,16 +2938,13 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
 }
 
 #ifdef DEBUG
-static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
-                                  struct sdpcm_shared *sh, char __user *data,
-                                  size_t count)
+static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus,
+                                  struct sdpcm_shared *sh)
 {
        u32 addr, console_ptr, console_size, console_index;
        char *conbuf = NULL;
        __le32 sh_val;
        int rv;
-       loff_t pos = 0;
-       int nbytes = 0;
 
        /* obtain console information from device memory */
        addr = sh->console_addr + offsetof(struct rte_console, log_le);
@@ -2945,33 +2982,24 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
        if (rv < 0)
                goto done;
 
-       rv = simple_read_from_buffer(data, count, &pos,
-                                    conbuf + console_index,
-                                    console_size - console_index);
+       rv = seq_write(seq, conbuf + console_index,
+                      console_size - console_index);
        if (rv < 0)
                goto done;
 
-       nbytes = rv;
-       if (console_index > 0) {
-               pos = 0;
-               rv = simple_read_from_buffer(data+nbytes, count, &pos,
-                                            conbuf, console_index - 1);
-               if (rv < 0)
-                       goto done;
-               rv += nbytes;
-       }
+       if (console_index > 0)
+               rv = seq_write(seq, conbuf, console_index - 1);
+
 done:
        vfree(conbuf);
        return rv;
 }
 
-static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
-                               char __user *data, size_t count)
+static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus,
+                               struct sdpcm_shared *sh)
 {
-       int error, res;
-       char buf[350];
+       int error;
        struct brcmf_trap_info tr;
-       loff_t pos = 0;
 
        if ((sh->flags & SDPCM_SHARED_TRAP) == 0) {
                brcmf_dbg(INFO, "no trap in firmware\n");
@@ -2983,34 +3011,30 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
        if (error < 0)
                return error;
 
-       res = scnprintf(buf, sizeof(buf),
-                       "dongle trap info: type 0x%x @ epc 0x%08x\n"
-                       "  cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
-                       "  lr   0x%08x pc   0x%08x offset 0x%x\n"
-                       "  r0   0x%08x r1   0x%08x r2 0x%08x r3 0x%08x\n"
-                       "  r4   0x%08x r5   0x%08x r6 0x%08x r7 0x%08x\n",
-                       le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
-                       le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
-                       le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
-                       le32_to_cpu(tr.pc), sh->trap_addr,
-                       le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
-                       le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
-                       le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
-                       le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
-
-       return simple_read_from_buffer(data, count, &pos, buf, res);
+       seq_printf(seq,
+                  "dongle trap info: type 0x%x @ epc 0x%08x\n"
+                  "  cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
+                  "  lr   0x%08x pc   0x%08x offset 0x%x\n"
+                  "  r0   0x%08x r1   0x%08x r2 0x%08x r3 0x%08x\n"
+                  "  r4   0x%08x r5   0x%08x r6 0x%08x r7 0x%08x\n",
+                  le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
+                  le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
+                  le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
+                  le32_to_cpu(tr.pc), sh->trap_addr,
+                  le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
+                  le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
+                  le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
+                  le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
+
+       return 0;
 }
 
-static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
-                                 struct sdpcm_shared *sh, char __user *data,
-                                 size_t count)
+static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus,
+                                 struct sdpcm_shared *sh)
 {
        int error = 0;
-       char buf[200];
        char file[80] = "?";
        char expr[80] = "<???>";
-       int res;
-       loff_t pos = 0;
 
        if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
                brcmf_dbg(INFO, "firmware not built with -assert\n");
@@ -3035,10 +3059,9 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
        }
        sdio_release_host(bus->sdiodev->func[1]);
 
-       res = scnprintf(buf, sizeof(buf),
-                       "dongle assert: %s:%d: assert(%s)\n",
-                       file, sh->assert_line, expr);
-       return simple_read_from_buffer(data, count, &pos, buf, res);
+       seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n",
+                  file, sh->assert_line, expr);
+       return 0;
 }
 
 static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
@@ -3062,59 +3085,75 @@ static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
        return 0;
 }
 
-static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data,
-                               size_t count, loff_t *ppos)
+static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus)
 {
        int error = 0;
        struct sdpcm_shared sh;
-       int nbytes = 0;
-       loff_t pos = *ppos;
-
-       if (pos != 0)
-               return 0;
 
        error = brcmf_sdio_readshared(bus, &sh);
        if (error < 0)
                goto done;
 
-       error = brcmf_sdio_assert_info(bus, &sh, data, count);
+       error = brcmf_sdio_assert_info(seq, bus, &sh);
        if (error < 0)
                goto done;
-       nbytes = error;
 
-       error = brcmf_sdio_trap_info(bus, &sh, data+nbytes, count);
+       error = brcmf_sdio_trap_info(seq, bus, &sh);
        if (error < 0)
                goto done;
-       nbytes += error;
 
-       error = brcmf_sdio_dump_console(bus, &sh, data+nbytes, count);
-       if (error < 0)
-               goto done;
-       nbytes += error;
+       error = brcmf_sdio_dump_console(seq, bus, &sh);
 
-       error = nbytes;
-       *ppos += nbytes;
 done:
        return error;
 }
 
-static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data,
-                                       size_t count, loff_t *ppos)
+static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data)
 {
-       struct brcmf_sdio *bus = f->private_data;
-       int res;
+       struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+       struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus;
 
-       res = brcmf_sdio_died_dump(bus, data, count, ppos);
-       if (res > 0)
-               *ppos += res;
-       return (ssize_t)res;
+       return brcmf_sdio_died_dump(seq, bus);
 }
 
-static const struct file_operations brcmf_sdio_forensic_ops = {
-       .owner = THIS_MODULE,
-       .open = simple_open,
-       .read = brcmf_sdio_forensic_read
-};
+static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+       struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt;
+
+       seq_printf(seq,
+                  "intrcount:    %u\nlastintrs:    %u\n"
+                  "pollcnt:      %u\nregfails:     %u\n"
+                  "tx_sderrs:    %u\nfcqueued:     %u\n"
+                  "rxrtx:        %u\nrx_toolong:   %u\n"
+                  "rxc_errors:   %u\nrx_hdrfail:   %u\n"
+                  "rx_badhdr:    %u\nrx_badseq:    %u\n"
+                  "fc_rcvd:      %u\nfc_xoff:      %u\n"
+                  "fc_xon:       %u\nrxglomfail:   %u\n"
+                  "rxglomframes: %u\nrxglompkts:   %u\n"
+                  "f2rxhdrs:     %u\nf2rxdata:     %u\n"
+                  "f2txdata:     %u\nf1regdata:    %u\n"
+                  "tickcnt:      %u\ntx_ctlerrs:   %lu\n"
+                  "tx_ctlpkts:   %lu\nrx_ctlerrs:   %lu\n"
+                  "rx_ctlpkts:   %lu\nrx_readahead: %lu\n",
+                  sdcnt->intrcount, sdcnt->lastintrs,
+                  sdcnt->pollcnt, sdcnt->regfails,
+                  sdcnt->tx_sderrs, sdcnt->fcqueued,
+                  sdcnt->rxrtx, sdcnt->rx_toolong,
+                  sdcnt->rxc_errors, sdcnt->rx_hdrfail,
+                  sdcnt->rx_badhdr, sdcnt->rx_badseq,
+                  sdcnt->fc_rcvd, sdcnt->fc_xoff,
+                  sdcnt->fc_xon, sdcnt->rxglomfail,
+                  sdcnt->rxglomframes, sdcnt->rxglompkts,
+                  sdcnt->f2rxhdrs, sdcnt->f2rxdata,
+                  sdcnt->f2txdata, sdcnt->f1regdata,
+                  sdcnt->tickcnt, sdcnt->tx_ctlerrs,
+                  sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
+                  sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
+
+       return 0;
+}
 
 static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
 {
@@ -3124,9 +3163,9 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
        if (IS_ERR_OR_NULL(dentry))
                return;
 
-       debugfs_create_file("forensics", S_IRUGO, dentry, bus,
-                           &brcmf_sdio_forensic_ops);
-       brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
+       brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read);
+       brcmf_debugfs_add_entry(drvr, "counters",
+                               brcmf_debugfs_sdio_count_read);
        debugfs_create_u32("console_interval", 0644, dentry,
                           &bus->console_interval);
 }
@@ -3598,17 +3637,17 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
                return;
 
        switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) {
-       case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12):
+       case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12):
                str_tab = sdiod_drvstr_tab1_1v8;
                str_mask = 0x00003800;
                str_shift = 11;
                break;
-       case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17):
+       case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17):
                str_tab = sdiod_drvstr_tab6_1v8;
                str_mask = 0x00001800;
                str_shift = 11;
                break;
-       case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
+       case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17):
                /* note: 43143 does not support tristate */
                i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
                if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) {
@@ -3619,7 +3658,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
                        brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n",
                                  ci->name, drivestrength);
                break;
-       case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13):
+       case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13):
                str_tab = sdiod_drive_strength_tab5_1v8;
                str_mask = 0x00003800;
                str_shift = 11;
@@ -3720,12 +3759,12 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr)
        u32 val, rev;
 
        val = brcmf_sdiod_regrl(sdiodev, addr, NULL);
-       if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
+       if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID &&
            addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) {
                rev = (val & CID_REV_MASK) >> CID_REV_SHIFT;
                if (rev >= 2) {
                        val &= ~CID_ID_MASK;
-                       val |= BCM4339_CHIP_ID;
+                       val |= BRCM_CC_4339_CHIP_ID;
                }
        }
        return val;
@@ -4127,11 +4166,12 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
        brcmf_sdio_debugfs_create(bus);
        brcmf_dbg(INFO, "completed!!\n");
 
+       ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev);
+       if (ret)
+               goto fail;
+
        ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM,
-                                    brcmf_sdio_get_fwname(bus->ci,
-                                                          BRCMF_FIRMWARE_BIN),
-                                    brcmf_sdio_get_fwname(bus->ci,
-                                                          BRCMF_FIRMWARE_NVRAM),
+                                    sdiodev->fw_name, sdiodev->nvram_name,
                                     brcmf_sdio_firmware_callback);
        if (ret != 0) {
                brcmf_err("async firmware request failed: %d\n", ret);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
new file mode 100644 (file)
index 0000000..50877e3
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/netdevice.h>
+
+#include <brcm_hw_ids.h>
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_dbg.h"
+#include "fwil.h"
+#include "feature.h"
+
+/*
+ * firmware error code received if iovar is unsupported.
+ */
+#define EBRCMF_FEAT_UNSUPPORTED                23
+
+/*
+ * expand feature list to array of feature strings.
+ */
+#define BRCMF_FEAT_DEF(_f) \
+       #_f,
+static const char *brcmf_feat_names[] = {
+       BRCMF_FEAT_LIST
+};
+#undef BRCMF_FEAT_DEF
+
+#ifdef DEBUG
+/*
+ * expand quirk list to array of quirk strings.
+ */
+#define BRCMF_QUIRK_DEF(_q) \
+       #_q,
+static const char * const brcmf_quirk_names[] = {
+       BRCMF_QUIRK_LIST
+};
+#undef BRCMF_QUIRK_DEF
+
+/**
+ * brcmf_feat_debugfs_read() - expose feature info to debugfs.
+ *
+ * @seq: sequence for debugfs entry.
+ * @data: raw data pointer.
+ */
+static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+       u32 feats = bus_if->drvr->feat_flags;
+       u32 quirks = bus_if->drvr->chip_quirks;
+       int id;
+
+       seq_printf(seq, "Features: %08x\n", feats);
+       for (id = 0; id < BRCMF_FEAT_LAST; id++)
+               if (feats & BIT(id))
+                       seq_printf(seq, "\t%s\n", brcmf_feat_names[id]);
+       seq_printf(seq, "\nQuirks:   %08x\n", quirks);
+       for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++)
+               if (quirks & BIT(id))
+                       seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]);
+       return 0;
+}
+#else
+static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data)
+{
+       return 0;
+}
+#endif /* DEBUG */
+
+/**
+ * brcmf_feat_iovar_int_get() - determine feature through iovar query.
+ *
+ * @ifp: interface to query.
+ * @id: feature id.
+ * @name: iovar name.
+ */
+static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp,
+                                    enum brcmf_feat_id id, char *name)
+{
+       u32 data;
+       int err;
+
+       err = brcmf_fil_iovar_int_get(ifp, name, &data);
+       if (err == 0) {
+               brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
+               ifp->drvr->feat_flags |= BIT(id);
+       } else {
+               brcmf_dbg(TRACE, "%s feature check failed: %d\n",
+                         brcmf_feat_names[id], err);
+       }
+}
+
+void brcmf_feat_attach(struct brcmf_pub *drvr)
+{
+       struct brcmf_if *ifp = drvr->iflist[0];
+
+       brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
+
+       /* set chip related quirks */
+       switch (drvr->bus_if->chip) {
+       case BRCM_CC_43236_CHIP_ID:
+               drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH);
+               break;
+       case BRCM_CC_4329_CHIP_ID:
+               drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC);
+               break;
+       default:
+               /* no quirks */
+               break;
+       }
+
+       brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read);
+}
+
+bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id)
+{
+       return (ifp->drvr->feat_flags & BIT(id));
+}
+
+bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
+                                enum brcmf_feat_quirk quirk)
+{
+       return (ifp->drvr->chip_quirks & BIT(quirk));
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
new file mode 100644 (file)
index 0000000..961d175
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _BRCMF_FEATURE_H
+#define _BRCMF_FEATURE_H
+
+/*
+ * Features:
+ *
+ * MCHAN: multi-channel for concurrent P2P.
+ */
+#define BRCMF_FEAT_LIST \
+       BRCMF_FEAT_DEF(MCHAN)
+/*
+ * Quirks:
+ *
+ * AUTO_AUTH: workaround needed for automatic authentication type.
+ * NEED_MPC: driver needs to disable MPC during scanning operation.
+ */
+#define BRCMF_QUIRK_LIST \
+       BRCMF_QUIRK_DEF(AUTO_AUTH) \
+       BRCMF_QUIRK_DEF(NEED_MPC)
+
+#define BRCMF_FEAT_DEF(_f) \
+       BRCMF_FEAT_ ## _f,
+/*
+ * expand feature list to enumeration.
+ */
+enum brcmf_feat_id {
+       BRCMF_FEAT_LIST
+       BRCMF_FEAT_LAST
+};
+#undef BRCMF_FEAT_DEF
+
+#define BRCMF_QUIRK_DEF(_q) \
+       BRCMF_FEAT_QUIRK_ ## _q,
+/*
+ * expand quirk list to enumeration.
+ */
+enum brcmf_feat_quirk {
+       BRCMF_QUIRK_LIST
+       BRCMF_FEAT_QUIRK_LAST
+};
+#undef BRCMF_QUIRK_DEF
+
+/**
+ * brcmf_feat_attach() - determine features and quirks.
+ *
+ * @drvr: driver instance.
+ */
+void brcmf_feat_attach(struct brcmf_pub *drvr);
+
+/**
+ * brcmf_feat_is_enabled() - query feature.
+ *
+ * @ifp: interface instance.
+ * @id: feature id to check.
+ *
+ * Return: true is feature is enabled; otherwise false.
+ */
+bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id);
+
+/**
+ * brcmf_feat_is_quirk_enabled() - query chip quirk.
+ *
+ * @ifp: interface instance.
+ * @quirk: quirk id to check.
+ *
+ * Return: true is quirk is enabled; otherwise false.
+ */
+bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp,
+                                enum brcmf_feat_quirk quirk);
+
+#endif /* _BRCMF_FEATURE_H */
index 7b7d237c1ddb274ff663c24653a87b2727a89cdd..8ea9f283d2b8065f4766978c2e43e05e6576fba6 100644 (file)
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/firmware.h>
+#include <linux/module.h>
 
 #include "dhd_dbg.h"
 #include "firmware.h"
 
+char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
+module_param_string(firmware_path, brcmf_firmware_path,
+                   BRCMF_FW_PATH_LEN, 0440);
+
 enum nvram_parser_state {
        IDLE,
        KEY,
index 6431bfd7afffc6c544fa1e292b3a6a98cfd4a664..4d3482356b77b0e068e1dc095b8888a7c8477094 100644 (file)
 #define BRCMF_FW_REQ_FLAGS             0x00F0
 #define  BRCMF_FW_REQ_NV_OPTIONAL      0x0010
 
+#define        BRCMF_FW_PATH_LEN       256
+#define        BRCMF_FW_NAME_LEN       32
+
+extern char brcmf_firmware_path[];
+
 void brcmf_fw_nvram_free(void *nvram);
 /*
  * Request firmware(s) asynchronously. When the asynchronous request
index 59a5af5bf994d88b3e4f969c8e0633de3bd57d87..ded328f80cd1237454274ce53fd39433249b0e85 100644 (file)
@@ -54,7 +54,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
        if (err >= 0)
                err = 0;
        else
-               brcmf_err("Failed err=%d\n", err);
+               brcmf_dbg(FIL, "Failed err=%d\n", err);
 
        return err;
 }
index 699908de314a94ff3f382f62536756b3f4a5eed2..d42f7d04b65f115a6a370f2015015b6b03718b2a 100644 (file)
@@ -454,6 +454,34 @@ struct brcmf_fws_macdesc_table {
        struct brcmf_fws_mac_descriptor other;
 };
 
+struct brcmf_fws_stats {
+       u32 tlv_parse_failed;
+       u32 tlv_invalid_type;
+       u32 header_only_pkt;
+       u32 header_pulls;
+       u32 pkt2bus;
+       u32 send_pkts[5];
+       u32 requested_sent[5];
+       u32 generic_error;
+       u32 mac_update_failed;
+       u32 mac_ps_update_failed;
+       u32 if_update_failed;
+       u32 packet_request_failed;
+       u32 credit_request_failed;
+       u32 rollback_success;
+       u32 rollback_failed;
+       u32 delayq_full_error;
+       u32 supprq_full_error;
+       u32 txs_indicate;
+       u32 txs_discard;
+       u32 txs_supp_core;
+       u32 txs_supp_ps;
+       u32 txs_tossed;
+       u32 txs_host_tossed;
+       u32 bus_flow_block;
+       u32 fws_flow_block;
+};
+
 struct brcmf_fws_info {
        struct brcmf_pub *drvr;
        spinlock_t spinlock;
@@ -2017,6 +2045,75 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
        brcmf_fws_unlock(fws);
 }
 
+#ifdef DEBUG
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(seq->private);
+       struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats;
+
+       seq_printf(seq,
+                  "header_pulls:      %u\n"
+                  "header_only_pkt:   %u\n"
+                  "tlv_parse_failed:  %u\n"
+                  "tlv_invalid_type:  %u\n"
+                  "mac_update_fails:  %u\n"
+                  "ps_update_fails:   %u\n"
+                  "if_update_fails:   %u\n"
+                  "pkt2bus:           %u\n"
+                  "generic_error:     %u\n"
+                  "rollback_success:  %u\n"
+                  "rollback_failed:   %u\n"
+                  "delayq_full:       %u\n"
+                  "supprq_full:       %u\n"
+                  "txs_indicate:      %u\n"
+                  "txs_discard:       %u\n"
+                  "txs_suppr_core:    %u\n"
+                  "txs_suppr_ps:      %u\n"
+                  "txs_tossed:        %u\n"
+                  "txs_host_tossed:   %u\n"
+                  "bus_flow_block:    %u\n"
+                  "fws_flow_block:    %u\n"
+                  "send_pkts:         BK:%u BE:%u VO:%u VI:%u BCMC:%u\n"
+                  "requested_sent:    BK:%u BE:%u VO:%u VI:%u BCMC:%u\n",
+                  fwstats->header_pulls,
+                  fwstats->header_only_pkt,
+                  fwstats->tlv_parse_failed,
+                  fwstats->tlv_invalid_type,
+                  fwstats->mac_update_failed,
+                  fwstats->mac_ps_update_failed,
+                  fwstats->if_update_failed,
+                  fwstats->pkt2bus,
+                  fwstats->generic_error,
+                  fwstats->rollback_success,
+                  fwstats->rollback_failed,
+                  fwstats->delayq_full_error,
+                  fwstats->supprq_full_error,
+                  fwstats->txs_indicate,
+                  fwstats->txs_discard,
+                  fwstats->txs_supp_core,
+                  fwstats->txs_supp_ps,
+                  fwstats->txs_tossed,
+                  fwstats->txs_host_tossed,
+                  fwstats->bus_flow_block,
+                  fwstats->fws_flow_block,
+                  fwstats->send_pkts[0], fwstats->send_pkts[1],
+                  fwstats->send_pkts[2], fwstats->send_pkts[3],
+                  fwstats->send_pkts[4],
+                  fwstats->requested_sent[0],
+                  fwstats->requested_sent[1],
+                  fwstats->requested_sent[2],
+                  fwstats->requested_sent[3],
+                  fwstats->requested_sent[4]);
+
+       return 0;
+}
+#else
+static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data)
+{
+       return 0;
+}
+#endif
+
 int brcmf_fws_init(struct brcmf_pub *drvr)
 {
        struct brcmf_fws_info *fws;
@@ -2107,7 +2204,8 @@ int brcmf_fws_init(struct brcmf_pub *drvr)
                        BRCMF_FWS_PSQ_LEN);
 
        /* create debugfs file for statistics */
-       brcmf_debugfs_create_fws_stats(drvr, &fws->stats);
+       brcmf_debugfs_add_entry(drvr, "fws_stats",
+                               brcmf_debugfs_fws_stats_read);
 
        brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
                  fws->fw_signals ? "enabled" : "disabled", tlv);
index f3445ac627e48d84ef0391f59b5904beb9bf80f9..588fdbdb325539bf8e774464594e9646041389bd 100644 (file)
@@ -708,7 +708,7 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
                active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
        else if (num_chans == AF_PEER_SEARCH_CNT)
                active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
-       else if (wl_get_vif_state_all(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
+       else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
                active = -1;
        else
                active = P2PAPI_SCAN_DWELL_TIME_MS;
index 3deab7959a0d9a5c3b22ad757caf4615b283b596..6c5e585ccda99fcf83cf8d8ae5e7a7b22130e542 100644 (file)
@@ -18,6 +18,8 @@
 #define        _BRCM_SDH_H_
 
 #include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include "firmware.h"
 
 #define SDIO_FUNC_0            0
 #define SDIO_FUNC_1            1
@@ -182,6 +184,8 @@ struct brcmf_sdio_dev {
        uint max_segment_size;
        uint txglomsz;
        struct sg_table sgtable;
+       char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
+       char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
 };
 
 /* sdio core registers */
index d06fcb05adf2517a292ab727467e69d0abf28259..dc135915470d2cb70efab5480d11a036a0e00d3f 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/vmalloc.h>
 
 #include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
 #include <brcmu_wifi.h>
 #include <dhd_bus.h>
 #include <dhd_dbg.h>
 #include "usb_rdl.h"
 #include "usb.h"
 
-#define IOCTL_RESP_TIMEOUT  2000
+#define IOCTL_RESP_TIMEOUT             2000
 
 #define BRCMF_USB_RESET_GETVER_SPINWAIT        100     /* in unit of ms */
 #define BRCMF_USB_RESET_GETVER_LOOP_CNT        10
 
 #define BRCMF_POSTBOOT_ID              0xA123  /* ID to detect if dongle
                                                   has boot up */
-#define BRCMF_USB_NRXQ 50
-#define BRCMF_USB_NTXQ 50
+#define BRCMF_USB_NRXQ                 50
+#define BRCMF_USB_NTXQ                 50
 
-#define CONFIGDESC(usb)         (&((usb)->actconfig)->desc)
-#define IFPTR(usb, idx)         ((usb)->actconfig->interface[(idx)])
-#define IFALTS(usb, idx)        (IFPTR((usb), (idx))->altsetting[0])
-#define IFDESC(usb, idx)        IFALTS((usb), (idx)).desc
-#define IFEPDESC(usb, idx, ep)  (IFALTS((usb), (idx)).endpoint[(ep)]).desc
+#define BRCMF_USB_CBCTL_WRITE          0
+#define BRCMF_USB_CBCTL_READ           1
+#define BRCMF_USB_MAX_PKT_SIZE         1600
 
-#define CONTROL_IF              0
-#define BULK_IF                 0
-
-#define BRCMF_USB_CBCTL_WRITE  0
-#define BRCMF_USB_CBCTL_READ   1
-#define BRCMF_USB_MAX_PKT_SIZE 1600
-
-#define BRCMF_USB_43143_FW_NAME        "brcm/brcmfmac43143.bin"
-#define BRCMF_USB_43236_FW_NAME        "brcm/brcmfmac43236b.bin"
-#define BRCMF_USB_43242_FW_NAME        "brcm/brcmfmac43242a.bin"
+#define BRCMF_USB_43143_FW_NAME                "brcm/brcmfmac43143.bin"
+#define BRCMF_USB_43236_FW_NAME                "brcm/brcmfmac43236b.bin"
+#define BRCMF_USB_43242_FW_NAME                "brcm/brcmfmac43242a.bin"
+#define BRCMF_USB_43569_FW_NAME                "brcm/brcmfmac43569.bin"
 
 struct brcmf_usb_image {
        struct list_head list;
@@ -70,7 +63,7 @@ struct brcmf_usbdev_info {
        struct list_head rx_postq;
        struct list_head tx_freeq;
        struct list_head tx_postq;
-       uint rx_pipe, tx_pipe, rx_pipe2;
+       uint rx_pipe, tx_pipe;
 
        int rx_low_watermark;
        int tx_low_watermark;
@@ -97,6 +90,7 @@ struct brcmf_usbdev_info {
        int ctl_completed;
        wait_queue_head_t ioctl_resp_wait;
        ulong ctl_op;
+       u8 ifnum;
 
        struct urb *bulk_urb; /* used for FW download */
 };
@@ -576,7 +570,6 @@ fail:
 static int brcmf_usb_up(struct device *dev)
 {
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
-       u16 ifnum;
 
        brcmf_dbg(USB, "Enter\n");
        if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
@@ -589,21 +582,19 @@ static int brcmf_usb_up(struct device *dev)
                devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
                devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
 
-               ifnum = IFDESC(devinfo->usbdev, CONTROL_IF).bInterfaceNumber;
-
                /* CTL Write */
                devinfo->ctl_write.bRequestType =
                        USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
                devinfo->ctl_write.bRequest = 0;
                devinfo->ctl_write.wValue = cpu_to_le16(0);
-               devinfo->ctl_write.wIndex = cpu_to_le16p(&ifnum);
+               devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum);
 
                /* CTL Read */
                devinfo->ctl_read.bRequestType =
                        USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
                devinfo->ctl_read.bRequest = 1;
                devinfo->ctl_read.wValue = cpu_to_le16(0);
-               devinfo->ctl_read.wIndex = cpu_to_le16p(&ifnum);
+               devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum);
        }
        brcmf_usb_rx_fill_all(devinfo);
        return 0;
@@ -642,19 +633,19 @@ brcmf_usb_sync_complete(struct urb *urb)
        brcmf_usb_ioctl_resp_wake(devinfo);
 }
 
-static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
-                            void *buffer, int buflen)
+static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
+                           void *buffer, int buflen)
 {
-       int ret = 0;
+       int ret;
        char *tmpbuf;
        u16 size;
 
        if ((!devinfo) || (devinfo->ctl_urb == NULL))
-               return false;
+               return -EINVAL;
 
        tmpbuf = kmalloc(buflen, GFP_ATOMIC);
        if (!tmpbuf)
-               return false;
+               return -ENOMEM;
 
        size = buflen;
        devinfo->ctl_urb->transfer_buffer_length = size;
@@ -675,14 +666,16 @@ static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
        ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
        if (ret < 0) {
                brcmf_err("usb_submit_urb failed %d\n", ret);
-               kfree(tmpbuf);
-               return false;
+               goto finalize;
        }
 
-       ret = brcmf_usb_ioctl_resp_wait(devinfo);
-       memcpy(buffer, tmpbuf, buflen);
-       kfree(tmpbuf);
+       if (!brcmf_usb_ioctl_resp_wait(devinfo))
+               ret = -ETIMEDOUT;
+       else
+               memcpy(buffer, tmpbuf, buflen);
 
+finalize:
+       kfree(tmpbuf);
        return ret;
 }
 
@@ -724,6 +717,7 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
 {
        struct bootrom_id_le id;
        u32 loop_cnt;
+       int err;
 
        brcmf_dbg(USB, "Enter\n");
 
@@ -732,7 +726,9 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
                mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT);
                loop_cnt++;
                id.chip = cpu_to_le32(0xDEAD);       /* Get the ID */
-               brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
+               err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
+               if ((err) && (err != -ETIMEDOUT))
+                       return err;
                if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID))
                        break;
        } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT);
@@ -794,8 +790,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
        }
 
        /* 1) Prepare USB boot loader for runtime image */
-       brcmf_usb_dl_cmd(devinfo, DL_START, &state,
-                        sizeof(struct rdl_state_le));
+       brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
 
        rdlstate = le32_to_cpu(state.state);
        rdlbytes = le32_to_cpu(state.bytes);
@@ -839,10 +834,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
                        dlpos += sendlen;
                        sent += sendlen;
                }
-               if (!brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
-                                     sizeof(struct rdl_state_le))) {
-                       brcmf_err("DL_GETSTATE Failed xxxx\n");
-                       err = -EINVAL;
+               err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
+                                      sizeof(state));
+               if (err) {
+                       brcmf_err("DL_GETSTATE Failed\n");
                        goto fail;
                }
 
@@ -898,13 +893,12 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
                return -EINVAL;
 
        /* Check we are runnable */
-       brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
-               sizeof(struct rdl_state_le));
+       state.state = 0;
+       brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state));
 
        /* Start the image */
        if (state.state == cpu_to_le32(DL_RUNNABLE)) {
-               if (!brcmf_usb_dl_cmd(devinfo, DL_GO, &state,
-                       sizeof(struct rdl_state_le)))
+               if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state)))
                        return -ENODEV;
                if (brcmf_usb_resetcfg(devinfo))
                        return -ENODEV;
@@ -920,13 +914,16 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
 static bool brcmf_usb_chip_support(int chipid, int chiprev)
 {
        switch(chipid) {
-       case 43143:
+       case BRCM_CC_43143_CHIP_ID:
                return true;
-       case 43235:
-       case 43236:
-       case 43238:
+       case BRCM_CC_43235_CHIP_ID:
+       case BRCM_CC_43236_CHIP_ID:
+       case BRCM_CC_43238_CHIP_ID:
                return (chiprev == 3);
-       case 43242:
+       case BRCM_CC_43242_CHIP_ID:
+               return true;
+       case BRCM_CC_43566_CHIP_ID:
+       case BRCM_CC_43569_CHIP_ID:
                return true;
        default:
                break;
@@ -1020,14 +1017,17 @@ static int check_file(const u8 *headers)
 static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
 {
        switch (devinfo->bus_pub.devid) {
-       case 43143:
+       case BRCM_CC_43143_CHIP_ID:
                return BRCMF_USB_43143_FW_NAME;
-       case 43235:
-       case 43236:
-       case 43238:
+       case BRCM_CC_43235_CHIP_ID:
+       case BRCM_CC_43236_CHIP_ID:
+       case BRCM_CC_43238_CHIP_ID:
                return BRCMF_USB_43236_FW_NAME;
-       case 43242:
+       case BRCM_CC_43242_CHIP_ID:
                return BRCMF_USB_43242_FW_NAME;
+       case BRCM_CC_43566_CHIP_ID:
+       case BRCM_CC_43569_CHIP_ID:
+               return BRCMF_USB_43569_FW_NAME;
        default:
                return NULL;
        }
@@ -1222,15 +1222,15 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
 static int
 brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
-       int ep;
-       struct usb_endpoint_descriptor *endpoint;
-       int ret = 0;
        struct usb_device *usb = interface_to_usbdev(intf);
-       int num_of_eps;
-       u8 endpoint_num;
        struct brcmf_usbdev_info *devinfo;
+       struct usb_interface_descriptor *desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int ret = 0;
+       u32 num_of_eps;
+       u8 endpoint_num, ep;
 
-       brcmf_dbg(USB, "Enter\n");
+       brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct);
 
        devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC);
        if (devinfo == NULL)
@@ -1238,92 +1238,71 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 
        devinfo->usbdev = usb;
        devinfo->dev = &usb->dev;
-
        usb_set_intfdata(intf, devinfo);
 
        /* Check that the device supports only one configuration */
        if (usb->descriptor.bNumConfigurations != 1) {
-               ret = -1;
-               goto fail;
-       }
-
-       if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
-               ret = -1;
+               brcmf_err("Number of configurations: %d not supported\n",
+                         usb->descriptor.bNumConfigurations);
+               ret = -ENODEV;
                goto fail;
        }
 
-       /*
-        * Only the BDC interface configuration is supported:
-        *      Device class: USB_CLASS_VENDOR_SPEC
-        *      if0 class: USB_CLASS_VENDOR_SPEC
-        *      if0/ep0: control
-        *      if0/ep1: bulk in
-        *      if0/ep2: bulk out (ok if swapped with bulk in)
-        */
-       if (CONFIGDESC(usb)->bNumInterfaces != 1) {
-               ret = -1;
+       if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) &&
+           (usb->descriptor.bDeviceClass != USB_CLASS_MISC) &&
+           (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) {
+               brcmf_err("Device class: 0x%x not supported\n",
+                         usb->descriptor.bDeviceClass);
+               ret = -ENODEV;
                goto fail;
        }
 
-       /* Check interface */
-       if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC ||
-           IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 ||
-           IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) {
-               brcmf_err("invalid control interface: class %d, subclass %d, proto %d\n",
-                         IFDESC(usb, CONTROL_IF).bInterfaceClass,
-                         IFDESC(usb, CONTROL_IF).bInterfaceSubClass,
-                         IFDESC(usb, CONTROL_IF).bInterfaceProtocol);
-               ret = -1;
+       desc = &intf->altsetting[0].desc;
+       if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+           (desc->bInterfaceSubClass != 2) ||
+           (desc->bInterfaceProtocol != 0xff)) {
+               brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
+                         desc->bInterfaceNumber, desc->bInterfaceClass,
+                         desc->bInterfaceSubClass, desc->bInterfaceProtocol);
+               ret = -ENODEV;
                goto fail;
        }
 
-       /* Check control endpoint */
-       endpoint = &IFEPDESC(usb, CONTROL_IF, 0);
-       if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
-               != USB_ENDPOINT_XFER_INT) {
-               brcmf_err("invalid control endpoint %d\n",
-                         endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
-               ret = -1;
-               goto fail;
-       }
-
-       devinfo->rx_pipe = 0;
-       devinfo->rx_pipe2 = 0;
-       devinfo->tx_pipe = 0;
-       num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1;
-
-       /* Check data endpoints and get pipes */
-       for (ep = 1; ep <= num_of_eps; ep++) {
-               endpoint = &IFEPDESC(usb, BULK_IF, ep);
-               if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
-                   USB_ENDPOINT_XFER_BULK) {
-                       brcmf_err("invalid data endpoint %d\n", ep);
-                       ret = -1;
-                       goto fail;
-               }
-
-               endpoint_num = endpoint->bEndpointAddress &
-                              USB_ENDPOINT_NUMBER_MASK;
-               if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
-                       == USB_DIR_IN) {
-                       if (!devinfo->rx_pipe) {
+       num_of_eps = desc->bNumEndpoints;
+       for (ep = 0; ep < num_of_eps; ep++) {
+               endpoint = &intf->altsetting[0].endpoint[ep].desc;
+               endpoint_num = usb_endpoint_num(endpoint);
+               if (!usb_endpoint_xfer_bulk(endpoint))
+                       continue;
+               if (usb_endpoint_dir_in(endpoint)) {
+                       if (!devinfo->rx_pipe)
                                devinfo->rx_pipe =
                                        usb_rcvbulkpipe(usb, endpoint_num);
-                       } else {
-                               devinfo->rx_pipe2 =
-                                       usb_rcvbulkpipe(usb, endpoint_num);
-                       }
                } else {
-                       devinfo->tx_pipe = usb_sndbulkpipe(usb, endpoint_num);
+                       if (!devinfo->tx_pipe)
+                               devinfo->tx_pipe =
+                                       usb_sndbulkpipe(usb, endpoint_num);
                }
        }
+       if (devinfo->rx_pipe == 0) {
+               brcmf_err("No RX (in) Bulk EP found\n");
+               ret = -ENODEV;
+               goto fail;
+       }
+       if (devinfo->tx_pipe == 0) {
+               brcmf_err("No TX (out) Bulk EP found\n");
+               ret = -ENODEV;
+               goto fail;
+       }
+
+       devinfo->ifnum = desc->bInterfaceNumber;
 
        if (usb->speed == USB_SPEED_SUPER)
-               brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n");
+               brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n");
        else if (usb->speed == USB_SPEED_HIGH)
-               brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n");
+               brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n");
        else
-               brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n");
+               brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n");
 
        ret = brcmf_usb_probe_cb(devinfo);
        if (ret)
@@ -1333,11 +1312,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
        return 0;
 
 fail:
-       brcmf_err("failed with errno %d\n", ret);
        kfree(devinfo);
        usb_set_intfdata(intf, NULL);
        return ret;
-
 }
 
 static void
@@ -1382,6 +1359,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
 {
        struct usb_device *usb = interface_to_usbdev(intf);
        struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+
        brcmf_dbg(USB, "Enter\n");
 
        return brcmf_fw_get_firmwares(&usb->dev, 0,
@@ -1389,25 +1367,24 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
                                      brcmf_usb_probe_phase2);
 }
 
-#define BRCMF_USB_VENDOR_ID_BROADCOM   0x0a5c
-#define BRCMF_USB_DEVICE_ID_43143      0xbd1e
-#define BRCMF_USB_DEVICE_ID_43236      0xbd17
-#define BRCMF_USB_DEVICE_ID_43242      0xbd1f
-#define BRCMF_USB_DEVICE_ID_BCMFW      0x0bdc
+#define BRCMF_USB_DEVICE(dev_id)       \
+       { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) }
 
 static struct usb_device_id brcmf_usb_devid_table[] = {
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) },
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) },
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) },
+       BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID),
+       BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID),
+       BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID),
+       BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID),
        /* special entry for device with firmware loaded and running */
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) },
-       { }
+       BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID),
+       { /* end: all zeroes */ }
 };
 
 MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
 MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
 MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
 MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME);
+MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME);
 
 static struct usb_driver brcmf_usbdrvr = {
        .name = KBUILD_MODNAME,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
new file mode 100644 (file)
index 0000000..5960d82
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/vmalloc.h>
+#include <net/cfg80211.h>
+#include <net/netlink.h>
+
+#include <brcmu_wifi.h>
+#include "fwil_types.h"
+#include "dhd.h"
+#include "p2p.h"
+#include "dhd_dbg.h"
+#include "wl_cfg80211.h"
+#include "vendor.h"
+#include "fwil.h"
+
+static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
+                                                struct wireless_dev *wdev,
+                                                const void *data, int len)
+{
+       struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+       struct net_device *ndev = cfg_to_ndev(cfg);
+       const struct brcmf_vndr_dcmd_hdr *cmdhdr = data;
+       struct sk_buff *reply;
+       int ret, payload, ret_len;
+       void *dcmd_buf = NULL, *wr_pointer;
+       u16 msglen, maxmsglen = PAGE_SIZE - 0x100;
+
+       brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set,
+                 cmdhdr->len);
+
+       len -= sizeof(struct brcmf_vndr_dcmd_hdr);
+       ret_len = cmdhdr->len;
+       if (ret_len > 0 || len > 0) {
+               if (len > BRCMF_DCMD_MAXLEN) {
+                       brcmf_err("oversize input buffer %d\n", len);
+                       len = BRCMF_DCMD_MAXLEN;
+               }
+               if (ret_len > BRCMF_DCMD_MAXLEN) {
+                       brcmf_err("oversize return buffer %d\n", ret_len);
+                       ret_len = BRCMF_DCMD_MAXLEN;
+               }
+               payload = max(ret_len, len) + 1;
+               dcmd_buf = vzalloc(payload);
+               if (NULL == dcmd_buf)
+                       return -ENOMEM;
+
+               memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len);
+               *(char *)(dcmd_buf + len)  = '\0';
+       }
+
+       if (cmdhdr->set)
+               ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd,
+                                            dcmd_buf, ret_len);
+       else
+               ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd,
+                                            dcmd_buf, ret_len);
+       if (ret != 0)
+               goto exit;
+
+       wr_pointer = dcmd_buf;
+       while (ret_len > 0) {
+               msglen = ret_len > maxmsglen ? maxmsglen : ret_len;
+               ret_len -= msglen;
+               payload = msglen + sizeof(msglen);
+               reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+               if (NULL == reply) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) ||
+                   nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) {
+                       kfree_skb(reply);
+                       ret = -ENOBUFS;
+                       break;
+               }
+
+               ret = cfg80211_vendor_cmd_reply(reply);
+               if (ret)
+                       break;
+
+               wr_pointer += msglen;
+       }
+
+exit:
+       vfree(dcmd_buf);
+
+       return ret;
+}
+
+const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
+       {
+               {
+                       .vendor_id = BROADCOM_OUI,
+                       .subcmd = BRCMF_VNDR_CMDS_DCMD
+               },
+               .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+                        WIPHY_VENDOR_CMD_NEED_NETDEV,
+               .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler
+       },
+};
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/brcm80211/brcmfmac/vendor.h
new file mode 100644 (file)
index 0000000..061b7bf
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _vendor_h_
+#define _vendor_h_
+
+#define BROADCOM_OUI   0x001018
+
+enum brcmf_vndr_cmds {
+       BRCMF_VNDR_CMDS_UNSPEC,
+       BRCMF_VNDR_CMDS_DCMD,
+       BRCMF_VNDR_CMDS_LAST
+};
+
+/**
+ * enum brcmf_nlattrs - nl80211 message attributes
+ *
+ * @BRCMF_NLATTR_LEN: message body length
+ * @BRCMF_NLATTR_DATA: message body
+ */
+enum brcmf_nlattrs {
+       BRCMF_NLATTR_UNSPEC,
+
+       BRCMF_NLATTR_LEN,
+       BRCMF_NLATTR_DATA,
+
+       __BRCMF_NLATTR_AFTER_LAST,
+       BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1
+};
+
+/**
+ * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd
+ *                             support
+ *
+ * @cmd: common dongle cmd definition
+ * @len: length of expecting return buffer
+ * @offset: offset of data buffer
+ * @set: get or set request(optional)
+ * @magic: magic number for verification
+ */
+struct brcmf_vndr_dcmd_hdr {
+       uint cmd;
+       int len;
+       uint offset;
+       uint set;
+       uint magic;
+};
+
+extern const struct wiphy_vendor_command brcmf_vendor_cmds[];
+
+#endif /* _vendor_h_ */
index d8fa276e368b4fc038b62a941191b3d25393b8f6..48078a321716787069c7491880c0e783316acb3a 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/kernel.h>
 #include <linux/etherdevice.h>
 #include <linux/module.h>
+#include <linux/vmalloc.h>
 #include <net/cfg80211.h>
 #include <net/netlink.h>
 
@@ -32,7 +33,9 @@
 #include "p2p.h"
 #include "btcoex.h"
 #include "wl_cfg80211.h"
+#include "feature.h"
 #include "fwil.h"
+#include "vendor.h"
 
 #define BRCMF_SCAN_IE_LEN_MAX          2048
 #define BRCMF_PNO_VERSION              2
@@ -100,24 +103,6 @@ static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
        return true;
 }
 
-#define CHAN2G(_channel, _freq, _flags) {                      \
-       .band                   = IEEE80211_BAND_2GHZ,          \
-       .center_freq            = (_freq),                      \
-       .hw_value               = (_channel),                   \
-       .flags                  = (_flags),                     \
-       .max_antenna_gain       = 0,                            \
-       .max_power              = 30,                           \
-}
-
-#define CHAN5G(_channel, _flags) {                             \
-       .band                   = IEEE80211_BAND_5GHZ,          \
-       .center_freq            = 5000 + (5 * (_channel)),      \
-       .hw_value               = (_channel),                   \
-       .flags                  = (_flags),                     \
-       .max_antenna_gain       = 0,                            \
-       .max_power              = 30,                           \
-}
-
 #define RATE_TO_BASE100KBPS(rate)   (((rate) * 10) / 2)
 #define RATETAB_ENT(_rateid, _flags) \
        {                                                               \
@@ -146,58 +131,17 @@ static struct ieee80211_rate __wl_rates[] = {
 #define wl_g_rates             (__wl_rates + 0)
 #define wl_g_rates_size        12
 
-static struct ieee80211_channel __wl_2ghz_channels[] = {
-       CHAN2G(1, 2412, 0),
-       CHAN2G(2, 2417, 0),
-       CHAN2G(3, 2422, 0),
-       CHAN2G(4, 2427, 0),
-       CHAN2G(5, 2432, 0),
-       CHAN2G(6, 2437, 0),
-       CHAN2G(7, 2442, 0),
-       CHAN2G(8, 2447, 0),
-       CHAN2G(9, 2452, 0),
-       CHAN2G(10, 2457, 0),
-       CHAN2G(11, 2462, 0),
-       CHAN2G(12, 2467, 0),
-       CHAN2G(13, 2472, 0),
-       CHAN2G(14, 2484, 0),
-};
-
-static struct ieee80211_channel __wl_5ghz_a_channels[] = {
-       CHAN5G(34, 0), CHAN5G(36, 0),
-       CHAN5G(38, 0), CHAN5G(40, 0),
-       CHAN5G(42, 0), CHAN5G(44, 0),
-       CHAN5G(46, 0), CHAN5G(48, 0),
-       CHAN5G(52, 0), CHAN5G(56, 0),
-       CHAN5G(60, 0), CHAN5G(64, 0),
-       CHAN5G(100, 0), CHAN5G(104, 0),
-       CHAN5G(108, 0), CHAN5G(112, 0),
-       CHAN5G(116, 0), CHAN5G(120, 0),
-       CHAN5G(124, 0), CHAN5G(128, 0),
-       CHAN5G(132, 0), CHAN5G(136, 0),
-       CHAN5G(140, 0), CHAN5G(149, 0),
-       CHAN5G(153, 0), CHAN5G(157, 0),
-       CHAN5G(161, 0), CHAN5G(165, 0),
-       CHAN5G(184, 0), CHAN5G(188, 0),
-       CHAN5G(192, 0), CHAN5G(196, 0),
-       CHAN5G(200, 0), CHAN5G(204, 0),
-       CHAN5G(208, 0), CHAN5G(212, 0),
-       CHAN5G(216, 0),
-};
-
-static struct ieee80211_supported_band __wl_band_2ghz = {
+/* Band templates duplicated per wiphy. The channel info
+ * is filled in after querying the device.
+ */
+static const struct ieee80211_supported_band __wl_band_2ghz = {
        .band = IEEE80211_BAND_2GHZ,
-       .channels = __wl_2ghz_channels,
-       .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
        .bitrates = wl_g_rates,
        .n_bitrates = wl_g_rates_size,
-       .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true},
 };
 
-static struct ieee80211_supported_band __wl_band_5ghz_a = {
+static const struct ieee80211_supported_band __wl_band_5ghz_a = {
        .band = IEEE80211_BAND_5GHZ,
-       .channels = __wl_5ghz_a_channels,
-       .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
        .bitrates = wl_a_rates,
        .n_bitrates = wl_a_rates_size,
 };
@@ -588,6 +532,12 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
        }
 }
 
+static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
+{
+       if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
+               brcmf_set_mpc(ifp, mpc);
+}
+
 void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
 {
        s32 err = 0;
@@ -641,7 +591,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
                        brcmf_err("Scan abort  failed\n");
        }
 
-       brcmf_set_mpc(ifp, 1);
+       brcmf_scan_config_mpc(ifp, 1);
 
        /*
         * e-scan can be initiated by scheduled scan
@@ -920,7 +870,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
                brcmf_err("error (%d)\n", err);
                return err;
        }
-       brcmf_set_mpc(ifp, 0);
+       brcmf_scan_config_mpc(ifp, 0);
        results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
        results->version = 0;
        results->count = 0;
@@ -928,7 +878,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
 
        err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
        if (err)
-               brcmf_set_mpc(ifp, 1);
+               brcmf_scan_config_mpc(ifp, 1);
        return err;
 }
 
@@ -1019,7 +969,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
                        brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
                        goto scan_out;
                }
-               brcmf_set_mpc(ifp, 0);
+               brcmf_scan_config_mpc(ifp, 0);
                err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
                                             &sr->ssid_le, sizeof(sr->ssid_le));
                if (err) {
@@ -1029,7 +979,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
                        else
                                brcmf_err("WLC_SCAN error (%d)\n", err);
 
-                       brcmf_set_mpc(ifp, 1);
+                       brcmf_scan_config_mpc(ifp, 1);
                        goto scan_out;
                }
        }
@@ -1331,7 +1281,6 @@ static s32
 brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
 {
        struct brcmf_if *ifp = netdev_priv(ndev);
-       s32 err = 0;
 
        brcmf_dbg(TRACE, "Enter\n");
        if (!check_vif_up(ifp->vif))
@@ -1341,7 +1290,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
 
        brcmf_dbg(TRACE, "Exit\n");
 
-       return err;
+       return 0;
 }
 
 static s32 brcmf_set_wpa_version(struct net_device *ndev,
@@ -1612,17 +1561,10 @@ static
 enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
                                           enum nl80211_auth_type type)
 {
-       u32 ci;
-       if (type == NL80211_AUTHTYPE_AUTOMATIC) {
-               /* shift to ignore chip revision */
-               ci = brcmf_get_chip_info(ifp) >> 4;
-               switch (ci) {
-               case 43236:
-                       brcmf_dbg(CONN, "43236 WAR: use OPEN instead of AUTO\n");
-                       return NL80211_AUTHTYPE_OPEN_SYSTEM;
-               default:
-                       break;
-               }
+       if (type == NL80211_AUTHTYPE_AUTOMATIC &&
+           brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
+               brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
+               type = NL80211_AUTHTYPE_OPEN_SYSTEM;
        }
        return type;
 }
@@ -2388,7 +2330,6 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
        struct cfg80211_bss *bss;
        struct ieee80211_supported_band *band;
        struct brcmu_chan ch;
-       s32 err = 0;
        u16 channel;
        u32 freq;
        u16 notify_capability;
@@ -2438,7 +2379,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
 
        cfg80211_put_bss(wiphy, bss);
 
-       return err;
+       return 0;
 }
 
 static struct brcmf_bss_info_le *
@@ -2690,7 +2631,6 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
 {
        struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
        s32 status;
-       s32 err = 0;
        struct brcmf_escan_result_le *escan_result_le;
        struct brcmf_bss_info_le *bss_info_le;
        struct brcmf_bss_info_le *bss = NULL;
@@ -2781,7 +2721,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
                                  status);
        }
 exit:
-       return err;
+       return 0;
 }
 
 static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
@@ -3260,35 +3200,6 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
        return 0;
 }
 
-#ifdef CONFIG_NL80211_TESTMODE
-static int brcmf_cfg80211_testmode(struct wiphy *wiphy,
-                                  struct wireless_dev *wdev,
-                                  void *data, int len)
-{
-       struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
-       struct net_device *ndev = cfg_to_ndev(cfg);
-       struct brcmf_dcmd *dcmd = data;
-       struct sk_buff *reply;
-       int ret;
-
-       brcmf_dbg(TRACE, "cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
-                 dcmd->buf, dcmd->len);
-
-       if (dcmd->set)
-               ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
-                                            dcmd->buf, dcmd->len);
-       else
-               ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
-                                            dcmd->buf, dcmd->len);
-       if (ret == 0) {
-               reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
-               nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
-               ret = cfg80211_testmode_reply(reply);
-       }
-       return ret;
-}
-#endif
-
 static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
 {
        s32 err;
@@ -3507,7 +3418,6 @@ static s32
 brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
                     struct parsed_vndr_ies *vndr_ies)
 {
-       s32 err = 0;
        struct brcmf_vs_tlv *vndrie;
        struct brcmf_tlv *ie;
        struct parsed_vndr_ie_info *parsed_info;
@@ -3560,7 +3470,7 @@ next:
                        ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
                                TLV_HDR_LEN);
        }
-       return err;
+       return 0;
 }
 
 static u32
@@ -4307,120 +4217,8 @@ static struct cfg80211_ops wl_cfg80211_ops = {
        .crit_proto_start = brcmf_cfg80211_crit_proto_start,
        .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
        .tdls_oper = brcmf_cfg80211_tdls_oper,
-       CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode)
-};
-
-static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
-{
-       /* scheduled scan settings */
-       wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
-       wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
-       wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
-       wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
-}
-
-static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
-       {
-               .max = 2,
-               .types = BIT(NL80211_IFTYPE_STATION) |
-                        BIT(NL80211_IFTYPE_ADHOC) |
-                        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 brcmf_iface_combos[] = {
-       {
-                .max_interfaces = BRCMF_IFACE_MAX_CNT,
-                .num_different_channels = 2,
-                .n_limits = ARRAY_SIZE(brcmf_iface_limits),
-                .limits = brcmf_iface_limits
-       }
-};
-
-static const struct ieee80211_txrx_stypes
-brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
-       [NL80211_IFTYPE_STATION] = {
-               .tx = 0xffff,
-               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
-                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
-       },
-       [NL80211_IFTYPE_P2P_CLIENT] = {
-               .tx = 0xffff,
-               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
-                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
-       },
-       [NL80211_IFTYPE_P2P_GO] = {
-               .tx = 0xffff,
-               .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
-                     BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
-                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
-                     BIT(IEEE80211_STYPE_DISASSOC >> 4) |
-                     BIT(IEEE80211_STYPE_AUTH >> 4) |
-                     BIT(IEEE80211_STYPE_DEAUTH >> 4) |
-                     BIT(IEEE80211_STYPE_ACTION >> 4)
-       },
-       [NL80211_IFTYPE_P2P_DEVICE] = {
-               .tx = 0xffff,
-               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
-                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
-       }
 };
 
-static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
-{
-       struct wiphy *wiphy;
-       s32 err = 0;
-
-       wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
-       if (!wiphy) {
-               brcmf_err("Could not allocate wiphy device\n");
-               return ERR_PTR(-ENOMEM);
-       }
-       set_wiphy_dev(wiphy, phydev);
-       wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
-       wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
-       wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
-       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-                                BIT(NL80211_IFTYPE_ADHOC) |
-                                BIT(NL80211_IFTYPE_AP) |
-                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                                BIT(NL80211_IFTYPE_P2P_GO) |
-                                BIT(NL80211_IFTYPE_P2P_DEVICE);
-       wiphy->iface_combinations = brcmf_iface_combos;
-       wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
-       wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
-       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
-       wiphy->cipher_suites = __wl_cipher_suites;
-       wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
-       wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
-                       WIPHY_FLAG_OFFCHAN_TX |
-                       WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
-                       WIPHY_FLAG_SUPPORTS_TDLS;
-       if (!brcmf_roamoff)
-               wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
-       wiphy->mgmt_stypes = brcmf_txrx_stypes;
-       wiphy->max_remain_on_channel_duration = 5000;
-       brcmf_wiphy_pno_params(wiphy);
-       brcmf_dbg(INFO, "Registering custom regulatory\n");
-       wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
-       wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
-       err = wiphy_register(wiphy);
-       if (err < 0) {
-               brcmf_err("Could not register wiphy device (%d)\n", err);
-               wiphy_free(wiphy);
-               return ERR_PTR(err);
-       }
-       return wiphy;
-}
-
 struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
                                           enum nl80211_iftype type,
                                           bool pm_block)
@@ -4650,7 +4448,6 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
        struct brcmf_if *ifp = netdev_priv(ndev);
        struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
        struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
-       s32 err = 0;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -4676,7 +4473,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
                          completed ? "succeeded" : "failed");
        }
        brcmf_dbg(TRACE, "Exit\n");
-       return err;
+       return 0;
 }
 
 static s32
@@ -4768,7 +4565,6 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp,
                            const struct brcmf_event_msg *e, void *data)
 {
        struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
-       s32 err = 0;
        u32 event = e->event_code;
        u32 status = e->status;
 
@@ -4779,7 +4575,7 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp,
                        brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
        }
 
-       return err;
+       return 0;
 }
 
 static s32
@@ -4966,182 +4762,53 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
        mutex_init(&event->vif_event_lock);
 }
 
-static int brcmf_enable_bw40_2g(struct brcmf_if *ifp)
-{
-       struct brcmf_fil_bwcap_le band_bwcap;
-       u32 val;
-       int err;
-
-       /* verify support for bw_cap command */
-       val = WLC_BAND_5G;
-       err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
-
-       if (!err) {
-               /* only set 2G bandwidth using bw_cap command */
-               band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
-               band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
-               err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
-                                              sizeof(band_bwcap));
-       } else {
-               brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
-               val = WLC_N_BW_40ALL;
-               err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
-       }
-       return err;
-}
-
-struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
-                                                 struct device *busdev)
+static s32
+brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
 {
-       struct net_device *ndev = drvr->iflist[0]->ndev;
-       struct brcmf_cfg80211_info *cfg;
-       struct wiphy *wiphy;
-       struct brcmf_cfg80211_vif *vif;
-       struct brcmf_if *ifp;
        s32 err = 0;
-       s32 io_type;
-
-       if (!ndev) {
-               brcmf_err("ndev is invalid\n");
-               return NULL;
-       }
-
-       ifp = netdev_priv(ndev);
-       wiphy = brcmf_setup_wiphy(busdev);
-       if (IS_ERR(wiphy))
-               return NULL;
-
-       cfg = wiphy_priv(wiphy);
-       cfg->wiphy = wiphy;
-       cfg->pub = drvr;
-       init_vif_event(&cfg->vif_event);
-       INIT_LIST_HEAD(&cfg->vif_list);
+       __le32 roamtrigger[2];
+       __le32 roam_delta[2];
 
-       vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
-       if (IS_ERR(vif)) {
-               wiphy_free(wiphy);
-               return NULL;
+       /*
+        * Setup timeout if Beacons are lost and roam is
+        * off to report link down
+        */
+       if (brcmf_roamoff) {
+               err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
+               if (err) {
+                       brcmf_err("bcn_timeout error (%d)\n", err);
+                       goto dongle_rom_out;
+               }
        }
 
-       vif->ifp = ifp;
-       vif->wdev.netdev = ndev;
-       ndev->ieee80211_ptr = &vif->wdev;
-       SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
-
-       err = wl_init_priv(cfg);
+       /*
+        * Enable/Disable built-in roaming to allow supplicant
+        * to take care of roaming
+        */
+       brcmf_dbg(INFO, "Internal Roaming = %s\n",
+                 brcmf_roamoff ? "Off" : "On");
+       err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
        if (err) {
-               brcmf_err("Failed to init iwm_priv (%d)\n", err);
-               goto cfg80211_attach_out;
+               brcmf_err("roam_off error (%d)\n", err);
+               goto dongle_rom_out;
        }
-       ifp->vif = vif;
 
-       err = brcmf_p2p_attach(cfg);
-       if (err) {
-               brcmf_err("P2P initilisation failed (%d)\n", err);
-               goto cfg80211_p2p_attach_out;
-       }
-       err = brcmf_btcoex_attach(cfg);
+       roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
+       roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
+       err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
+                                    (void *)roamtrigger, sizeof(roamtrigger));
        if (err) {
-               brcmf_err("BT-coex initialisation failed (%d)\n", err);
-               brcmf_p2p_detach(&cfg->p2p);
-               goto cfg80211_p2p_attach_out;
+               brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
+               goto dongle_rom_out;
        }
 
-       /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
-        * setup 40MHz in 2GHz band and enable OBSS scanning.
-        */
-       if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap &
-           IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
-               err = brcmf_enable_bw40_2g(ifp);
-               if (!err)
-                       err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
-                                                     BRCMF_OBSS_COEX_AUTO);
-       }
-
-       err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
-       if (err) {
-               brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
-               wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
-       }
-
-       err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION,
-                                   &io_type);
-       if (err) {
-               brcmf_err("Failed to get D11 version (%d)\n", err);
-               goto cfg80211_p2p_attach_out;
-       }
-       cfg->d11inf.io_type = (u8)io_type;
-       brcmu_d11_attach(&cfg->d11inf);
-
-       return cfg;
-
-cfg80211_p2p_attach_out:
-       wl_deinit_priv(cfg);
-
-cfg80211_attach_out:
-       brcmf_free_vif(vif);
-       return NULL;
-}
-
-void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
-{
-       if (!cfg)
-               return;
-
-       WARN_ON(!list_empty(&cfg->vif_list));
-       wiphy_unregister(cfg->wiphy);
-       brcmf_btcoex_detach(cfg);
-       wl_deinit_priv(cfg);
-       wiphy_free(cfg->wiphy);
-}
-
-static s32
-brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
-{
-       s32 err = 0;
-       __le32 roamtrigger[2];
-       __le32 roam_delta[2];
-
-       /*
-        * Setup timeout if Beacons are lost and roam is
-        * off to report link down
-        */
-       if (brcmf_roamoff) {
-               err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
-               if (err) {
-                       brcmf_err("bcn_timeout error (%d)\n", err);
-                       goto dongle_rom_out;
-               }
-       }
-
-       /*
-        * Enable/Disable built-in roaming to allow supplicant
-        * to take care of roaming
-        */
-       brcmf_dbg(INFO, "Internal Roaming = %s\n",
-                 brcmf_roamoff ? "Off" : "On");
-       err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
-       if (err) {
-               brcmf_err("roam_off error (%d)\n", err);
-               goto dongle_rom_out;
-       }
-
-       roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
-       roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
-       err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
-                                    (void *)roamtrigger, sizeof(roamtrigger));
-       if (err) {
-               brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
-               goto dongle_rom_out;
-       }
-
-       roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
-       roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
-       err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
-                                    (void *)roam_delta, sizeof(roam_delta));
-       if (err) {
-               brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
-               goto dongle_rom_out;
+       roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
+       roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
+       err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
+                                    (void *)roam_delta, sizeof(roam_delta));
+       if (err) {
+               brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
+               goto dongle_rom_out;
        }
 
 dongle_rom_out:
@@ -5187,25 +4854,77 @@ dongle_scantime_out:
        return err;
 }
 
+/* Filter the list of channels received from firmware counting only
+ * the 20MHz channels. The wiphy band data only needs those which get
+ * flagged to indicate if they can take part in higher bandwidth.
+ */
+static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg,
+                                      struct brcmf_chanspec_list *chlist,
+                                      u32 chcnt[])
+{
+       u32 total = le32_to_cpu(chlist->count);
+       struct brcmu_chan ch;
+       int i;
+
+       for (i = 0; i <= total; i++) {
+               ch.chspec = (u16)le32_to_cpu(chlist->element[i]);
+               cfg->d11inf.decchspec(&ch);
+
+               /* Firmware gives a ordered list. We skip non-20MHz
+                * channels is 2G. For 5G we can abort upon reaching
+                * a non-20MHz channel in the list.
+                */
+               if (ch.bw != BRCMU_CHAN_BW_20) {
+                       if (ch.band == BRCMU_CHAN_BAND_5G)
+                               break;
+                       else
+                               continue;
+               }
+
+               if (ch.band == BRCMU_CHAN_BAND_2G)
+                       chcnt[0] += 1;
+               else if (ch.band == BRCMU_CHAN_BAND_5G)
+                       chcnt[1] += 1;
+       }
+}
 
-static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
-                                  u32 bw_cap[])
+static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
+                                          struct brcmu_chan *ch)
+{
+       u32 ht40_flag;
+
+       ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
+       if (ch->sb == BRCMU_CHAN_SB_U) {
+               if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+                       channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+               channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
+       } else {
+               /* It should be one of
+                * IEEE80211_CHAN_NO_HT40 or
+                * IEEE80211_CHAN_NO_HT40PLUS
+                */
+               channel->flags &= ~IEEE80211_CHAN_NO_HT40;
+               if (ht40_flag == IEEE80211_CHAN_NO_HT40)
+                       channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
+       }
+}
+
+static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
+                                   u32 bw_cap[])
 {
        struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
-       struct ieee80211_channel *band_chan_arr;
+       struct ieee80211_supported_band *band;
+       struct ieee80211_channel *channel;
+       struct wiphy *wiphy;
        struct brcmf_chanspec_list *list;
        struct brcmu_chan ch;
-       s32 err;
+       int err;
        u8 *pbuf;
        u32 i, j;
        u32 total;
-       enum ieee80211_band band;
-       u32 channel;
-       u32 *n_cnt;
+       u32 chaninfo;
+       u32 chcnt[2] = { 0, 0 };
        u32 index;
-       u32 ht40_flag;
-       bool update;
-       u32 array_size;
 
        pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
 
@@ -5218,11 +4937,45 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                                       BRCMF_DCMD_MEDLEN);
        if (err) {
                brcmf_err("get chanspecs error (%d)\n", err);
-               goto exit;
+               goto fail_pbuf;
        }
 
-       __wl_band_2ghz.n_channels = 0;
-       __wl_band_5ghz_a.n_channels = 0;
+       brcmf_count_20mhz_channels(cfg, list, chcnt);
+       wiphy = cfg_to_wiphy(cfg);
+       if (chcnt[0]) {
+               band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
+                              GFP_KERNEL);
+               if (band == NULL) {
+                       err = -ENOMEM;
+                       goto fail_pbuf;
+               }
+               band->channels = kcalloc(chcnt[0], sizeof(*channel),
+                                        GFP_KERNEL);
+               if (band->channels == NULL) {
+                       kfree(band);
+                       err = -ENOMEM;
+                       goto fail_pbuf;
+               }
+               band->n_channels = 0;
+               wiphy->bands[IEEE80211_BAND_2GHZ] = band;
+       }
+       if (chcnt[1]) {
+               band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a),
+                              GFP_KERNEL);
+               if (band == NULL) {
+                       err = -ENOMEM;
+                       goto fail_band2g;
+               }
+               band->channels = kcalloc(chcnt[1], sizeof(*channel),
+                                        GFP_KERNEL);
+               if (band->channels == NULL) {
+                       kfree(band);
+                       err = -ENOMEM;
+                       goto fail_band2g;
+               }
+               band->n_channels = 0;
+               wiphy->bands[IEEE80211_BAND_5GHZ] = band;
+       }
 
        total = le32_to_cpu(list->count);
        for (i = 0; i < total; i++) {
@@ -5230,97 +4983,148 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
                cfg->d11inf.decchspec(&ch);
 
                if (ch.band == BRCMU_CHAN_BAND_2G) {
-                       band_chan_arr = __wl_2ghz_channels;
-                       array_size = ARRAY_SIZE(__wl_2ghz_channels);
-                       n_cnt = &__wl_band_2ghz.n_channels;
-                       band = IEEE80211_BAND_2GHZ;
+                       band = wiphy->bands[IEEE80211_BAND_2GHZ];
                } else if (ch.band == BRCMU_CHAN_BAND_5G) {
-                       band_chan_arr = __wl_5ghz_a_channels;
-                       array_size = ARRAY_SIZE(__wl_5ghz_a_channels);
-                       n_cnt = &__wl_band_5ghz_a.n_channels;
-                       band = IEEE80211_BAND_5GHZ;
+                       band = wiphy->bands[IEEE80211_BAND_5GHZ];
                } else {
                        brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
                        continue;
                }
-               if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) &&
+               if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
                    ch.bw == BRCMU_CHAN_BW_40)
                        continue;
-               if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) &&
+               if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
                    ch.bw == BRCMU_CHAN_BW_80)
                        continue;
-               update = false;
-               for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
-                       if (band_chan_arr[j].hw_value == ch.chnum) {
-                               update = true;
+
+               channel = band->channels;
+               index = band->n_channels;
+               for (j = 0; j < band->n_channels; j++) {
+                       if (channel[j].hw_value == ch.chnum) {
+                               index = j;
                                break;
                        }
                }
-               if (update)
-                       index = j;
-               else
-                       index = *n_cnt;
-               if (index <  array_size) {
-                       band_chan_arr[index].center_freq =
-                               ieee80211_channel_to_frequency(ch.chnum, band);
-                       band_chan_arr[index].hw_value = ch.chnum;
-
-                       /* assuming the chanspecs order is HT20,
-                        * HT40 upper, HT40 lower, and VHT80.
+               channel[index].center_freq =
+                       ieee80211_channel_to_frequency(ch.chnum, band->band);
+               channel[index].hw_value = ch.chnum;
+
+               /* assuming the chanspecs order is HT20,
+                * HT40 upper, HT40 lower, and VHT80.
+                */
+               if (ch.bw == BRCMU_CHAN_BW_80) {
+                       channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
+               } else if (ch.bw == BRCMU_CHAN_BW_40) {
+                       brcmf_update_bw40_channel_flag(&channel[index], &ch);
+               } else {
+                       /* disable other bandwidths for now as mentioned
+                        * order assure they are enabled for subsequent
+                        * chanspecs.
                         */
-                       if (ch.bw == BRCMU_CHAN_BW_80) {
-                               band_chan_arr[index].flags &=
-                                       ~IEEE80211_CHAN_NO_80MHZ;
-                       } else if (ch.bw == BRCMU_CHAN_BW_40) {
-                               ht40_flag = band_chan_arr[index].flags &
-                                           IEEE80211_CHAN_NO_HT40;
-                               if (ch.sb == BRCMU_CHAN_SB_U) {
-                                       if (ht40_flag == IEEE80211_CHAN_NO_HT40)
-                                               band_chan_arr[index].flags &=
-                                                       ~IEEE80211_CHAN_NO_HT40;
-                                       band_chan_arr[index].flags |=
-                                               IEEE80211_CHAN_NO_HT40PLUS;
-                               } else {
-                                       /* It should be one of
-                                        * IEEE80211_CHAN_NO_HT40 or
-                                        * IEEE80211_CHAN_NO_HT40PLUS
-                                        */
-                                       band_chan_arr[index].flags &=
-                                                       ~IEEE80211_CHAN_NO_HT40;
-                                       if (ht40_flag == IEEE80211_CHAN_NO_HT40)
-                                               band_chan_arr[index].flags |=
-                                                   IEEE80211_CHAN_NO_HT40MINUS;
-                               }
-                       } else {
-                               /* disable other bandwidths for now as mentioned
-                                * order assure they are enabled for subsequent
-                                * chanspecs.
-                                */
-                               band_chan_arr[index].flags =
-                                               IEEE80211_CHAN_NO_HT40 |
-                                               IEEE80211_CHAN_NO_80MHZ;
-                               ch.bw = BRCMU_CHAN_BW_20;
-                               cfg->d11inf.encchspec(&ch);
-                               channel = ch.chspec;
-                               err = brcmf_fil_bsscfg_int_get(ifp,
-                                                              "per_chan_info",
-                                                              &channel);
-                               if (!err) {
-                                       if (channel & WL_CHAN_RADAR)
-                                               band_chan_arr[index].flags |=
-                                                       (IEEE80211_CHAN_RADAR |
-                                                       IEEE80211_CHAN_NO_IR);
-                                       if (channel & WL_CHAN_PASSIVE)
-                                               band_chan_arr[index].flags |=
-                                                   IEEE80211_CHAN_NO_IR;
-                               }
+                       channel[index].flags = IEEE80211_CHAN_NO_HT40 |
+                                              IEEE80211_CHAN_NO_80MHZ;
+                       ch.bw = BRCMU_CHAN_BW_20;
+                       cfg->d11inf.encchspec(&ch);
+                       chaninfo = ch.chspec;
+                       err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
+                                                      &chaninfo);
+                       if (!err) {
+                               if (chaninfo & WL_CHAN_RADAR)
+                                       channel[index].flags |=
+                                               (IEEE80211_CHAN_RADAR |
+                                                IEEE80211_CHAN_NO_IR);
+                               if (chaninfo & WL_CHAN_PASSIVE)
+                                       channel[index].flags |=
+                                               IEEE80211_CHAN_NO_IR;
                        }
-                       if (!update)
-                               (*n_cnt)++;
                }
+               if (index == band->n_channels)
+                       band->n_channels++;
        }
-exit:
        kfree(pbuf);
+       return 0;
+
+fail_band2g:
+       kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+       kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
+       wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+fail_pbuf:
+       kfree(pbuf);
+       return err;
+}
+
+static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
+{
+       struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+       struct ieee80211_supported_band *band;
+       struct brcmf_fil_bwcap_le band_bwcap;
+       struct brcmf_chanspec_list *list;
+       u8 *pbuf;
+       u32 val;
+       int err;
+       struct brcmu_chan ch;
+       u32 num_chan;
+       int i, j;
+
+       /* verify support for bw_cap command */
+       val = WLC_BAND_5G;
+       err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
+
+       if (!err) {
+               /* only set 2G bandwidth using bw_cap command */
+               band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
+               band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
+               err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
+                                              sizeof(band_bwcap));
+       } else {
+               brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
+               val = WLC_N_BW_40ALL;
+               err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
+       }
+
+       if (!err) {
+               /* update channel info in 2G band */
+               pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+               if (pbuf == NULL)
+                       return -ENOMEM;
+
+               ch.band = BRCMU_CHAN_BAND_2G;
+               ch.bw = BRCMU_CHAN_BW_40;
+               ch.chnum = 0;
+               cfg->d11inf.encchspec(&ch);
+
+               /* pass encoded chanspec in query */
+               *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
+
+               err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+                                              BRCMF_DCMD_MEDLEN);
+               if (err) {
+                       brcmf_err("get chanspecs error (%d)\n", err);
+                       kfree(pbuf);
+                       return err;
+               }
+
+               band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
+               list = (struct brcmf_chanspec_list *)pbuf;
+               num_chan = le32_to_cpu(list->count);
+               for (i = 0; i < num_chan; i++) {
+                       ch.chspec = (u16)le32_to_cpu(list->element[i]);
+                       cfg->d11inf.decchspec(&ch);
+                       if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
+                               continue;
+                       if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
+                               continue;
+                       for (j = 0; j < band->n_channels; j++) {
+                               if (band->channels[j].hw_value == ch.chnum)
+                                       break;
+                       }
+                       if (WARN_ON(j == band->n_channels))
+                               continue;
+
+                       brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
+               }
+       }
        return err;
 }
 
@@ -5414,44 +5218,19 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
        band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
 }
 
-static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
+static int brcmf_setup_wiphybands(struct wiphy *wiphy)
 {
+       struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
        struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
-       struct wiphy *wiphy;
-       s32 phy_list;
-       u32 band_list[3];
        u32 nmode = 0;
        u32 vhtmode = 0;
-       u32 bw_cap[2] = { 0, 0 };
+       u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
        u32 rxchain;
        u32 nchain;
-       s8 phy;
-       s32 err;
-       u32 nband;
+       int err;
        s32 i;
-       struct ieee80211_supported_band *bands[2] = { NULL, NULL };
        struct ieee80211_supported_band *band;
 
-       err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
-                                    &phy_list, sizeof(phy_list));
-       if (err) {
-               brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err);
-               return err;
-       }
-
-       phy = ((char *)&phy_list)[0];
-       brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy);
-
-
-       err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST,
-                                    &band_list, sizeof(band_list));
-       if (err) {
-               brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err);
-               return err;
-       }
-       brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n",
-                 band_list[0], band_list[1], band_list[2]);
-
        (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
        err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
        if (err) {
@@ -5473,44 +5252,129 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
        }
        brcmf_dbg(INFO, "nchain=%d\n", nchain);
 
-       err = brcmf_construct_reginfo(cfg, bw_cap);
+       err = brcmf_construct_chaninfo(cfg, bw_cap);
        if (err) {
-               brcmf_err("brcmf_construct_reginfo failed (%d)\n", err);
+               brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
                return err;
        }
 
-       nband = band_list[0];
-
-       for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) {
-               band = NULL;
-               if ((band_list[i] == WLC_BAND_5G) &&
-                   (__wl_band_5ghz_a.n_channels > 0))
-                       band = &__wl_band_5ghz_a;
-               else if ((band_list[i] == WLC_BAND_2G) &&
-                        (__wl_band_2ghz.n_channels > 0))
-                       band = &__wl_band_2ghz;
-               else
+       wiphy = cfg_to_wiphy(cfg);
+       for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
+               band = wiphy->bands[i];
+               if (band == NULL)
                        continue;
 
                if (nmode)
                        brcmf_update_ht_cap(band, bw_cap, nchain);
                if (vhtmode)
                        brcmf_update_vht_cap(band, bw_cap, nchain);
-               bands[band->band] = band;
        }
 
-       wiphy = cfg_to_wiphy(cfg);
-       wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ];
-       wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ];
-       wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
-
-       return err;
+       return 0;
 }
 
+static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
+       {
+               .max = 2,
+               .types = BIT(NL80211_IFTYPE_STATION) |
+                        BIT(NL80211_IFTYPE_ADHOC) |
+                        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 struct ieee80211_iface_combination brcmf_iface_combos[] = {
+       {
+                .max_interfaces = BRCMF_IFACE_MAX_CNT,
+                .num_different_channels = 1,
+                .n_limits = ARRAY_SIZE(brcmf_iface_limits),
+                .limits = brcmf_iface_limits
+       }
+};
+
+static const struct ieee80211_txrx_stypes
+brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
+       [NL80211_IFTYPE_STATION] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_CLIENT] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       },
+       [NL80211_IFTYPE_P2P_GO] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+                     BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+                     BIT(IEEE80211_STYPE_AUTH >> 4) |
+                     BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+                     BIT(IEEE80211_STYPE_ACTION >> 4)
+       },
+       [NL80211_IFTYPE_P2P_DEVICE] = {
+               .tx = 0xffff,
+               .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+       }
+};
 
-static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
+static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
 {
-       return brcmf_update_wiphybands(cfg);
+       /* scheduled scan settings */
+       wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
+       wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
+       wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+       wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+}
+
+static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
+{
+       struct ieee80211_iface_combination ifc_combo;
+       wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+       wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
+       wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
+       wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                BIT(NL80211_IFTYPE_ADHOC) |
+                                BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                BIT(NL80211_IFTYPE_P2P_GO) |
+                                BIT(NL80211_IFTYPE_P2P_DEVICE);
+       /* need VSDB firmware feature for concurrent channels */
+       ifc_combo = brcmf_iface_combos[0];
+       if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
+               ifc_combo.num_different_channels = 2;
+       wiphy->iface_combinations = kmemdup(&ifc_combo,
+                                           sizeof(ifc_combo),
+                                           GFP_KERNEL);
+       wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
+       wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+       wiphy->cipher_suites = __wl_cipher_suites;
+       wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+       wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
+                       WIPHY_FLAG_OFFCHAN_TX |
+                       WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                       WIPHY_FLAG_SUPPORTS_TDLS;
+       if (!brcmf_roamoff)
+               wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+       wiphy->mgmt_stypes = brcmf_txrx_stypes;
+       wiphy->max_remain_on_channel_duration = 5000;
+       brcmf_wiphy_pno_params(wiphy);
+
+       /* vendor commands/events support */
+       wiphy->vendor_commands = brcmf_vendor_cmds;
+       wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
+
+       return brcmf_setup_wiphybands(wiphy);
 }
 
 static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
@@ -5548,9 +5412,6 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
                                          NULL, NULL);
        if (err)
                goto default_conf_out;
-       err = brcmf_dongle_probecap(cfg);
-       if (err)
-               goto default_conf_out;
 
        brcmf_configure_arp_offload(ifp, true);
 
@@ -5625,16 +5486,15 @@ enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
        return wdev->iftype;
 }
 
-u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state)
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state)
 {
        struct brcmf_cfg80211_vif *vif;
-       bool result = 0;
 
        list_for_each_entry(vif, &cfg->vif_list, list) {
                if (test_bit(state, &vif->sme_state))
-                       result++;
+                       return true;
        }
-       return result;
+       return false;
 }
 
 static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
@@ -5679,3 +5539,150 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
                                  vif_event_equals(event, action), timeout);
 }
 
+static void brcmf_free_wiphy(struct wiphy *wiphy)
+{
+       kfree(wiphy->iface_combinations);
+       if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+               kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
+               kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
+       }
+       if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
+               kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
+               kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
+       }
+       wiphy_free(wiphy);
+}
+
+struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
+                                                 struct device *busdev)
+{
+       struct net_device *ndev = drvr->iflist[0]->ndev;
+       struct brcmf_cfg80211_info *cfg;
+       struct wiphy *wiphy;
+       struct brcmf_cfg80211_vif *vif;
+       struct brcmf_if *ifp;
+       s32 err = 0;
+       s32 io_type;
+       u16 *cap = NULL;
+
+       if (!ndev) {
+               brcmf_err("ndev is invalid\n");
+               return NULL;
+       }
+
+       ifp = netdev_priv(ndev);
+       wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
+       if (!wiphy) {
+               brcmf_err("Could not allocate wiphy device\n");
+               return NULL;
+       }
+       set_wiphy_dev(wiphy, busdev);
+
+       cfg = wiphy_priv(wiphy);
+       cfg->wiphy = wiphy;
+       cfg->pub = drvr;
+       init_vif_event(&cfg->vif_event);
+       INIT_LIST_HEAD(&cfg->vif_list);
+
+       vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
+       if (IS_ERR(vif))
+               goto wiphy_out;
+
+       vif->ifp = ifp;
+       vif->wdev.netdev = ndev;
+       ndev->ieee80211_ptr = &vif->wdev;
+       SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
+
+       err = wl_init_priv(cfg);
+       if (err) {
+               brcmf_err("Failed to init iwm_priv (%d)\n", err);
+               brcmf_free_vif(vif);
+               goto wiphy_out;
+       }
+       ifp->vif = vif;
+
+       /* determine d11 io type before wiphy setup */
+       err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
+       if (err) {
+               brcmf_err("Failed to get D11 version (%d)\n", err);
+               goto priv_out;
+       }
+       cfg->d11inf.io_type = (u8)io_type;
+       brcmu_d11_attach(&cfg->d11inf);
+
+       err = brcmf_setup_wiphy(wiphy, ifp);
+       if (err < 0)
+               goto priv_out;
+
+       brcmf_dbg(INFO, "Registering custom regulatory\n");
+       wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+       wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
+
+       /* firmware defaults to 40MHz disabled in 2G band. We signal
+        * cfg80211 here that we do and have it decide we can enable
+        * it. But first check if device does support 2G operation.
+        */
+       if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
+               cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
+               *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       }
+       err = wiphy_register(wiphy);
+       if (err < 0) {
+               brcmf_err("Could not register wiphy device (%d)\n", err);
+               goto priv_out;
+       }
+
+       /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
+        * setup 40MHz in 2GHz band and enable OBSS scanning.
+        */
+       if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
+               err = brcmf_enable_bw40_2g(cfg);
+               if (!err)
+                       err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
+                                                     BRCMF_OBSS_COEX_AUTO);
+               else
+                       *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+       }
+
+       err = brcmf_p2p_attach(cfg);
+       if (err) {
+               brcmf_err("P2P initilisation failed (%d)\n", err);
+               goto wiphy_unreg_out;
+       }
+       err = brcmf_btcoex_attach(cfg);
+       if (err) {
+               brcmf_err("BT-coex initialisation failed (%d)\n", err);
+               brcmf_p2p_detach(&cfg->p2p);
+               goto wiphy_unreg_out;
+       }
+
+       err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
+       if (err) {
+               brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
+               wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+       }
+
+       return cfg;
+
+wiphy_unreg_out:
+       wiphy_unregister(cfg->wiphy);
+priv_out:
+       wl_deinit_priv(cfg);
+       brcmf_free_vif(vif);
+wiphy_out:
+       brcmf_free_wiphy(wiphy);
+       return NULL;
+}
+
+void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
+{
+       if (!cfg)
+               return;
+
+       WARN_ON(!list_empty(&cfg->vif_list));
+       wiphy_unregister(cfg->wiphy);
+       brcmf_btcoex_detach(cfg);
+       brcmf_p2p_detach(&cfg->p2p);
+       wl_deinit_priv(cfg);
+       brcmf_free_wiphy(cfg->wiphy);
+}
index 283c525a44f759d7995d882df93fdb700360abed..f9fb10998e7960d68ca03db0afbf2a7d84f780b2 100644 (file)
@@ -477,7 +477,7 @@ const struct brcmf_tlv *
 brcmf_parse_tlvs(const void *buf, int buflen, uint key);
 u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
                        struct ieee80211_channel *ch);
-u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state);
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state);
 void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
                                  struct brcmf_cfg80211_vif *vif);
 bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);
index af8ba64ace39286e3214d69bfc23f776e66b6336..1b474828d5b884fb957daf52ac59ed3c135b4b1c 100644 (file)
@@ -4707,41 +4707,6 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core,
        return err;
 }
 
-static void brcms_c_attach_antgain_init(struct brcms_c_info *wlc)
-{
-       uint unit;
-       unit = wlc->pub->unit;
-
-       if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) {
-               /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */
-               wlc->band->antgain = 8;
-       } else if (wlc->band->antgain == -1) {
-               wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in"
-                         " srom, using 2dB\n", unit, __func__);
-               wlc->band->antgain = 8;
-       } else {
-               s8 gain, fract;
-               /* Older sroms specified gain in whole dbm only.  In order
-                * be able to specify qdbm granularity and remain backward
-                * compatible the whole dbms are now encoded in only
-                * low 6 bits and remaining qdbms are encoded in the hi 2 bits.
-                * 6 bit signed number ranges from -32 - 31.
-                *
-                * Examples:
-                * 0x1 = 1 db,
-                * 0xc1 = 1.75 db (1 + 3 quarters),
-                * 0x3f = -1 (-1 + 0 quarters),
-                * 0x7f = -.75 (-1 + 1 quarters) = -3 qdbm.
-                * 0xbf = -.50 (-1 + 2 quarters) = -2 qdbm.
-                */
-               gain = wlc->band->antgain & 0x3f;
-               gain <<= 2;     /* Sign extend */
-               gain >>= 2;
-               fract = (wlc->band->antgain & 0xc0) >> 6;
-               wlc->band->antgain = 4 * gain + fract;
-       }
-}
-
 static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc)
 {
        int aa;
@@ -4780,8 +4745,6 @@ static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc)
        else
                wlc->band->antgain = sprom->antenna_gain.a0;
 
-       brcms_c_attach_antgain_init(wlc);
-
        return true;
 }
 
index b0fd807f2b2b6bb18e8dcabb80164fdb6578d16d..57ecc05802e965546a47346e7b8b8914ab83c062 100644 (file)
@@ -1538,11 +1538,7 @@ static s8
 wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band,
                                 u8 rate)
 {
-       s8 offset = 0;
-
-       if (!pi->user_txpwr_at_rfport)
-               return offset;
-       return offset;
+       return 0;
 }
 
 void wlc_phy_txpower_recalc_target(struct brcms_phy *pi)
index d816270db3be56cd7af0ec130fc6bf74b9aa0b69..64d1a7ba040ccc430dc65e7f5f3b662dbcd388f0 100644 (file)
 #ifndef        _BRCM_HW_IDS_H_
 #define        _BRCM_HW_IDS_H_
 
-#define BCM4313_D11N2G_ID      0x4727  /* 4313 802.11n 2.4G device */
+#include <linux/pci_ids.h>
+#include <linux/mmc/sdio_ids.h>
+
+#define BRCM_USB_VENDOR_ID_BROADCOM    0x0a5c
+#define BRCM_PCIE_VENDOR_ID_BROADCOM   PCI_VENDOR_ID_BROADCOM
+#define BRCM_SDIO_VENDOR_ID_BROADCOM   SDIO_VENDOR_ID_BROADCOM
+
+/* Chipcommon Core Chip IDs */
+#define BRCM_CC_43143_CHIP_ID          43143
+#define BRCM_CC_43235_CHIP_ID          43235
+#define BRCM_CC_43236_CHIP_ID          43236
+#define BRCM_CC_43238_CHIP_ID          43238
+#define BRCM_CC_43241_CHIP_ID          0x4324
+#define BRCM_CC_43242_CHIP_ID          43242
+#define BRCM_CC_4329_CHIP_ID           0x4329
+#define BRCM_CC_4330_CHIP_ID           0x4330
+#define BRCM_CC_4334_CHIP_ID           0x4334
+#define BRCM_CC_43362_CHIP_ID          43362
+#define BRCM_CC_4335_CHIP_ID           0x4335
+#define BRCM_CC_4339_CHIP_ID           0x4339
+#define BRCM_CC_4354_CHIP_ID           0x4354
+#define BRCM_CC_43566_CHIP_ID          43566
+#define BRCM_CC_43569_CHIP_ID          43569
+
+/* SDIO Device IDs */
+#define BRCM_SDIO_43143_DEVICE_ID      BRCM_CC_43143_CHIP_ID
+#define BRCM_SDIO_43241_DEVICE_ID      BRCM_CC_43241_CHIP_ID
+#define BRCM_SDIO_4329_DEVICE_ID       BRCM_CC_4329_CHIP_ID
+#define BRCM_SDIO_4330_DEVICE_ID       BRCM_CC_4330_CHIP_ID
+#define BRCM_SDIO_4334_DEVICE_ID       BRCM_CC_4334_CHIP_ID
+#define BRCM_SDIO_43362_DEVICE_ID      BRCM_CC_43362_CHIP_ID
+#define BRCM_SDIO_4335_4339_DEVICE_ID  BRCM_CC_4335_CHIP_ID
+#define BRCM_SDIO_4354_DEVICE_ID       BRCM_CC_4354_CHIP_ID
 
+/* USB Device IDs */
+#define BRCM_USB_43143_DEVICE_ID       0xbd1e
+#define BRCM_USB_43236_DEVICE_ID       0xbd17
+#define BRCM_USB_43242_DEVICE_ID       0xbd1f
+#define BRCM_USB_43569_DEVICE_ID       0xbd27
+#define BRCM_USB_BCMFW_DEVICE_ID       0x0bdc
+
+/* brcmsmac IDs */
+#define BCM4313_D11N2G_ID      0x4727  /* 4313 802.11n 2.4G device */
 #define BCM43224_D11N_ID       0x4353  /* 43224 802.11n dualband device */
 #define BCM43224_D11N_ID_VEN1  0x0576  /* Vendor specific 43224 802.11n db */
-
 #define BCM43225_D11N2G_ID     0x4357  /* 43225 802.11n 2.4GHz device */
-
 #define BCM43236_D11N_ID       0x4346  /* 43236 802.11n dualband device */
 #define BCM43236_D11N2G_ID     0x4347  /* 43236 802.11n 2.4GHz device */
 
-/* Chipcommon Core Chip IDs */
 #define BCM4313_CHIP_ID                0x4313
-#define BCM43143_CHIP_ID       43143
 #define BCM43224_CHIP_ID       43224
-#define BCM43225_CHIP_ID       43225
-#define BCM43235_CHIP_ID       43235
-#define BCM43236_CHIP_ID       43236
-#define BCM43238_CHIP_ID       43238
-#define BCM43241_CHIP_ID       0x4324
-#define BCM4329_CHIP_ID                0x4329
-#define BCM4330_CHIP_ID                0x4330
-#define BCM4331_CHIP_ID                0x4331
-#define BCM4334_CHIP_ID                0x4334
-#define BCM4335_CHIP_ID                0x4335
-#define BCM43362_CHIP_ID       43362
-#define BCM4339_CHIP_ID                0x4339
-#define BCM4354_CHIP_ID                0x4354
 
 #endif                         /* _BRCM_HW_IDS_H_ */
index 9afcd4ce3368d37908a055df935f29cf4032f259..b2fb6c632092b202d484dcc6d1d73b2840ecf255 100644 (file)
@@ -53,9 +53,10 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
 
 int cw1200_hw_scan(struct ieee80211_hw *hw,
                   struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req)
+                  struct ieee80211_scan_request *hw_req)
 {
        struct cw1200_common *priv = hw->priv;
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wsm_template_frame frame = {
                .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
        };
index 5a8296ccfa82c962fd0f07ddfec80cfcee01bb14..cc75459e5784d9e8e28a677ba39195e36413e500 100644 (file)
@@ -41,7 +41,7 @@ struct cw1200_scan {
 
 int cw1200_hw_scan(struct ieee80211_hw *hw,
                   struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req);
+                  struct ieee80211_scan_request *hw_req);
 void cw1200_scan_work(struct work_struct *work);
 void cw1200_scan_timeout(struct work_struct *work);
 void cw1200_clear_recent_scan_work(struct work_struct *work);
index cd0cad7f775993661af9e8f4577c89976b15b811..5b84664db13b3c0c36d27d2ef74d87cff618b2c7 100644 (file)
@@ -2289,7 +2289,6 @@ static int cw1200_upload_null(struct cw1200_common *priv)
 
 static int cw1200_upload_qosnull(struct cw1200_common *priv)
 {
-       int ret = 0;
        /* TODO:  This needs to be implemented
 
        struct wsm_template_frame frame = {
@@ -2306,7 +2305,7 @@ static int cw1200_upload_qosnull(struct cw1200_common *priv)
        dev_kfree_skb(frame.skb);
 
        */
-       return ret;
+       return 0;
 }
 
 static int cw1200_enable_beaconing(struct cw1200_common *priv,
index 3adb24021a282703a0966e79a61fa5f22af128fb..5f31b72a492181657f784408a3d33a11b418f221 100644 (file)
@@ -100,8 +100,7 @@ static inline void libipw_networks_free(struct libipw_device *ieee)
        int i;
 
        for (i = 0; i < MAX_NETWORK_COUNT; i++) {
-               if (ieee->networks[i]->ibss_dfs)
-                       kfree(ieee->networks[i]->ibss_dfs);
+               kfree(ieee->networks[i]->ibss_dfs);
                kfree(ieee->networks[i]);
        }
 }
index ecc674627e6e10b30a5a7b11ab3150c1ad42b37e..03de7467aecf7b63fef9ae007a15c13dbe5594f0 100644 (file)
@@ -1572,8 +1572,9 @@ il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
 
 int
 il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-              struct cfg80211_scan_request *req)
+              struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct il_priv *il = hw->priv;
        int ret;
 
index ea5c0f863c4ee35b2cf6738569a5cb3253553c12..5b972798bdffb66d6acd242e80765fc574ce83af 100644 (file)
@@ -1787,7 +1787,7 @@ int il_scan_cancel(struct il_priv *il);
 int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms);
 void il_force_scan_end(struct il_priv *il);
 int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
-                  struct cfg80211_scan_request *req);
+                  struct ieee80211_scan_request *hw_req);
 void il_internal_short_hw_scan(struct il_priv *il);
 int il_force_reset(struct il_priv *il, bool external);
 u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
index 7fd50428b93494416db2d4c46a3862c18c5bc2e1..6451d2b6abcff3c33f486c9d7eb90390647a1266 100644 (file)
@@ -20,16 +20,17 @@ config IWLWIFI
                Intel 2000 Series Wi-Fi Adapters
                Intel 7260 Wi-Fi Adapter
                Intel 3160 Wi-Fi Adapter
+               Intel 7265 Wi-Fi Adapter
 
 
          This driver uses the kernel's mac80211 subsystem.
 
-         In order to use this driver, you will need a microcode (uCode)
+         In order to use this driver, you will need a firmware
          image for it. You can obtain the microcode from:
 
-                 <http://intellinuxwireless.org/>.
+                 <http://wireless.kernel.org/en/users/Drivers/iwlwifi>.
 
-         The microcode is typically installed in /lib/firmware. You can
+         The firmware is typically installed in /lib/firmware. You can
          look in the hotplug script /etc/hotplug/firmware.agent to
          determine which directory FIRMWARE_DIR is set to when the script
          runs.
@@ -39,9 +40,10 @@ config IWLWIFI
          say M here and read <file:Documentation/kbuild/modules.txt>.  The
          module will be called iwlwifi.
 
+if IWLWIFI
+
 config IWLWIFI_LEDS
        bool
-       depends on IWLWIFI
        depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI
        select LEDS_TRIGGERS
        select MAC80211_LEDS
@@ -49,7 +51,7 @@ config IWLWIFI_LEDS
 
 config IWLDVM
        tristate "Intel Wireless WiFi DVM Firmware support"
-       depends on IWLWIFI
+       depends on m
        default IWLWIFI
        help
          This is the driver that supports the DVM firmware which is
@@ -58,7 +60,7 @@ config IWLDVM
 
 config IWLMVM
        tristate "Intel Wireless WiFi MVM Firmware support"
-       depends on IWLWIFI
+       depends on m
        help
          This is the driver that supports the MVM firmware which is
          currently only available for 7260 and 3160 devices.
@@ -70,7 +72,7 @@ config IWLWIFI_OPMODE_MODULAR
        default y if IWLMVM=m
 
 comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM"
-       depends on IWLWIFI && IWLDVM=n && IWLMVM=n
+       depends on IWLDVM=n && IWLMVM=n
 
 config IWLWIFI_BCAST_FILTERING
        bool "Enable broadcast filtering"
@@ -86,11 +88,9 @@ config IWLWIFI_BCAST_FILTERING
          expect incoming broadcasts for their normal operations.
 
 menu "Debugging Options"
-       depends on IWLWIFI
 
 config IWLWIFI_DEBUG
        bool "Enable full debugging output in the iwlwifi driver"
-       depends on IWLWIFI
        ---help---
          This option will enable debug tracing output for the iwlwifi drivers
 
@@ -115,7 +115,7 @@ config IWLWIFI_DEBUG
 
 config IWLWIFI_DEBUGFS
         bool "iwlwifi debugfs support"
-        depends on IWLWIFI && MAC80211_DEBUGFS
+        depends on MAC80211_DEBUGFS
         ---help---
          Enable creation of debugfs files for the iwlwifi drivers. This
          is a low-impact option that allows getting insight into the
@@ -123,13 +123,12 @@ config IWLWIFI_DEBUGFS
 
 config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE
         bool "Experimental uCode support"
-        depends on IWLWIFI && IWLWIFI_DEBUG
+        depends on IWLWIFI_DEBUG
         ---help---
          Enable use of experimental ucode for testing and debugging.
 
 config IWLWIFI_DEVICE_TRACING
        bool "iwlwifi device access tracing"
-       depends on IWLWIFI
        depends on EVENT_TRACING
        help
          Say Y here to trace all commands, including TX frames and IO
@@ -145,3 +144,5 @@ config IWLWIFI_DEVICE_TRACING
          If unsure, say Y so we can help you better when problems
          occur.
 endmenu
+
+endif
index 29af7b51e3708788d02f4a1651205a348a5102dd..afb98f4fdaf3608afd3c9ab844ed34c8dff6851a 100644 (file)
@@ -1495,9 +1495,10 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
 
 static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
-                             struct cfg80211_scan_request *req)
+                             struct ieee80211_scan_request *hw_req)
 {
        struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+       struct cfg80211_scan_request *req = &hw_req->req;
        int ret;
 
        IWL_DEBUG_MAC80211(priv, "enter\n");
index f2c1439566b5ffa7814c02c041bf34bb3b3391d6..760c45c34ef36ea18a90c7762afac5c602796d3d 100644 (file)
 #include "commands.h"
 #include "power.h"
 
+static bool force_cam;
+module_param(force_cam, bool, 0644);
+MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)");
+
 /*
  * Setting power level allows the card to go to sleep when not busy.
  *
@@ -288,6 +292,11 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
        bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
        int dtimper;
 
+       if (force_cam) {
+               iwl_power_sleep_cam_cmd(priv, cmd);
+               return;
+       }
+
        dtimper = priv->hw->conf.ps_dtim_period ?: 1;
 
        if (priv->wowlan)
index 51c41531d81d7f5af8354aced5da7e6b4e647f78..44b19e015102096e39fd086539794ab608c4c359 100644 (file)
@@ -67,7 +67,7 @@
 #include "iwl-agn-hw.h"
 
 /* Highest firmware API version supported */
-#define IWL8000_UCODE_API_MAX  8
+#define IWL8000_UCODE_API_MAX  9
 
 /* Oldest version we won't warn about */
 #define IWL8000_UCODE_API_OK   8
@@ -85,6 +85,9 @@
 #define NVM_HW_SECTION_NUM_FAMILY_8000         10
 #define DEFAULT_NVM_FILE_FAMILY_8000           "iwl_nvm_8000.bin"
 
+/* Max SDIO RX aggregation size of the ADDBA request/response */
+#define MAX_RX_AGG_SIZE_8260_SDIO      28
+
 static const struct iwl_base_params iwl8000_base_params = {
        .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
        .num_of_queues = IWLAGN_NUM_QUEUES,
@@ -119,10 +122,9 @@ const struct iwl_cfg iwl8260_2ac_cfg = {
        .ht_params = &iwl8000_ht_params,
        .nvm_ver = IWL8000_NVM_VERSION,
        .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
-       .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
 };
 
-const struct iwl_cfg iwl8260_n_cfg = {
+const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
        .name = "Intel(R) Dual Band Wireless-AC 8260",
        .fw_name_pre = IWL8000_FW_PRE,
        IWL_DEVICE_8000,
@@ -130,6 +132,7 @@ const struct iwl_cfg iwl8260_n_cfg = {
        .nvm_ver = IWL8000_NVM_VERSION,
        .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
        .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
+       .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
 };
 
 MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
index b7047905f41a38ce6a88f27b93ad75384306958a..8da596db9abe9cd308132c8577d10fabc6be09bc 100644 (file)
@@ -240,6 +240,7 @@ struct iwl_pwr_tx_backoff {
  * @d0i3: device uses d0i3 instead of d3
  * @nvm_hw_section_num: the ID of the HW NVM section
  * @pwr_tx_backoffs: translation table between power limits and backoffs
+ * @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
  *
  * We enable the driver to be backward compatible wrt. hardware features.
  * API differences in uCode shouldn't be handled here but through TLVs
@@ -276,6 +277,7 @@ struct iwl_cfg {
        const struct iwl_pwr_tx_backoff *pwr_tx_backoffs;
        bool no_power_up_nic_in_init;
        const char *default_nvm_file;
+       unsigned int max_rx_agg_size;
 };
 
 /*
@@ -337,7 +339,7 @@ extern const struct iwl_cfg iwl7265_2ac_cfg;
 extern const struct iwl_cfg iwl7265_2n_cfg;
 extern const struct iwl_cfg iwl7265_n_cfg;
 extern const struct iwl_cfg iwl8260_2ac_cfg;
-extern const struct iwl_cfg iwl8260_n_cfg;
+extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
 #endif /* CONFIG_IWLMVM */
 
 #endif /* __IWL_CONFIG_H__ */
index f2a5c12269a3ed7de811399580c9ea908bc89173..77e3178040b2a80716ff97cb3c4771b356682897 100644 (file)
@@ -155,6 +155,8 @@ static struct iwlwifi_opmode_table {
        [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
 };
 
+#define IWL_DEFAULT_SCAN_CHANNELS 40
+
 /*
  * struct fw_sec: Just for the image parsing proccess.
  * For the fw storage we are using struct fw_desc.
@@ -565,6 +567,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
        }
 
        drv->fw.ucode_ver = le32_to_cpu(ucode->ver);
+       memcpy(drv->fw.human_readable, ucode->human_readable,
+              sizeof(drv->fw.human_readable));
        build = le32_to_cpu(ucode->build);
 
        if (build)
@@ -819,6 +823,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                        if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
                                goto invalid_tlv_len;
                        break;
+               case IWL_UCODE_TLV_N_SCAN_CHANNELS:
+                       if (tlv_len != sizeof(u32))
+                               goto invalid_tlv_len;
+                       capa->n_scan_channels =
+                               le32_to_cpup((__le32 *)tlv_data);
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
@@ -973,6 +983,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
        fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
        fw->ucode_capa.standard_phy_calibration_size =
                        IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
+       fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS;
 
        if (!api_ok)
                api_ok = api_max;
@@ -1394,3 +1405,7 @@ module_param_named(power_level, iwlwifi_mod_params.power_level,
                int, S_IRUGO);
 MODULE_PARM_DESC(power_level,
                 "default power save level (range from 1 - 5, default: 1)");
+
+module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
+MODULE_PARM_DESC(fw_monitor,
+                "firmware monitor - to debug FW (default: false - needs lots of memory)");
index c44cf1149648860e4e8faa4630140e8bb76ae6b3..07ff7e0028ee81518f0b7b2db618787cd5d1882e 100644 (file)
@@ -779,7 +779,6 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
        if (cfg->ht_params->ht40_bands & BIT(band)) {
                ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
                ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
-               ht_info->mcs.rx_mask[4] = 0x01;
                max_bit_rate = MAX_BIT_RATE_40_MHZ;
        }
 
index 2953ffceda3881d29be37812544a374816b2130c..de5994a776c765a99c66bc86e0533451d4c7a027 100644 (file)
 /**
  * enum iwl_fw_error_dump_type - types of data in the dump file
  * @IWL_FW_ERROR_DUMP_SRAM:
- * @IWL_FW_ERROR_DUMP_REG:
+ * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0
  * @IWL_FW_ERROR_DUMP_RXF:
  * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
  *     &struct iwl_fw_error_dump_txcmd packets
+ * @IWL_FW_ERROR_DUMP_DEV_FW_INFO:  struct %iwl_fw_error_dump_info
+ *     info on the device / firmware.
+ * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
+ * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several
+ *     sections like this in a single file.
  */
 enum iwl_fw_error_dump_type {
        IWL_FW_ERROR_DUMP_SRAM = 0,
-       IWL_FW_ERROR_DUMP_REG = 1,
+       IWL_FW_ERROR_DUMP_CSR = 1,
        IWL_FW_ERROR_DUMP_RXF = 2,
        IWL_FW_ERROR_DUMP_TXCMD = 3,
+       IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
+       IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
+       IWL_FW_ERROR_DUMP_PRPH = 6,
 
        IWL_FW_ERROR_DUMP_MAX,
 };
@@ -87,8 +95,8 @@ enum iwl_fw_error_dump_type {
 /**
  * struct iwl_fw_error_dump_data - data for one type
  * @type: %enum iwl_fw_error_dump_type
- * @len: the length starting from %data - must be a multiplier of 4.
- * @data: the data itself padded to be a multiplier of 4.
+ * @len: the length starting from %data
+ * @data: the data itself
  */
 struct iwl_fw_error_dump_data {
        __le32 type;
@@ -120,13 +128,60 @@ struct iwl_fw_error_dump_txcmd {
        u8 data[];
 } __packed;
 
+enum iwl_fw_error_dump_family {
+       IWL_FW_ERROR_DUMP_FAMILY_7 = 7,
+       IWL_FW_ERROR_DUMP_FAMILY_8 = 8,
+};
+
+/**
+ * struct iwl_fw_error_dump_info - info on the device / firmware
+ * @device_family: the family of the device (7 / 8)
+ * @hw_step: the step of the device
+ * @fw_human_readable: human readable FW version
+ * @dev_human_readable: name of the device
+ * @bus_human_readable: name of the bus used
+ */
+struct iwl_fw_error_dump_info {
+       __le32 device_family;
+       __le32 hw_step;
+       u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ];
+       u8 dev_human_readable[64];
+       u8 bus_human_readable[8];
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_fw_mon - FW monitor data
+ * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
+ * @fw_mon_base_ptr: base pointer of the data
+ * @fw_mon_cycle_cnt: number of wrap arounds
+ * @reserved: for future use
+ * @data: captured data
+ */
+struct iwl_fw_error_dump_fw_mon {
+       __le32 fw_mon_wr_ptr;
+       __le32 fw_mon_base_ptr;
+       __le32 fw_mon_cycle_cnt;
+       __le32 reserved[3];
+       u8 data[];
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_prph - periphery registers data
+ * @prph_start: address of the first register in this chunk
+ * @data: the content of the registers
+ */
+struct iwl_fw_error_dump_prph {
+       __le32 prph_start;
+       __le32 data[];
+};
+
 /**
- * iwl_mvm_fw_error_next_data - advance fw error dump data pointer
+ * iwl_fw_error_next_data - advance fw error dump data pointer
  * @data: previous data block
  * Returns: next data block
  */
 static inline struct iwl_fw_error_dump_data *
-iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data)
+iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
 {
        return (void *)(data->data + le32_to_cpu(data->len));
 }
index b45e576a4b57feb01982edb99191a65a3ae38bca..929a8063354ca43e456b9f46fbc9d4e51c22a8a1 100644 (file)
@@ -128,6 +128,7 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_CSCHEME           = 28,
        IWL_UCODE_TLV_API_CHANGES_SET   = 29,
        IWL_UCODE_TLV_ENABLED_CAPABILITIES      = 30,
+       IWL_UCODE_TLV_N_SCAN_CHANNELS           = 31,
 };
 
 struct iwl_ucode_tlv {
@@ -136,7 +137,8 @@ struct iwl_ucode_tlv {
        u8 data[0];
 };
 
-#define IWL_TLV_UCODE_MAGIC    0x0a4c5749
+#define IWL_TLV_UCODE_MAGIC            0x0a4c5749
+#define FW_VER_HUMAN_READABLE_SZ       64
 
 struct iwl_tlv_ucode_header {
        /*
@@ -147,7 +149,7 @@ struct iwl_tlv_ucode_header {
         */
        __le32 zero;
        __le32 magic;
-       u8 human_readable[64];
+       u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
        __le32 ver;             /* major/minor/API/serial */
        __le32 build;
        __le64 ignore;
index b1a33322b9bac9534e02a65c8e431d387f509542..1bb5193c5b1b5097c11bbf5a986f4c4d6e409c58 100644 (file)
@@ -65,6 +65,8 @@
 #include <linux/types.h>
 #include <net/mac80211.h>
 
+#include "iwl-fw-file.h"
+
 /**
  * enum iwl_ucode_tlv_flag - ucode API flags
  * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
@@ -118,11 +120,19 @@ enum iwl_ucode_tlv_flag {
 /**
  * enum iwl_ucode_tlv_api - ucode api
  * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
+ * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
+ * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
  * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
+ * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
+ * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
  */
 enum iwl_ucode_tlv_api {
        IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID     = BIT(0),
+       IWL_UCODE_TLV_CAPA_EXTENDED_BEACON      = BIT(1),
+       IWL_UCODE_TLV_API_BT_COEX_SPLIT         = BIT(3),
        IWL_UCODE_TLV_API_CSA_FLOW              = BIT(4),
+       IWL_UCODE_TLV_API_DISABLE_STA_TX        = BIT(5),
+       IWL_UCODE_TLV_API_LMAC_SCAN             = BIT(6),
 };
 
 /**
@@ -179,6 +189,7 @@ enum iwl_ucode_sec {
 
 struct iwl_ucode_capabilities {
        u32 max_probe_length;
+       u32 n_scan_channels;
        u32 standard_phy_calibration_size;
        u32 flags;
        u32 api[IWL_API_ARRAY_SIZE];
@@ -312,6 +323,7 @@ struct iwl_fw {
        bool mvm_fw;
 
        struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
+       u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
 };
 
 #endif  /* __iwl_fw_h__ */
index d051857729ab8e2f238115165c482d09033147db..71507cf490e6c2bb18ba666f45e3dc052cee8634 100644 (file)
@@ -99,10 +99,11 @@ enum iwl_disable_11n {
  * @wd_disable: disable stuck queue check, default = 1
  * @bt_coex_active: enable bt coex, default = true
  * @led_mode: system default, default = 0
- * @power_save: disable power save, default = false
+ * @power_save: enable power save, default = false
  * @power_level: power level, default = 1
  * @debug_level: levels are IWL_DL_*
  * @ant_coupling: antenna coupling in dB, default = 0
+ * @fw_monitor: allow to use firmware monitor
  */
 struct iwl_mod_params {
        int sw_crypto;
@@ -120,6 +121,7 @@ struct iwl_mod_params {
        int ant_coupling;
        char *nvm_file;
        bool uapsd_disable;
+       bool fw_monitor;
 };
 
 #endif /* #__iwl_modparams_h__ */
index 85eee79c495c8f1080d9844a3f71ffad1b0d023d..018af2957d3b7b319c47222eae4d1dfe1cdc3891 100644 (file)
@@ -63,6 +63,7 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/etherdevice.h>
+#include <linux/pci.h>
 #include "iwl-drv.h"
 #include "iwl-modparams.h"
 #include "iwl-nvm-parse.h"
@@ -87,8 +88,10 @@ enum wkp_nvm_offsets {
 
 enum family_8000_nvm_offsets {
        /* NVM HW-Section offset (in words) definitions */
-       HW_ADDR0_FAMILY_8000 = 0x12,
-       HW_ADDR1_FAMILY_8000 = 0x16,
+       HW_ADDR0_WFPM_FAMILY_8000 = 0x12,
+       HW_ADDR1_WFPM_FAMILY_8000 = 0x16,
+       HW_ADDR0_PCIE_FAMILY_8000 = 0x8A,
+       HW_ADDR1_PCIE_FAMILY_8000 = 0x8E,
        MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1,
 
        /* NVM SW-Section offset (in words) definitions */
@@ -174,7 +177,9 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = {
  * @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_INDOOR_ONLY: only indoor use is allowed
+ * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS
+ *     on same channel on 2.4 or same UNII band on 5.2
  * @NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
  * @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
  * @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
@@ -185,7 +190,8 @@ enum iwl_nvm_channel_flags {
        NVM_CHANNEL_IBSS = BIT(1),
        NVM_CHANNEL_ACTIVE = BIT(3),
        NVM_CHANNEL_RADAR = BIT(4),
-       NVM_CHANNEL_DFS = BIT(7),
+       NVM_CHANNEL_INDOOR_ONLY = BIT(5),
+       NVM_CHANNEL_GO_CONCURRENT = BIT(6),
        NVM_CHANNEL_WIDE = BIT(8),
        NVM_CHANNEL_40MHZ = BIT(9),
        NVM_CHANNEL_80MHZ = BIT(10),
@@ -273,6 +279,16 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                if (ch_flags & NVM_CHANNEL_RADAR)
                        channel->flags |= IEEE80211_CHAN_RADAR;
 
+               if (ch_flags & NVM_CHANNEL_INDOOR_ONLY)
+                       channel->flags |= IEEE80211_CHAN_INDOOR_ONLY;
+
+               /* Set the GO concurrent flag only in case that NO_IR is set.
+                * Otherwise it is meaningless
+                */
+               if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) &&
+                   (channel->flags & IEEE80211_CHAN_NO_IR))
+                       channel->flags |= IEEE80211_CHAN_GO_CONCURRENT;
+
                /* Initialize regulatory-based run-time data */
 
                /*
@@ -282,7 +298,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                channel->max_power = DEFAULT_MAX_TX_POWER;
                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",
+                                "Ch. %d [%sGHz] %s%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),
@@ -290,7 +306,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
                                 CHECK_AND_PRINT_I(ACTIVE),
                                 CHECK_AND_PRINT_I(RADAR),
                                 CHECK_AND_PRINT_I(WIDE),
-                                CHECK_AND_PRINT_I(DFS),
+                                CHECK_AND_PRINT_I(INDOOR_ONLY),
+                                CHECK_AND_PRINT_I(GO_CONCURRENT),
                                 ch_flags,
                                 channel->max_power,
                                 ((ch_flags & NVM_CHANNEL_IBSS) &&
@@ -462,7 +479,8 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
        data->hw_addr[5] = hw_addr[4];
 }
 
-static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
+static void iwl_set_hw_address_family_8000(struct device *dev,
+                                          const struct iwl_cfg *cfg,
                                           struct iwl_nvm_data *data,
                                           const __le16 *mac_override,
                                           const __le16 *nvm_hw)
@@ -481,20 +499,64 @@ static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
                data->hw_addr[4] = hw_addr[5];
                data->hw_addr[5] = hw_addr[4];
 
-               if (is_valid_ether_addr(hw_addr))
+               if (is_valid_ether_addr(data->hw_addr))
                        return;
+
+               IWL_ERR_DEV(dev,
+                           "mac address from nvm override section is not valid\n");
        }
 
-       /* take the MAC address from the OTP */
-       hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000);
-       data->hw_addr[0] = hw_addr[3];
-       data->hw_addr[1] = hw_addr[2];
-       data->hw_addr[2] = hw_addr[1];
-       data->hw_addr[3] = hw_addr[0];
+       if (nvm_hw) {
+               /* read the MAC address from OTP */
+               if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) {
+                       /* read the mac address from the WFPM location */
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR0_WFPM_FAMILY_8000);
+                       data->hw_addr[0] = hw_addr[3];
+                       data->hw_addr[1] = hw_addr[2];
+                       data->hw_addr[2] = hw_addr[1];
+                       data->hw_addr[3] = hw_addr[0];
+
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR1_WFPM_FAMILY_8000);
+                       data->hw_addr[4] = hw_addr[1];
+                       data->hw_addr[5] = hw_addr[0];
+               } else if ((data->nvm_version >= 0xE08) &&
+                          (data->nvm_version < 0xE0B)) {
+                       /* read "reverse order"  from the PCIe location */
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR0_PCIE_FAMILY_8000);
+                       data->hw_addr[5] = hw_addr[2];
+                       data->hw_addr[4] = hw_addr[1];
+                       data->hw_addr[3] = hw_addr[0];
+
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR1_PCIE_FAMILY_8000);
+                       data->hw_addr[2] = hw_addr[3];
+                       data->hw_addr[1] = hw_addr[2];
+                       data->hw_addr[0] = hw_addr[1];
+               } else {
+                       /* read from the PCIe location */
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR0_PCIE_FAMILY_8000);
+                       data->hw_addr[5] = hw_addr[0];
+                       data->hw_addr[4] = hw_addr[1];
+                       data->hw_addr[3] = hw_addr[2];
+
+                       hw_addr = (const u8 *)(nvm_hw +
+                                              HW_ADDR1_PCIE_FAMILY_8000);
+                       data->hw_addr[2] = hw_addr[1];
+                       data->hw_addr[1] = hw_addr[2];
+                       data->hw_addr[0] = hw_addr[3];
+               }
+               if (!is_valid_ether_addr(data->hw_addr))
+                       IWL_ERR_DEV(dev,
+                                   "mac address from hw section is not valid\n");
+
+               return;
+       }
 
-       hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000);
-       data->hw_addr[4] = hw_addr[1];
-       data->hw_addr[5] = hw_addr[0];
+       IWL_ERR_DEV(dev, "mac address is not found\n");
 }
 
 struct iwl_nvm_data *
@@ -556,7 +618,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
                                rx_chains);
        } else {
                /* MAC address in family 8000 */
-               iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw);
+               iwl_set_hw_address_family_8000(dev, cfg, data, mac_override,
+                                              nvm_hw);
 
                iwl_init_sbands(dev, cfg, data, regulatory,
                                sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
index 4997e27672b3ae22b4176df391a5bbb3ae9e1f4d..47033a35a40293f100312ede34bfc3a5b43c9efd 100644 (file)
@@ -359,4 +359,10 @@ enum secure_load_status_reg {
 #define RXF_LD_FENCE_OFFSET_ADDR       (0xa00c10)
 #define RXF_FIFO_RD_FENCE_ADDR         (0xa00c0c)
 
+/* FW monitor */
+#define MON_BUFF_BASE_ADDR             (0xa03c3c)
+#define MON_BUFF_END_ADDR              (0xa03c40)
+#define MON_BUFF_WRPTR                 (0xa03c44)
+#define MON_BUFF_CYCLE_CNT             (0xa03c48)
+
 #endif                         /* __iwl_prph_h__ */
index 34d49e171fb4dae1f10928c931c26dc93a414e67..656371a668daa5e2325e45fe0b576fc3e13bd307 100644 (file)
@@ -394,6 +394,11 @@ struct iwl_trans_config {
        const char *const *command_names;
 };
 
+struct iwl_trans_dump_data {
+       u32 len;
+       u8 data[];
+};
+
 struct iwl_trans;
 
 /**
@@ -461,10 +466,8 @@ struct iwl_trans;
  * @unref: release a reference previously taken with @ref. Note that
  *     initially the reference count is 1, making an initial @unref
  *     necessary to allow low power states.
- * @dump_data: fill a data dump with debug data, maybe containing last
- *     TX'ed commands and similar. When called with a NULL buffer and
- *     zero buffer length, provide only the (estimated) required buffer
- *     length. Return the used buffer length.
+ * @dump_data: return a vmalloc'ed buffer with debug data, maybe containing last
+ *     TX'ed commands and similar. The buffer will be vfree'd by the caller.
  *     Note that the transport must fill in the proper file headers.
  */
 struct iwl_trans_ops {
@@ -518,7 +521,7 @@ struct iwl_trans_ops {
        void (*unref)(struct iwl_trans *trans);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-       u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen);
+       struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans);
 #endif
 };
 
@@ -685,12 +688,12 @@ static inline void iwl_trans_unref(struct iwl_trans *trans)
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-static inline u32 iwl_trans_dump_data(struct iwl_trans *trans,
-                                     void *buf, u32 buflen)
+static inline struct iwl_trans_dump_data *
+iwl_trans_dump_data(struct iwl_trans *trans)
 {
        if (!trans->ops->dump_data)
-               return 0;
-       return trans->ops->dump_data(trans, buf, buflen);
+               return NULL;
+       return trans->ops->dump_data(trans);
 }
 #endif
 
index c30d7f64ec1e4e1c47a635e091c4e503c2ceaa83..a28235913c2cf94c5b07b9c0e3dc3c8768a7d990 100644 (file)
@@ -2,7 +2,7 @@ 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 sf.o
 iwlmvm-y += scan.o time-event.o rs.o
-iwlmvm-y += power.o coex.o
+iwlmvm-y += power.o coex.o coex_legacy.o
 iwlmvm-y += tt.o offloading.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
 iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
index c8c3b38228f02f9768b780a7bdd31273f49e9541..2291bbcaaeab8bfe57c3aa20f9409bab41ba4cef 100644 (file)
 #include "mvm.h"
 #include "iwl-debug.h"
 
-#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant)                 \
-       [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) |    \
-                  ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
-
-static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
-                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
-                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
-                      BT_COEX_PRIO_TBL_PRIO_LOW, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
-                      BT_COEX_PRIO_TBL_PRIO_LOW, 1),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
-                      BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
-                      BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
-                      BT_COEX_PRIO_TBL_DISABLED, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
-                      BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
-                      BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
-       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
-                      BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
-       0, 0, 0, 0, 0, 0,
-};
-
-#undef EVENT_PRIO_ANT
-
-#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD    (-62)
-#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD   (-65)
 #define BT_ANTENNA_COUPLING_THRESHOLD          (30)
 
-static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
-{
-       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
-                                   sizeof(struct iwl_bt_coex_prio_tbl_cmd),
-                                   &iwl_bt_prio_tbl);
-}
+const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
+       [BT_KILL_MSK_DEFAULT] = 0xfffffc00,
+       [BT_KILL_MSK_NEVER] = 0xffffffff,
+       [BT_KILL_MSK_ALWAYS] = 0,
+};
 
-const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
-       [BT_KILL_MSK_DEFAULT] = 0xffff0000,
-       [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
-       [BT_KILL_MSK_REDUCED_TXPOW] = 0,
+const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
+       {
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_ALWAYS,
+       },
+       {
+               BT_KILL_MSK_NEVER,
+               BT_KILL_MSK_NEVER,
+               BT_KILL_MSK_NEVER,
+       },
+       {
+               BT_KILL_MSK_NEVER,
+               BT_KILL_MSK_NEVER,
+               BT_KILL_MSK_NEVER,
+       },
+       {
+               BT_KILL_MSK_DEFAULT,
+               BT_KILL_MSK_NEVER,
+               BT_KILL_MSK_DEFAULT,
+       },
 };
 
-const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = {
-       [BT_KILL_MSK_DEFAULT] = 0xffff0000,
-       [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
-       [BT_KILL_MSK_REDUCED_TXPOW] = 0,
+const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT] = {
+       {
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_ALWAYS,
+       },
+       {
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_ALWAYS,
+       },
+       {
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_ALWAYS,
+       },
+       {
+               BT_KILL_MSK_DEFAULT,
+               BT_KILL_MSK_ALWAYS,
+               BT_KILL_MSK_DEFAULT,
+       },
 };
 
 static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
@@ -519,6 +520,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum iwl_bt_coex_lut_type ret;
        u16 phy_ctx_id;
+       u32 primary_ch_phy_id, secondary_ch_phy_id;
 
        /*
         * Checking that we hold mvm->mutex is a good idea, but the rate
@@ -535,7 +537,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
        if (!chanctx_conf ||
             chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
                rcu_read_unlock();
-               return BT_COEX_LOOSE_LUT;
+               return BT_COEX_INVALID_LUT;
        }
 
        ret = BT_COEX_TX_DIS_LUT;
@@ -546,10 +548,13 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
        }
 
        phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+       primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id);
+       secondary_ch_phy_id =
+               le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id);
 
-       if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id)
+       if (primary_ch_phy_id == phy_ctx_id)
                ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
-       else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id)
+       else if (secondary_ch_phy_id == phy_ctx_id)
                ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
        /* else - default = TX TX disallowed */
 
@@ -567,59 +572,63 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                .dataflags = { IWL_HCMD_DFL_NOCOPY, },
        };
        int ret;
-       u32 flags;
+       u32 mode;
 
-       ret = iwl_send_bt_prio_tbl(mvm);
-       if (ret)
-               return ret;
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_send_bt_init_conf_old(mvm);
 
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
        if (!bt_cmd)
                return -ENOMEM;
        cmd.data[0] = bt_cmd;
 
-       bt_cmd->max_kill = 5;
-       bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
-       bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
-       bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
-       bt_cmd->bt4_tx_rx_max_freq0 = 15;
-       bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
-       bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
-
-       flags = iwlwifi_mod_params.bt_coex_active ?
-                       BT_COEX_NW : BT_COEX_DISABLE;
-       bt_cmd->flags = cpu_to_le32(flags);
-
-       bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
-                                           BT_VALID_BT_PRIO_BOOST |
-                                           BT_VALID_MAX_KILL |
-                                           BT_VALID_3W_TMRS |
-                                           BT_VALID_KILL_ACK |
-                                           BT_VALID_KILL_CTS |
-                                           BT_VALID_REDUCED_TX_POWER |
-                                           BT_VALID_LUT |
-                                           BT_VALID_WIFI_RX_SW_PRIO_BOOST |
-                                           BT_VALID_WIFI_TX_SW_PRIO_BOOST |
-                                           BT_VALID_ANT_ISOLATION |
-                                           BT_VALID_ANT_ISOLATION_THRS |
-                                           BT_VALID_TXTX_DELTA_FREQ_THRS |
-                                           BT_VALID_TXRX_MAX_FREQ_0 |
-                                           BT_VALID_SYNC_TO_SCO);
+       lockdep_assert_held(&mvm->mutex);
 
-       if (IWL_MVM_BT_COEX_SYNC2SCO)
-               bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
+               u32 mode;
 
-       if (IWL_MVM_BT_COEX_CORUNNING) {
-               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
-                                                    BT_VALID_CORUN_LUT_40);
-               bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
+               switch (mvm->bt_force_ant_mode) {
+               case BT_FORCE_ANT_BT:
+                       mode = BT_COEX_BT;
+                       break;
+               case BT_FORCE_ANT_WIFI:
+                       mode = BT_COEX_WIFI;
+                       break;
+               default:
+                       WARN_ON(1);
+                       mode = 0;
+               }
+
+               bt_cmd->mode = cpu_to_le32(mode);
+               goto send_cmd;
        }
 
+       bt_cmd->max_kill = cpu_to_le32(5);
+       bt_cmd->bt4_antenna_isolation_thr =
+                               cpu_to_le32(BT_ANTENNA_COUPLING_THRESHOLD);
+       bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15);
+       bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15);
+       bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
+       bt_cmd->override_secondary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
+
+       mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE;
+       bt_cmd->mode = cpu_to_le32(mode);
+
+       if (IWL_MVM_BT_COEX_SYNC2SCO)
+               bt_cmd->enabled_modules |=
+                       cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
+
+       if (IWL_MVM_BT_COEX_CORUNNING)
+               bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
+
        if (IWL_MVM_BT_COEX_MPLUT) {
-               bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
-               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+               bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);
+               bt_cmd->enabled_modules |=
+                       cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED);
        }
 
+       bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
+
        if (mvm->cfg->bt_shared_single_ant)
                memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
                       sizeof(iwl_single_shared_ant));
@@ -627,21 +636,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
                memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
                       sizeof(iwl_combined_lookup));
 
-       /* Take first Co-running block LUT to get started */
-       memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
-              sizeof(bt_cmd->bt4_corun_lut20));
-       memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
-              sizeof(bt_cmd->bt4_corun_lut40));
-
-       memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+       memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost,
               sizeof(iwl_bt_prio_boost));
-       memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
+       memcpy(&bt_cmd->multiprio_lut, iwl_bt_mprio_lut,
               sizeof(iwl_bt_mprio_lut));
-       bt_cmd->kill_ack_msk =
-               cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
-       bt_cmd->kill_cts_msk =
-               cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
 
+send_cmd:
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
        memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
 
@@ -651,82 +651,54 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
        return ret;
 }
 
-static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
-                                          bool reduced_tx_power)
+static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm)
 {
-       enum iwl_bt_kill_msk bt_kill_msk;
-       struct iwl_bt_coex_cmd *bt_cmd;
        struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
-       struct iwl_host_cmd cmd = {
-               .id = BT_CONFIG,
-               .data[0] = &bt_cmd,
-               .len = { sizeof(*bt_cmd), },
-               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-       };
-       int ret = 0;
+       u32 primary_lut = le32_to_cpu(notif->primary_ch_lut);
+       u32 secondary_lut = le32_to_cpu(notif->secondary_ch_lut);
+       u32 ag = le32_to_cpu(notif->bt_activity_grading);
+       struct iwl_bt_coex_sw_boost_update_cmd cmd = {};
+       u8 ack_kill_msk[NUM_PHY_CTX] = {};
+       u8 cts_kill_msk[NUM_PHY_CTX] = {};
+       int i;
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (reduced_tx_power) {
-               /* Reduced Tx power has precedence on the type of the profile */
-               bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
-       } else {
-               /* Low latency BT profile is active: give higher prio to BT */
-               if (BT_MBOX_MSG(notif, 3, SCO_STATE)  ||
-                   BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
-                   BT_MBOX_MSG(notif, 3, SNIFF_STATE))
-                       bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
-               else
-                       bt_kill_msk = BT_KILL_MSK_DEFAULT;
-       }
+       ack_kill_msk[0] = iwl_bt_ack_kill_msk[ag][primary_lut];
+       cts_kill_msk[0] = iwl_bt_cts_kill_msk[ag][primary_lut];
 
-       IWL_DEBUG_COEX(mvm,
-                      "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
-                      bt_kill_msk,
-                      BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
-                      BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
-                      BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
+       ack_kill_msk[1] = iwl_bt_ack_kill_msk[ag][secondary_lut];
+       cts_kill_msk[1] = iwl_bt_cts_kill_msk[ag][secondary_lut];
 
        /* Don't send HCMD if there is no update */
-       if (bt_kill_msk == mvm->bt_kill_msk)
+       if (!memcmp(ack_kill_msk, mvm->bt_ack_kill_msk, sizeof(ack_kill_msk)) ||
+           !memcmp(cts_kill_msk, mvm->bt_cts_kill_msk, sizeof(cts_kill_msk)))
                return 0;
 
-       mvm->bt_kill_msk = bt_kill_msk;
-
-       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
-       if (!bt_cmd)
-               return -ENOMEM;
-       cmd.data[0] = bt_cmd;
-       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
-
-       bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
-       bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
-       bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
-                                            BT_VALID_KILL_ACK |
-                                            BT_VALID_KILL_CTS);
+       memcpy(mvm->bt_ack_kill_msk, ack_kill_msk,
+              sizeof(mvm->bt_ack_kill_msk));
+       memcpy(mvm->bt_cts_kill_msk, cts_kill_msk,
+              sizeof(mvm->bt_cts_kill_msk));
 
-       IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
-                      iwl_bt_ack_kill_msk[bt_kill_msk],
-                      iwl_bt_cts_kill_msk[bt_kill_msk]);
+       BUILD_BUG_ON(ARRAY_SIZE(ack_kill_msk) < ARRAY_SIZE(cmd.boost_values));
 
-       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       for (i = 0; i < ARRAY_SIZE(cmd.boost_values); i++) {
+               cmd.boost_values[i].kill_ack_msk =
+                       cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk[i]]);
+               cmd.boost_values[i].kill_cts_msk =
+                       cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk[i]]);
+       }
 
-       kfree(bt_cmd);
-       return ret;
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0,
+                                   sizeof(cmd), &cmd);
 }
 
 static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
                                       bool enable)
 {
-       struct iwl_bt_coex_cmd *bt_cmd;
-       /* Send ASYNC since this can be sent from an atomic context */
-       struct iwl_host_cmd cmd = {
-               .id = BT_CONFIG,
-               .len = { sizeof(*bt_cmd), },
-               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-               .flags = CMD_ASYNC,
-       };
+       struct iwl_bt_coex_reduced_txp_update_cmd cmd = {};
        struct iwl_mvm_sta *mvmsta;
+       u32 value;
        int ret;
 
        mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
@@ -737,35 +709,26 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
        if (mvmsta->bt_reduced_txpower == enable)
                return 0;
 
-       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
-       if (!bt_cmd)
-               return -ENOMEM;
-       cmd.data[0] = bt_cmd;
-       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
-
-       bt_cmd->valid_bit_msk =
-               cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
-       bt_cmd->bt_reduced_tx_power = sta_id;
+       value = mvmsta->sta_id;
 
        if (enable)
-               bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+               value |= BT_REDUCED_TX_POWER_BIT;
 
        IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
                       enable ? "en" : "dis", sta_id);
 
+       cmd.reduced_txp = cpu_to_le32(value);
        mvmsta->bt_reduced_txpower = enable;
 
-       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC,
+                                  sizeof(cmd), &cmd);
 
-       kfree(bt_cmd);
        return ret;
 }
 
 struct iwl_bt_iterator_data {
        struct iwl_bt_coex_profile_notif *notif;
        struct iwl_mvm *mvm;
-       u32 num_bss_ifaces;
-       bool reduced_tx_power;
        struct ieee80211_chanctx_conf *primary;
        struct ieee80211_chanctx_conf *secondary;
        bool primary_ll;
@@ -780,9 +743,9 @@ void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
 
        mvmvif->bf_data.last_bt_coex_event = rssi;
        mvmvif->bf_data.bt_coex_max_thold =
-               enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0;
+               enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
        mvmvif->bf_data.bt_coex_min_thold =
-               enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0;
+               enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
 }
 
 /* must be called under rcu_read_lock */
@@ -801,22 +764,12 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               /* Count BSSes vifs */
-               data->num_bss_ifaces++;
                /* default smps_mode for BSS / P2P client is AUTOMATIC */
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
                break;
        case NL80211_IFTYPE_AP:
-               /* default smps_mode for AP / GO is OFF */
-               smps_mode = IEEE80211_SMPS_OFF;
-               if (!mvmvif->ap_ibss_active) {
-                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
-                                           smps_mode);
+               if (!mvmvif->ap_ibss_active)
                        return;
-               }
-
-               /* the Ack / Cts kill mask must be default if AP / GO */
-               data->reduced_tx_power = false;
                break;
        default:
                return;
@@ -827,11 +780,10 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        /* If channel context is invalid or not on 2.4GHz .. */
        if ((!chanctx_conf ||
             chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
-               /* ... relax constraints and disable rssi events */
-               iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
-                                   smps_mode);
-               data->reduced_tx_power = false;
                if (vif->type == NL80211_IFTYPE_STATION) {
+                       /* ... relax constraints and disable rssi events */
+                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                           smps_mode);
                        iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
                                                    false);
                        iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
@@ -843,20 +795,23 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        if (bt_activity_grading >= BT_HIGH_TRAFFIC)
                smps_mode = IEEE80211_SMPS_STATIC;
        else if (bt_activity_grading >= BT_LOW_TRAFFIC)
-               smps_mode = vif->type == NL80211_IFTYPE_AP ?
-                               IEEE80211_SMPS_OFF :
-                               IEEE80211_SMPS_DYNAMIC;
+               smps_mode = IEEE80211_SMPS_DYNAMIC;
 
        /* relax SMPS contraints for next association */
        if (!vif->bss_conf.assoc)
                smps_mode = IEEE80211_SMPS_AUTOMATIC;
 
+       if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
+                              mvmvif->phy_ctxt->id))
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
        IWL_DEBUG_COEX(data->mvm,
-                      "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
-                      mvmvif->id, data->notif->bt_status, bt_activity_grading,
-                      smps_mode);
+                      "mac %d: bt_activity_grading %d smps_req %d\n",
+                      mvmvif->id, bt_activity_grading, smps_mode);
 
-       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
+       if (vif->type == NL80211_IFTYPE_STATION)
+               iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                   smps_mode);
 
        /* low latency is always primary */
        if (iwl_mvm_vif_low_latency(mvmvif)) {
@@ -906,8 +861,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
         */
        if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
            mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
-           !data->notif->bt_status) {
-               data->reduced_tx_power = false;
+           le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
                iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
                iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
@@ -919,26 +873,12 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        /* if the RSSI isn't valid, fake it is very low */
        if (!ave_rssi)
                ave_rssi = -100;
-       if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) {
+       if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
                if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
                        IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
-
-               /*
-                * bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
-                * BSS / P2P clients have rssi above threshold.
-                * We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
-                * the iteration, if one interface's rssi isn't good enough,
-                * bt_kill_msk will be set to default values.
-                */
-       } else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) {
+       } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
                if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
                        IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
-
-               /*
-                * One interface hasn't rssi above threshold, bt_kill_msk must
-                * be set to default values.
-                */
-               data->reduced_tx_power = false;
        }
 
        /* Begin to monitor the RSSI: it may influence the reduced Tx power */
@@ -950,11 +890,14 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
        struct iwl_bt_iterator_data data = {
                .mvm = mvm,
                .notif = &mvm->last_bt_notif,
-               .reduced_tx_power = true,
        };
        struct iwl_bt_coex_ci_cmd cmd = {};
        u8 ci_bw_idx;
 
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return;
+
        rcu_read_lock();
        ieee80211_iterate_active_interfaces_atomic(
                                        mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
@@ -969,9 +912,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                if (chan->def.width < NL80211_CHAN_WIDTH_40) {
                        ci_bw_idx = 0;
-                       cmd.co_run_bw_primary = 0;
                } else {
-                       cmd.co_run_bw_primary = 1;
                        if (chan->def.center_freq1 >
                            chan->def.chan->center_freq)
                                ci_bw_idx = 2;
@@ -981,7 +922,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                cmd.bt_primary_ci =
                        iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
-               cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+               cmd.primary_ch_phy_id =
+                       cpu_to_le32(*((u16 *)data.primary->drv_priv));
        }
 
        if (data.secondary) {
@@ -993,9 +935,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                if (chan->def.width < NL80211_CHAN_WIDTH_40) {
                        ci_bw_idx = 0;
-                       cmd.co_run_bw_secondary = 0;
                } else {
-                       cmd.co_run_bw_secondary = 1;
                        if (chan->def.center_freq1 >
                            chan->def.chan->center_freq)
                                ci_bw_idx = 2;
@@ -1005,7 +945,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
 
                cmd.bt_secondary_ci =
                        iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
-               cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
+               cmd.secondary_ch_phy_id =
+                       cpu_to_le32(*((u16 *)data.secondary->drv_priv));
        }
 
        rcu_read_unlock();
@@ -1018,14 +959,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
                memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd));
        }
 
-       /*
-        * If there are no BSS / P2P client interfaces, reduced Tx Power is
-        * irrelevant since it is based on the RSSI coming from the beacon.
-        * Use BT_KILL_MSK_DEFAULT in that case.
-        */
-       data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
-
-       if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+       if (iwl_mvm_bt_udpate_sw_boost(mvm))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
@@ -1036,11 +970,10 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
 
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_rx_bt_coex_notif_old(mvm, rxb, dev_cmd);
 
        IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
-       IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
-                      notif->bt_status ? "ON" : "OFF");
-       IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
        IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
        IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
                       le32_to_cpu(notif->primary_ch_lut));
@@ -1048,8 +981,6 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
                       le32_to_cpu(notif->secondary_ch_lut));
        IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
                       le32_to_cpu(notif->bt_activity_grading));
-       IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
-                      notif->bt_agg_traffic_load);
 
        /* remember this notification for future use: rssi fluctuations */
        memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
@@ -1097,16 +1028,6 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
                return;
 
        mvmsta = iwl_mvm_sta_from_mac80211(sta);
-
-       data->num_bss_ifaces++;
-
-       /*
-        * This interface doesn't support reduced Tx power (because of low
-        * RSSI probably), then set bt_kill_msk to default values.
-        */
-       if (!mvmsta->bt_reduced_txpower)
-               data->reduced_tx_power = false;
-       /* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
 }
 
 void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -1115,12 +1036,20 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
        struct iwl_bt_iterator_data data = {
                .mvm = mvm,
-               .reduced_tx_power = true,
        };
        int ret;
 
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+               iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event);
+               return;
+       }
+
        lockdep_assert_held(&mvm->mutex);
 
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return;
+
        /*
         * Rssi update while not associated - can happen since the statistics
         * are handled asynchronously
@@ -1129,7 +1058,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                return;
 
        /* No BT - reports should be disabled */
-       if (!mvm->last_bt_notif.bt_status)
+       if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF)
                return;
 
        IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
@@ -1153,14 +1082,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
                iwl_mvm_bt_rssi_iterator, &data);
 
-       /*
-        * If there are no BSS / P2P client interfaces, reduced Tx Power is
-        * irrelevant since it is based on the RSSI coming from the beacon.
-        * Use BT_KILL_MSK_DEFAULT in that case.
-        */
-       data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
-
-       if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+       if (iwl_mvm_bt_udpate_sw_boost(mvm))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
@@ -1171,15 +1093,23 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
        enum iwl_bt_coex_lut_type lut_type;
 
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
+
+       if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
        if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
            BT_HIGH_TRAFFIC)
                return LINK_QUAL_AGG_TIME_LIMIT_DEF;
 
        lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
 
-       if (lut_type == BT_COEX_LOOSE_LUT)
+       if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
                return LINK_QUAL_AGG_TIME_LIMIT_DEF;
 
        /* tight coex, high bt traffic, reduce AGG time limit */
@@ -1190,18 +1120,37 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
                                     struct ieee80211_sta *sta)
 {
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+       struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
+       enum iwl_bt_coex_lut_type lut_type;
+
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_bt_coex_is_mimo_allowed_old(mvm, sta);
+
+       if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
+               return true;
 
        if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
            BT_HIGH_TRAFFIC)
                return true;
 
        /*
-        * In Tight, BT can't Rx while we Tx, so use both antennas since BT is
-        * already killed.
-        * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we
-        * Tx.
+        * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
+        * since BT is already killed.
+        * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
+        * we Tx.
+        * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
         */
-       return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
+       lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+       return lut_type != BT_COEX_LOOSE_LUT;
+}
+
+bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
+{
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
+
+       return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF;
 }
 
 bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
@@ -1209,6 +1158,9 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
 {
        u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
 
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band);
+
        if (band != IEEE80211_BAND_2GHZ)
                return false;
 
@@ -1249,6 +1201,11 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
 
 void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
 {
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+               iwl_mvm_bt_coex_vif_change_old(mvm);
+               return;
+       }
+
        iwl_mvm_bt_coex_notif_handle(mvm);
 }
 
@@ -1258,22 +1215,22 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+       struct iwl_bt_coex_corun_lut_update_cmd cmd = {};
        u8 __maybe_unused lower_bound, upper_bound;
-       int ret;
        u8 lut;
 
-       struct iwl_bt_coex_cmd *bt_cmd;
-       struct iwl_host_cmd cmd = {
-               .id = BT_CONFIG,
-               .len = { sizeof(*bt_cmd), },
-               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
-       };
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+               return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd);
 
        if (!IWL_MVM_BT_COEX_CORUNNING)
                return 0;
 
        lockdep_assert_held(&mvm->mutex);
 
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return 0;
+
        if (ant_isolation ==  mvm->last_ant_isol)
                return 0;
 
@@ -1298,25 +1255,13 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
 
        mvm->last_corun_lut = lut;
 
-       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
-       if (!bt_cmd)
-               return 0;
-       cmd.data[0] = bt_cmd;
-
-       bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
-       bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
-                                            BT_VALID_CORUN_LUT_20 |
-                                            BT_VALID_CORUN_LUT_40);
-
        /* For the moment, use the same LUT for 20GHz and 40GHz */
-       memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
-              sizeof(bt_cmd->bt4_corun_lut20));
+       memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20,
+              sizeof(cmd.corun_lut20));
 
-       memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
-              sizeof(bt_cmd->bt4_corun_lut40));
+       memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20,
+              sizeof(cmd.corun_lut40));
 
-       ret = iwl_mvm_send_cmd(mvm, &cmd);
-
-       kfree(bt_cmd);
-       return ret;
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0,
+                                   sizeof(cmd), &cmd);
 }
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
new file mode 100644 (file)
index 0000000..a3be333
--- /dev/null
@@ -0,0 +1,1257 @@
+/******************************************************************************
+ *
+ * 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) 2013 - 2014 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 COPYING.
+ *
+ * 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) 2013 - 2014 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 <net/mac80211.h>
+
+#include "fw-api-coex.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-debug.h"
+
+#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant)                 \
+       [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) |    \
+                  ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
+
+static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
+                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
+                      BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
+                      BT_COEX_PRIO_TBL_PRIO_LOW, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
+                      BT_COEX_PRIO_TBL_PRIO_LOW, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
+                      BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
+                      BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
+                      BT_COEX_PRIO_TBL_DISABLED, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
+       EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
+                      BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
+       0, 0, 0, 0, 0, 0,
+};
+
+#undef EVENT_PRIO_ANT
+
+#define BT_ANTENNA_COUPLING_THRESHOLD          (30)
+
+static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
+{
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return 0;
+
+       return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
+                                   sizeof(struct iwl_bt_coex_prio_tbl_cmd),
+                                   &iwl_bt_prio_tbl);
+}
+
+static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
+       cpu_to_le32(0xf0f0f0f0), /* 50% */
+       cpu_to_le32(0xc0c0c0c0), /* 25% */
+       cpu_to_le32(0xfcfcfcfc), /* 75% */
+       cpu_to_le32(0xfefefefe), /* 87.5% */
+};
+
+static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x40000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x44000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+       },
+};
+
+static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+       {
+               /* Tight */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaeaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0x00004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               /* Loose */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x00000000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+       {
+               /* Tx Tx disabled */
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xeeaaaaaa),
+               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xcc00ff28),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xcc00aaaa),
+               cpu_to_le32(0x0000aaaa),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
+       },
+};
+
+/* 20MHz / 40MHz below / 40Mhz above*/
+static const __le64 iwl_ci_mask[][3] = {
+       /* dummy entry for channel 0 */
+       {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
+       {
+               cpu_to_le64(0x0000001FFFULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x00007FFFFFULL),
+       },
+       {
+               cpu_to_le64(0x000000FFFFULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x0003FFFFFFULL),
+       },
+       {
+               cpu_to_le64(0x000003FFFCULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x000FFFFFFCULL),
+       },
+       {
+               cpu_to_le64(0x00001FFFE0ULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x007FFFFFE0ULL),
+       },
+       {
+               cpu_to_le64(0x00007FFF80ULL),
+               cpu_to_le64(0x00007FFFFFULL),
+               cpu_to_le64(0x01FFFFFF80ULL),
+       },
+       {
+               cpu_to_le64(0x0003FFFC00ULL),
+               cpu_to_le64(0x0003FFFFFFULL),
+               cpu_to_le64(0x0FFFFFFC00ULL),
+       },
+       {
+               cpu_to_le64(0x000FFFF000ULL),
+               cpu_to_le64(0x000FFFFFFCULL),
+               cpu_to_le64(0x3FFFFFF000ULL),
+       },
+       {
+               cpu_to_le64(0x007FFF8000ULL),
+               cpu_to_le64(0x007FFFFFE0ULL),
+               cpu_to_le64(0xFFFFFF8000ULL),
+       },
+       {
+               cpu_to_le64(0x01FFFE0000ULL),
+               cpu_to_le64(0x01FFFFFF80ULL),
+               cpu_to_le64(0xFFFFFE0000ULL),
+       },
+       {
+               cpu_to_le64(0x0FFFF00000ULL),
+               cpu_to_le64(0x0FFFFFFC00ULL),
+               cpu_to_le64(0x0ULL),
+       },
+       {
+               cpu_to_le64(0x3FFFC00000ULL),
+               cpu_to_le64(0x3FFFFFF000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFFE000000ULL),
+               cpu_to_le64(0xFFFFFF8000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFF8000000ULL),
+               cpu_to_le64(0xFFFFFE0000ULL),
+               cpu_to_le64(0x0)
+       },
+       {
+               cpu_to_le64(0xFFC0000000ULL),
+               cpu_to_le64(0x0ULL),
+               cpu_to_le64(0x0ULL)
+       },
+};
+
+static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
+       cpu_to_le32(0x28412201),
+       cpu_to_le32(0x11118451),
+};
+
+struct corunning_block_luts {
+       u8 range;
+       __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
+};
+
+/*
+ * Ranges for the antenna coupling calibration / co-running block LUT:
+ *             LUT0: [ 0, 12[
+ *             LUT1: [12, 20[
+ *             LUT2: [20, 21[
+ *             LUT3: [21, 23[
+ *             LUT4: [23, 27[
+ *             LUT5: [27, 30[
+ *             LUT6: [30, 32[
+ *             LUT7: [32, 33[
+ *             LUT8: [33, - [
+ */
+static const struct corunning_block_luts antenna_coupling_ranges[] = {
+       {
+               .range = 0,
+               .lut20 = {
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 12,
+               .lut20 = {
+                       cpu_to_le32(0x00000001),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 20,
+               .lut20 = {
+                       cpu_to_le32(0x00000002),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 21,
+               .lut20 = {
+                       cpu_to_le32(0x00000003),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 23,
+               .lut20 = {
+                       cpu_to_le32(0x00000004),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 27,
+               .lut20 = {
+                       cpu_to_le32(0x00000005),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 30,
+               .lut20 = {
+                       cpu_to_le32(0x00000006),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 32,
+               .lut20 = {
+                       cpu_to_le32(0x00000007),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+       {
+               .range = 33,
+               .lut20 = {
+                       cpu_to_le32(0x00000008),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+                       cpu_to_le32(0x00000000),  cpu_to_le32(0x00000000),
+               },
+       },
+};
+
+static enum iwl_bt_coex_lut_type
+iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
+{
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum iwl_bt_coex_lut_type ret;
+       u16 phy_ctx_id;
+
+       /*
+        * Checking that we hold mvm->mutex is a good idea, but the rate
+        * control can't acquire the mutex since it runs in Tx path.
+        * So this is racy in that case, but in the worst case, the AMPDU
+        * size limit will be wrong for a short time which is not a big
+        * issue.
+        */
+
+       rcu_read_lock();
+
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+       if (!chanctx_conf ||
+           chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+               rcu_read_unlock();
+               return BT_COEX_INVALID_LUT;
+       }
+
+       ret = BT_COEX_TX_DIS_LUT;
+
+       if (mvm->cfg->bt_shared_single_ant) {
+               rcu_read_unlock();
+               return ret;
+       }
+
+       phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+
+       if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id)
+               ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut);
+       else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id)
+               ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut);
+       /* else - default = TX TX disallowed */
+
+       rcu_read_unlock();
+
+       return ret;
+}
+
+int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
+{
+       struct iwl_bt_coex_cmd_old *bt_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       int ret;
+       u32 flags;
+
+       ret = iwl_send_bt_prio_tbl(mvm);
+       if (ret)
+               return ret;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
+               switch (mvm->bt_force_ant_mode) {
+               case BT_FORCE_ANT_AUTO:
+                       flags = BT_COEX_AUTO_OLD;
+                       break;
+               case BT_FORCE_ANT_BT:
+                       flags = BT_COEX_BT_OLD;
+                       break;
+               case BT_FORCE_ANT_WIFI:
+                       flags = BT_COEX_WIFI_OLD;
+                       break;
+               default:
+                       WARN_ON(1);
+                       flags = 0;
+               }
+
+               bt_cmd->flags = cpu_to_le32(flags);
+               bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE);
+               goto send_cmd;
+       }
+
+       bt_cmd->max_kill = 5;
+       bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
+       bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
+       bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
+       bt_cmd->bt4_tx_rx_max_freq0 = 15;
+       bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
+       bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
+
+       flags = iwlwifi_mod_params.bt_coex_active ?
+                       BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD;
+       bt_cmd->flags = cpu_to_le32(flags);
+
+       bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
+                                           BT_VALID_BT_PRIO_BOOST |
+                                           BT_VALID_MAX_KILL |
+                                           BT_VALID_3W_TMRS |
+                                           BT_VALID_KILL_ACK |
+                                           BT_VALID_KILL_CTS |
+                                           BT_VALID_REDUCED_TX_POWER |
+                                           BT_VALID_LUT |
+                                           BT_VALID_WIFI_RX_SW_PRIO_BOOST |
+                                           BT_VALID_WIFI_TX_SW_PRIO_BOOST |
+                                           BT_VALID_ANT_ISOLATION |
+                                           BT_VALID_ANT_ISOLATION_THRS |
+                                           BT_VALID_TXTX_DELTA_FREQ_THRS |
+                                           BT_VALID_TXRX_MAX_FREQ_0 |
+                                           BT_VALID_SYNC_TO_SCO);
+
+       if (IWL_MVM_BT_COEX_SYNC2SCO)
+               bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
+
+       if (IWL_MVM_BT_COEX_CORUNNING) {
+               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
+                                                    BT_VALID_CORUN_LUT_40);
+               bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
+       }
+
+       if (IWL_MVM_BT_COEX_MPLUT) {
+               bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
+               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+       }
+
+       if (mvm->cfg->bt_shared_single_ant)
+               memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
+                      sizeof(iwl_single_shared_ant));
+       else
+               memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
+                      sizeof(iwl_combined_lookup));
+
+       /* Take first Co-running block LUT to get started */
+       memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
+              sizeof(bt_cmd->bt4_corun_lut20));
+       memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
+              sizeof(bt_cmd->bt4_corun_lut40));
+
+       memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+              sizeof(iwl_bt_prio_boost));
+       memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
+              sizeof(iwl_bt_mprio_lut));
+
+send_cmd:
+       memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
+       memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
+}
+
+static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm)
+{
+       struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old;
+       u32 primary_lut = le32_to_cpu(notif->primary_ch_lut);
+       u32 ag = le32_to_cpu(notif->bt_activity_grading);
+       struct iwl_bt_coex_cmd_old *bt_cmd;
+       u8 ack_kill_msk, cts_kill_msk;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .data[0] = &bt_cmd,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       int ret = 0;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ack_kill_msk = iwl_bt_ack_kill_msk[ag][primary_lut];
+       cts_kill_msk = iwl_bt_cts_kill_msk[ag][primary_lut];
+
+       if (mvm->bt_ack_kill_msk[0] == ack_kill_msk &&
+           mvm->bt_cts_kill_msk[0] == cts_kill_msk)
+               return 0;
+
+       mvm->bt_ack_kill_msk[0] = ack_kill_msk;
+       mvm->bt_cts_kill_msk[0] = cts_kill_msk;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+
+       bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[ack_kill_msk]);
+       bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_ctl_kill_msk[cts_kill_msk]);
+       bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+                                            BT_VALID_KILL_ACK |
+                                            BT_VALID_KILL_CTS);
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
+}
+
+static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
+                                      bool enable)
+{
+       struct iwl_bt_coex_cmd_old *bt_cmd;
+       /* Send ASYNC since this can be sent from an atomic context */
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+               .flags = CMD_ASYNC,
+       };
+       struct iwl_mvm_sta *mvmsta;
+       int ret;
+
+       mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+       if (!mvmsta)
+               return 0;
+
+       /* nothing to do */
+       if (mvmsta->bt_reduced_txpower == enable)
+               return 0;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
+       if (!bt_cmd)
+               return -ENOMEM;
+       cmd.data[0] = bt_cmd;
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+
+       bt_cmd->valid_bit_msk =
+               cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
+       bt_cmd->bt_reduced_tx_power = sta_id;
+
+       if (enable)
+               bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+
+       IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
+                      enable ? "en" : "dis", sta_id);
+
+       mvmsta->bt_reduced_txpower = enable;
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
+}
+
+struct iwl_bt_iterator_data {
+       struct iwl_bt_coex_profile_notif_old *notif;
+       struct iwl_mvm *mvm;
+       struct ieee80211_chanctx_conf *primary;
+       struct ieee80211_chanctx_conf *secondary;
+       bool primary_ll;
+};
+
+static inline
+void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
+                                      struct ieee80211_vif *vif,
+                                      bool enable, int rssi)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       mvmvif->bf_data.last_bt_coex_event = rssi;
+       mvmvif->bf_data.bt_coex_max_thold =
+               enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
+       mvmvif->bf_data.bt_coex_min_thold =
+               enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
+}
+
+/* must be called under rcu_read_lock */
+static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_bt_iterator_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       enum ieee80211_smps_mode smps_mode;
+       u32 bt_activity_grading;
+       int ave_rssi;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               /* default smps_mode for BSS / P2P client is AUTOMATIC */
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+               break;
+       case NL80211_IFTYPE_AP:
+               if (!mvmvif->ap_ibss_active)
+                       return;
+               break;
+       default:
+               return;
+       }
+
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+       /* If channel context is invalid or not on 2.4GHz .. */
+       if ((!chanctx_conf ||
+            chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
+               if (vif->type == NL80211_IFTYPE_STATION) {
+                       /* ... relax constraints and disable rssi events */
+                       iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                           smps_mode);
+                       iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+                                                   false);
+                       iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+               }
+               return;
+       }
+
+       bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
+       if (bt_activity_grading >= BT_HIGH_TRAFFIC)
+               smps_mode = IEEE80211_SMPS_STATIC;
+       else if (bt_activity_grading >= BT_LOW_TRAFFIC)
+               smps_mode = vif->type == NL80211_IFTYPE_AP ?
+                               IEEE80211_SMPS_OFF :
+                               IEEE80211_SMPS_DYNAMIC;
+
+       /* relax SMPS contraints for next association */
+       if (!vif->bss_conf.assoc)
+               smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+       IWL_DEBUG_COEX(data->mvm,
+                      "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
+                      mvmvif->id, data->notif->bt_status, bt_activity_grading,
+                      smps_mode);
+
+       if (vif->type == NL80211_IFTYPE_STATION)
+               iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+                                   smps_mode);
+
+       /* low latency is always primary */
+       if (iwl_mvm_vif_low_latency(mvmvif)) {
+               data->primary_ll = true;
+
+               data->secondary = data->primary;
+               data->primary = chanctx_conf;
+       }
+
+       if (vif->type == NL80211_IFTYPE_AP) {
+               if (!mvmvif->ap_ibss_active)
+                       return;
+
+               if (chanctx_conf == data->primary)
+                       return;
+
+               if (!data->primary_ll) {
+                       /*
+                        * downgrade the current primary no matter what its
+                        * type is.
+                        */
+                       data->secondary = data->primary;
+                       data->primary = chanctx_conf;
+               } else {
+                       /* there is low latency vif - we will be secondary */
+                       data->secondary = chanctx_conf;
+               }
+               return;
+       }
+
+       /*
+        * STA / P2P Client, try to be primary if first vif. If we are in low
+        * latency mode, we are already in primary and just don't do much
+        */
+       if (!data->primary || data->primary == chanctx_conf)
+               data->primary = chanctx_conf;
+       else if (!data->secondary)
+               /* if secondary is not NULL, it might be a GO */
+               data->secondary = chanctx_conf;
+
+       /*
+        * don't reduce the Tx power if one of these is true:
+        *  we are in LOOSE
+        *  single share antenna product
+        *  BT is active
+        *  we are associated
+        */
+       if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
+           mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
+           !data->notif->bt_status) {
+               iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
+               iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+               return;
+       }
+
+       /* try to get the avg rssi from fw */
+       ave_rssi = mvmvif->bf_data.ave_beacon_signal;
+
+       /* if the RSSI isn't valid, fake it is very low */
+       if (!ave_rssi)
+               ave_rssi = -100;
+       if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
+               if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
+                       IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+       } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
+               if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
+                       IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+       }
+
+       /* Begin to monitor the RSSI: it may influence the reduced Tx power */
+       iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
+}
+
+static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
+{
+       struct iwl_bt_iterator_data data = {
+               .mvm = mvm,
+               .notif = &mvm->last_bt_notif_old,
+       };
+       struct iwl_bt_coex_ci_cmd_old cmd = {};
+       u8 ci_bw_idx;
+
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return;
+
+       rcu_read_lock();
+       ieee80211_iterate_active_interfaces_atomic(
+                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+                                       iwl_mvm_bt_notif_iterator, &data);
+
+       if (data.primary) {
+               struct ieee80211_chanctx_conf *chan = data.primary;
+
+               if (WARN_ON(!chan->def.chan)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+                       ci_bw_idx = 0;
+                       cmd.co_run_bw_primary = 0;
+               } else {
+                       cmd.co_run_bw_primary = 1;
+                       if (chan->def.center_freq1 >
+                           chan->def.chan->center_freq)
+                               ci_bw_idx = 2;
+                       else
+                               ci_bw_idx = 1;
+               }
+
+               cmd.bt_primary_ci =
+                       iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+               cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+       }
+
+       if (data.secondary) {
+               struct ieee80211_chanctx_conf *chan = data.secondary;
+
+               if (WARN_ON(!data.secondary->def.chan)) {
+                       rcu_read_unlock();
+                       return;
+               }
+
+               if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+                       ci_bw_idx = 0;
+                       cmd.co_run_bw_secondary = 0;
+               } else {
+                       cmd.co_run_bw_secondary = 1;
+                       if (chan->def.center_freq1 >
+                           chan->def.chan->center_freq)
+                               ci_bw_idx = 2;
+                       else
+                               ci_bw_idx = 1;
+               }
+
+               cmd.bt_secondary_ci =
+                       iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+               cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
+       }
+
+       rcu_read_unlock();
+
+       /* Don't spam the fw with the same command over and over */
+       if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) {
+               if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
+                                        sizeof(cmd), &cmd))
+                       IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
+               memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd));
+       }
+
+       if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm))
+               IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
+                                struct iwl_rx_cmd_buffer *rxb,
+                                struct iwl_device_cmd *dev_cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data;
+
+       IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
+       IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
+                      notif->bt_status ? "ON" : "OFF");
+       IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
+       IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+       IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
+                      le32_to_cpu(notif->primary_ch_lut));
+       IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
+                      le32_to_cpu(notif->secondary_ch_lut));
+       IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
+                      le32_to_cpu(notif->bt_activity_grading));
+       IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
+                      notif->bt_agg_traffic_load);
+
+       /* remember this notification for future use: rssi fluctuations */
+       memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old));
+
+       iwl_mvm_bt_coex_notif_handle(mvm);
+
+       /*
+        * This is an async handler for a notification, returning anything other
+        * than 0 doesn't make sense even if HCMD failed.
+        */
+       return 0;
+}
+
+static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+       struct iwl_bt_iterator_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+
+       struct ieee80211_chanctx_conf *chanctx_conf;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(vif->chanctx_conf);
+       /* If channel context is invalid or not on 2.4GHz - don't count it */
+       if (!chanctx_conf ||
+           chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+               rcu_read_unlock();
+               return;
+       }
+       rcu_read_unlock();
+
+       if (vif->type != NL80211_IFTYPE_STATION ||
+           mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+               return;
+
+       sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+                                       lockdep_is_held(&mvm->mutex));
+
+       /* This can happen if the station has been removed right now */
+       if (IS_ERR_OR_NULL(sta))
+               return;
+
+       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+}
+
+void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                              enum ieee80211_rssi_event rssi_event)
+{
+       struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+       struct iwl_bt_iterator_data data = {
+               .mvm = mvm,
+       };
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return;
+
+       /*
+        * Rssi update while not associated - can happen since the statistics
+        * are handled asynchronously
+        */
+       if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+               return;
+
+       /* No BT - reports should be disabled */
+       if (!mvm->last_bt_notif_old.bt_status)
+               return;
+
+       IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
+                      rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
+
+       /*
+        * Check if rssi is good enough for reduced Tx power, but not in loose
+        * scheme.
+        */
+       if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
+           iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
+               ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+                                                 false);
+       else
+               ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
+
+       if (ret)
+               IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_bt_rssi_iterator, &data);
+
+       if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm))
+               IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF   (4000)
+#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT        (1200)
+
+u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
+                                   struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       enum iwl_bt_coex_lut_type lut_type;
+
+       if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
+           BT_HIGH_TRAFFIC)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       if (mvm->last_bt_notif_old.ttc_enabled)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+
+       if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
+               return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+       /* tight coex, high bt traffic, reduce AGG time limit */
+       return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
+}
+
+bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
+                                        struct ieee80211_sta *sta)
+{
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       enum iwl_bt_coex_lut_type lut_type;
+
+       if (mvm->last_bt_notif_old.ttc_enabled)
+               return true;
+
+       if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
+           BT_HIGH_TRAFFIC)
+               return true;
+
+       /*
+        * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
+        * since BT is already killed.
+        * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
+        * we Tx.
+        * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
+        */
+       lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+       return lut_type != BT_COEX_LOOSE_LUT;
+}
+
+bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
+{
+       u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+       return ag == BT_OFF;
+}
+
+bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
+                                       enum ieee80211_band band)
+{
+       u32 bt_activity =
+               le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+
+       if (band != IEEE80211_BAND_2GHZ)
+               return false;
+
+       return bt_activity >= BT_LOW_TRAFFIC;
+}
+
+void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm)
+{
+       iwl_mvm_bt_coex_notif_handle(mvm);
+}
+
+int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
+                                     struct iwl_rx_cmd_buffer *rxb,
+                                     struct iwl_device_cmd *dev_cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+       u8 __maybe_unused lower_bound, upper_bound;
+       int ret;
+       u8 lut;
+
+       struct iwl_bt_coex_cmd_old *bt_cmd;
+       struct iwl_host_cmd cmd = {
+               .id = BT_CONFIG,
+               .len = { sizeof(*bt_cmd), },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+
+       if (!IWL_MVM_BT_COEX_CORUNNING)
+               return 0;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Ignore updates if we are in force mode */
+       if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+               return 0;
+
+       if (ant_isolation ==  mvm->last_ant_isol)
+               return 0;
+
+       for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
+               if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
+                       break;
+
+       lower_bound = antenna_coupling_ranges[lut].range;
+
+       if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
+               upper_bound = antenna_coupling_ranges[lut + 1].range;
+       else
+               upper_bound = antenna_coupling_ranges[lut].range;
+
+       IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
+                      ant_isolation, lower_bound, upper_bound, lut);
+
+       mvm->last_ant_isol = ant_isolation;
+
+       if (mvm->last_corun_lut == lut)
+               return 0;
+
+       mvm->last_corun_lut = lut;
+
+       bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+       if (!bt_cmd)
+               return 0;
+       cmd.data[0] = bt_cmd;
+
+       bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+       bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+                                            BT_VALID_CORUN_LUT_20 |
+                                            BT_VALID_CORUN_LUT_40);
+
+       /* For the moment, use the same LUT for 20GHz and 40GHz */
+       memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
+              sizeof(bt_cmd->bt4_corun_lut20));
+
+       memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
+              sizeof(bt_cmd->bt4_corun_lut40));
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+       kfree(bt_cmd);
+       return ret;
+}
index 51685693af2e47e7ddfb7491bf8f74f0a1d745ae..ca79f7160573445110485b313d9a31a871d990b2 100644 (file)
@@ -79,6 +79,8 @@
 #define IWL_MVM_PS_SNOOZE_WINDOW               50
 #define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW                25
 #define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT       64
+#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH      62
+#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH     65
 #define IWL_MVM_BT_COEX_SYNC2SCO               1
 #define IWL_MVM_BT_COEX_CORUNNING              1
 #define IWL_MVM_BT_COEX_MPLUT                  1
index 29ca72695eaa60e0f53121dd45f1d080cdefba1d..7d18f466fbb3351b3b173fdcf4aa15bddd5d0b75 100644 (file)
@@ -146,17 +146,47 @@ static ssize_t iwl_dbgfs_fw_error_dump_read(struct file *file,
                                            char __user *user_buf,
                                            size_t count, loff_t *ppos)
 {
-       struct iwl_fw_error_dump_file *dump_file = file->private_data;
+       struct iwl_mvm_dump_ptrs *dump_ptrs = (void *)file->private_data;
+       ssize_t bytes_read = 0;
+       ssize_t bytes_read_trans = 0;
+
+       if (*ppos < dump_ptrs->op_mode_len)
+               bytes_read +=
+                       simple_read_from_buffer(user_buf, count, ppos,
+                                               dump_ptrs->op_mode_ptr,
+                                               dump_ptrs->op_mode_len);
+
+       if (bytes_read < 0 || *ppos < dump_ptrs->op_mode_len)
+               return bytes_read;
+
+       if (dump_ptrs->trans_ptr) {
+               *ppos -= dump_ptrs->op_mode_len;
+               bytes_read_trans =
+                       simple_read_from_buffer(user_buf + bytes_read,
+                                               count - bytes_read, ppos,
+                                               dump_ptrs->trans_ptr->data,
+                                               dump_ptrs->trans_ptr->len);
+               *ppos += dump_ptrs->op_mode_len;
+
+               if (bytes_read_trans >= 0)
+                       bytes_read += bytes_read_trans;
+               else if (!bytes_read)
+                       /* propagate the failure */
+                       return bytes_read_trans;
+       }
+
+       return bytes_read;
 
-       return simple_read_from_buffer(user_buf, count, ppos,
-                                      dump_file,
-                                      le32_to_cpu(dump_file->file_len));
 }
 
 static int iwl_dbgfs_fw_error_dump_release(struct inode *inode,
                                           struct file *file)
 {
-       vfree(file->private_data);
+       struct iwl_mvm_dump_ptrs *dump_ptrs = (void *)file->private_data;
+
+       vfree(dump_ptrs->op_mode_ptr);
+       vfree(dump_ptrs->trans_ptr);
+       kfree(dump_ptrs);
 
        return 0;
 }
@@ -312,20 +342,69 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
                                         BT_MBOX_MSG(notif, _num, _field),  \
                                         true ? "\n" : ", ");
 
-static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
-                                      size_t count, loff_t *ppos)
+static
+int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf,
+                          int pos, int bufsz)
 {
-       struct iwl_mvm *mvm = file->private_data;
-       struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
-       char *buf;
-       int ret, pos = 0, bufsz = sizeof(char) * 1024;
+       pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
 
-       buf = kmalloc(bufsz, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
+       BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
+       BT_MBOX_PRINT(0, LE_PROF1, false);
+       BT_MBOX_PRINT(0, LE_PROF2, false);
+       BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
+       BT_MBOX_PRINT(0, CHL_SEQ_N, false);
+       BT_MBOX_PRINT(0, INBAND_S, false);
+       BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
+       BT_MBOX_PRINT(0, LE_SCAN, false);
+       BT_MBOX_PRINT(0, LE_ADV, false);
+       BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
+       BT_MBOX_PRINT(0, OPEN_CON_1, true);
 
-       mutex_lock(&mvm->mutex);
+       pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
+
+       BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
+       BT_MBOX_PRINT(1, IP_SR, false);
+       BT_MBOX_PRINT(1, LE_MSTR, false);
+       BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
+       BT_MBOX_PRINT(1, MSG_TYPE, false);
+       BT_MBOX_PRINT(1, SSN, true);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
+
+       BT_MBOX_PRINT(2, SNIFF_ACT, false);
+       BT_MBOX_PRINT(2, PAG, false);
+       BT_MBOX_PRINT(2, INQUIRY, false);
+       BT_MBOX_PRINT(2, CONN, false);
+       BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
+       BT_MBOX_PRINT(2, DISC, false);
+       BT_MBOX_PRINT(2, SCO_TX_ACT, false);
+       BT_MBOX_PRINT(2, SCO_RX_ACT, false);
+       BT_MBOX_PRINT(2, ESCO_RE_TX, false);
+       BT_MBOX_PRINT(2, SCO_DURATION, true);
+
+       pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
+
+       BT_MBOX_PRINT(3, SCO_STATE, false);
+       BT_MBOX_PRINT(3, SNIFF_STATE, false);
+       BT_MBOX_PRINT(3, A2DP_STATE, false);
+       BT_MBOX_PRINT(3, ACL_STATE, false);
+       BT_MBOX_PRINT(3, MSTR_STATE, false);
+       BT_MBOX_PRINT(3, OBX_STATE, false);
+       BT_MBOX_PRINT(3, OPEN_CON_2, false);
+       BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
+       BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
+       BT_MBOX_PRINT(3, INBAND_P, false);
+       BT_MBOX_PRINT(3, MSG_TYPE_2, false);
+       BT_MBOX_PRINT(3, SSN_2, false);
+       BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
+
+       return pos;
+}
 
+static
+int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif,
+                              char *buf, int pos, int bufsz)
+{
        pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
 
        BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
@@ -378,25 +457,59 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
        BT_MBOX_PRINT(3, SSN_2, false);
        BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
 
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
-                        notif->bt_status);
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
-                        notif->bt_open_conn);
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
-                        notif->bt_traffic_load);
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
-                        notif->bt_agg_traffic_load);
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
-                        notif->bt_ci_compliance);
-       pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
-                        le32_to_cpu(notif->primary_ch_lut));
-       pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
-                        le32_to_cpu(notif->secondary_ch_lut));
-       pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n",
-                        le32_to_cpu(notif->bt_activity_grading));
-       pos += scnprintf(buf+pos, bufsz-pos,
-                        "antenna isolation = %d CORUN LUT index = %d\n",
-                        mvm->last_ant_isol, mvm->last_corun_lut);
+       return pos;
+}
+
+static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       char *buf;
+       int ret, pos = 0, bufsz = sizeof(char) * 1024;
+
+       buf = kmalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+               struct iwl_bt_coex_profile_notif_old *notif =
+                       &mvm->last_bt_notif_old;
+
+               pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz);
+
+               pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+                                notif->bt_ci_compliance);
+               pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+                                le32_to_cpu(notif->primary_ch_lut));
+               pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+                                le32_to_cpu(notif->secondary_ch_lut));
+               pos += scnprintf(buf+pos,
+                                bufsz-pos, "bt_activity_grading = %d\n",
+                                le32_to_cpu(notif->bt_activity_grading));
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "antenna isolation = %d CORUN LUT index = %d\n",
+                                mvm->last_ant_isol, mvm->last_corun_lut);
+       } else {
+               struct iwl_bt_coex_profile_notif *notif =
+                       &mvm->last_bt_notif;
+
+               pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz);
+
+               pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+                                notif->bt_ci_compliance);
+               pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+                                le32_to_cpu(notif->primary_ch_lut));
+               pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+                                le32_to_cpu(notif->secondary_ch_lut));
+               pos += scnprintf(buf+pos,
+                                bufsz-pos, "bt_activity_grading = %d\n",
+                                le32_to_cpu(notif->bt_activity_grading));
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "antenna isolation = %d CORUN LUT index = %d\n",
+                                mvm->last_ant_isol, mvm->last_corun_lut);
+       }
 
        mutex_unlock(&mvm->mutex);
 
@@ -411,28 +524,57 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
                                     size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
        char buf[256];
        int bufsz = sizeof(buf);
        int pos = 0;
 
        mutex_lock(&mvm->mutex);
 
-       pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n");
-       pos += scnprintf(buf+pos, bufsz-pos,
-                      "\tPrimary Channel Bitmap 0x%016llx Fat: %d\n",
-                      le64_to_cpu(cmd->bt_primary_ci),
-                      !!cmd->co_run_bw_primary);
-       pos += scnprintf(buf+pos, bufsz-pos,
-                      "\tSecondary Channel Bitmap 0x%016llx Fat: %d\n",
-                      le64_to_cpu(cmd->bt_secondary_ci),
-                      !!cmd->co_run_bw_secondary);
-
-       pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
-       pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
-                        iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
-       pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
-                        iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+               struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old;
+
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "Channel inhibition CMD\n");
+               pos += scnprintf(buf+pos, bufsz-pos,
+                              "\tPrimary Channel Bitmap 0x%016llx\n",
+                              le64_to_cpu(cmd->bt_primary_ci));
+               pos += scnprintf(buf+pos, bufsz-pos,
+                              "\tSecondary Channel Bitmap 0x%016llx\n",
+                              le64_to_cpu(cmd->bt_secondary_ci));
+
+               pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
+               pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
+                                iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]);
+               pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
+                                iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
+
+       } else {
+               struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
+
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "Channel inhibition CMD\n");
+               pos += scnprintf(buf+pos, bufsz-pos,
+                              "\tPrimary Channel Bitmap 0x%016llx\n",
+                              le64_to_cpu(cmd->bt_primary_ci));
+               pos += scnprintf(buf+pos, bufsz-pos,
+                              "\tSecondary Channel Bitmap 0x%016llx\n",
+                              le64_to_cpu(cmd->bt_secondary_ci));
+
+               pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "\tPrimary: ACK Kill Mask 0x%08x\n",
+                                iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[0]]);
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "\tPrimary: CTS Kill Mask 0x%08x\n",
+                                iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "\tSecondary: ACK Kill Mask 0x%08x\n",
+                                iwl_bt_ctl_kill_msk[mvm->bt_ack_kill_msk[1]]);
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "\tSecondary: CTS Kill Mask 0x%08x\n",
+                                iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[1]]);
+
+       }
 
        mutex_unlock(&mvm->mutex);
 
@@ -455,6 +597,43 @@ iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf,
        return count;
 }
 
+static ssize_t
+iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf,
+                            size_t count, loff_t *ppos)
+{
+       static const char * const modes_str[BT_FORCE_ANT_MAX] = {
+               [BT_FORCE_ANT_DIS] = "dis",
+               [BT_FORCE_ANT_AUTO] = "auto",
+               [BT_FORCE_ANT_BT] = "bt",
+               [BT_FORCE_ANT_WIFI] = "wifi",
+       };
+       int ret, bt_force_ant_mode;
+
+       for (bt_force_ant_mode = 0;
+            bt_force_ant_mode < ARRAY_SIZE(modes_str);
+            bt_force_ant_mode++) {
+               if (!strcmp(buf, modes_str[bt_force_ant_mode]))
+                       break;
+       }
+
+       if (bt_force_ant_mode >= ARRAY_SIZE(modes_str))
+               return -EINVAL;
+
+       ret = 0;
+       mutex_lock(&mvm->mutex);
+       if (mvm->bt_force_ant_mode == bt_force_ant_mode)
+               goto out;
+
+       mvm->bt_force_ant_mode = bt_force_ant_mode;
+       IWL_DEBUG_COEX(mvm, "Force mode: %s\n",
+                      modes_str[mvm->bt_force_ant_mode]);
+       ret = iwl_send_bt_init_conf(mvm);
+
+out:
+       mutex_unlock(&mvm->mutex);
+       return ret ?: count;
+}
+
 #define PRINT_STATS_LE32(_str, _val)                                   \
                         pos += scnprintf(buf + pos, bufsz - pos,       \
                                          fmt_table, _str,              \
@@ -690,8 +869,14 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf,
 static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf,
                                      size_t count, loff_t *ppos)
 {
+       int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_NMI);
+       if (ret)
+               return ret;
+
        iwl_force_nmi(mvm->trans);
 
+       iwl_mvm_unref(mvm, IWL_MVM_REF_NMI);
+
        return count;
 }
 
@@ -975,11 +1160,11 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
 }
 #endif
 
-#define PRINT_MVM_REF(ref) do {                                        \
-       if (test_bit(ref, mvm->ref_bitmap))                     \
-               pos += scnprintf(buf + pos, bufsz - pos,        \
-                                "\t(0x%lx) %s\n",              \
-                                BIT(ref), #ref);               \
+#define PRINT_MVM_REF(ref) do {                                                \
+       if (mvm->refs[ref])                                             \
+               pos += scnprintf(buf + pos, bufsz - pos,                \
+                                "\t(0x%lx): %d %s\n",                  \
+                                BIT(ref), mvm->refs[ref], #ref);       \
 } while (0)
 
 static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
@@ -987,12 +1172,17 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file,
                                        size_t count, loff_t *ppos)
 {
        struct iwl_mvm *mvm = file->private_data;
-       int pos = 0;
+       int i, pos = 0;
        char buf[256];
        const size_t bufsz = sizeof(buf);
+       u32 refs = 0;
+
+       for (i = 0; i < IWL_MVM_REF_COUNT; i++)
+               if (mvm->refs[i])
+                       refs |= BIT(i);
 
-       pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%lx\n",
-                        mvm->ref_bitmap[0]);
+       pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%x\n",
+                        refs);
 
        PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN);
        PRINT_MVM_REF(IWL_MVM_REF_SCAN);
@@ -1018,7 +1208,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf,
 
        mutex_lock(&mvm->mutex);
 
-       taken = test_bit(IWL_MVM_REF_USER, mvm->ref_bitmap);
+       taken = mvm->refs[IWL_MVM_REF_USER];
        if (value == 1 && !taken)
                iwl_mvm_ref(mvm, IWL_MVM_REF_USER);
        else if (value == 0 && taken)
@@ -1054,14 +1244,21 @@ iwl_dbgfs_prph_reg_read(struct file *file,
        int pos = 0;
        char buf[32];
        const size_t bufsz = sizeof(buf);
+       int ret;
 
        if (!mvm->dbgfs_prph_reg_addr)
                return -EINVAL;
 
+       ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_READ);
+       if (ret)
+               return ret;
+
        pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n",
                mvm->dbgfs_prph_reg_addr,
                iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr));
 
+       iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_READ);
+
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
@@ -1071,6 +1268,7 @@ iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,
 {
        u8 args;
        u32 value;
+       int ret;
 
        args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value);
        /* if we only want to set the reg address - nothing more to do */
@@ -1081,7 +1279,13 @@ iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf,
        if (args != 2)
                return -EINVAL;
 
+       ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE);
+       if (ret)
+               return ret;
+
        iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value);
+
+       iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
 out:
        return count;
 }
@@ -1101,6 +1305,7 @@ MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
 MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
 
@@ -1142,6 +1347,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR);
+       MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
                             S_IWUSR | S_IRUSR);
        MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
index 5fe82c29c8ad07bcb7bab43726a4e09b136d8b53..69875716dcdb80abeb688949d826267eb391ccf0 100644 (file)
  * enum iwl_bt_coex_flags - flags for BT_COEX command
  * @BT_COEX_MODE_POS:
  * @BT_COEX_MODE_MSK:
- * @BT_COEX_DISABLE:
- * @BT_COEX_2W:
- * @BT_COEX_3W:
- * @BT_COEX_NW:
+ * @BT_COEX_DISABLE_OLD:
+ * @BT_COEX_2W_OLD:
+ * @BT_COEX_3W_OLD:
+ * @BT_COEX_NW_OLD:
+ * @BT_COEX_AUTO_OLD:
+ * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests)
+ * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests)
  * @BT_COEX_SYNC2SCO:
  * @BT_COEX_CORUNNING:
  * @BT_COEX_MPLUT:
 enum iwl_bt_coex_flags {
        BT_COEX_MODE_POS                = 3,
        BT_COEX_MODE_MSK                = BITS(3) << BT_COEX_MODE_POS,
-       BT_COEX_DISABLE                 = 0x0 << BT_COEX_MODE_POS,
-       BT_COEX_2W                      = 0x1 << BT_COEX_MODE_POS,
-       BT_COEX_3W                      = 0x2 << BT_COEX_MODE_POS,
-       BT_COEX_NW                      = 0x3 << BT_COEX_MODE_POS,
+       BT_COEX_DISABLE_OLD             = 0x0 << BT_COEX_MODE_POS,
+       BT_COEX_2W_OLD                  = 0x1 << BT_COEX_MODE_POS,
+       BT_COEX_3W_OLD                  = 0x2 << BT_COEX_MODE_POS,
+       BT_COEX_NW_OLD                  = 0x3 << BT_COEX_MODE_POS,
+       BT_COEX_AUTO_OLD                = 0x5 << BT_COEX_MODE_POS,
+       BT_COEX_BT_OLD                  = 0x6 << BT_COEX_MODE_POS,
+       BT_COEX_WIFI_OLD                = 0x7 << BT_COEX_MODE_POS,
        BT_COEX_SYNC2SCO                = BIT(7),
        BT_COEX_CORUNNING               = BIT(8),
        BT_COEX_MPLUT                   = BIT(9),
@@ -151,7 +157,7 @@ enum iwl_bt_coex_lut_type {
 #define BT_REDUCED_TX_POWER_BIT BIT(7)
 
 /**
- * struct iwl_bt_coex_cmd - bt coex configuration command
+ * struct iwl_bt_coex_cmd_old - bt coex configuration command
  * @flags:&enum iwl_bt_coex_flags
  * @max_kill:
  * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
@@ -176,7 +182,7 @@ enum iwl_bt_coex_lut_type {
  *
  * The structure is used for the BT_COEX command.
  */
-struct iwl_bt_coex_cmd {
+struct iwl_bt_coex_cmd_old {
        __le32 flags;
        u8 max_kill;
        u8 bt_reduced_tx_power;
@@ -202,26 +208,117 @@ struct iwl_bt_coex_cmd {
        __le32 valid_bit_msk;
 } __packed; /* BT_COEX_CMD_API_S_VER_5 */
 
+enum iwl_bt_coex_mode {
+       BT_COEX_DISABLE                 = 0x0,
+       BT_COEX_NW                      = 0x1,
+       BT_COEX_BT                      = 0x2,
+       BT_COEX_WIFI                    = 0x3,
+}; /* BT_COEX_MODES_E */
+
+enum iwl_bt_coex_enabled_modules {
+       BT_COEX_MPLUT_ENABLED           = BIT(0),
+       BT_COEX_MPLUT_BOOST_ENABLED     = BIT(1),
+       BT_COEX_SYNC2SCO_ENABLED        = BIT(2),
+       BT_COEX_CORUN_ENABLED           = BIT(3),
+       BT_COEX_HIGH_BAND_RET           = BIT(4),
+}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */
+
+/**
+ * struct iwl_bt_coex_cmd - bt coex configuration command
+ * @mode: enum %iwl_bt_coex_mode
+ * @enabled_modules: enum %iwl_bt_coex_enabled_modules
+ * @max_kill: max count of Tx retries due to kill from PTA
+ * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ *     should be set by default
+ * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ *     should be set by default
+ * @bt4_antenna_isolation_thr: antenna threshold value
+ * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
+ * @bt4_tx_rx_max_freq0: TxRx max frequency
+ * @multiprio_lut: multi priority LUT configuration
+ * @mplut_prio_boost: BT priority boost registers
+ * @decision_lut: PTA decision LUT, per Prio-Ch
+ *
+ * The structure is used for the BT_COEX command.
+ */
+struct iwl_bt_coex_cmd {
+       __le32 mode;
+       __le32 enabled_modules;
+
+       __le32 max_kill;
+       __le32 override_primary_lut;
+       __le32 override_secondary_lut;
+       __le32 bt4_antenna_isolation_thr;
+
+       __le32 bt4_tx_tx_delta_freq_thr;
+       __le32 bt4_tx_rx_max_freq0;
+
+       __le32 multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
+       __le32 mplut_prio_boost[BT_COEX_BOOST_SIZE];
+
+       __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
+} __packed; /* BT_COEX_CMD_API_S_VER_6 */
+
+/**
+ * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut
+ * @corun_lut20: co-running 20 MHz LUT configuration
+ * @corun_lut40: co-running 40 MHz LUT configuration
+ *
+ * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command.
+ */
+struct iwl_bt_coex_corun_lut_update_cmd {
+       __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE];
+       __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE];
+} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_sw_boost - SW boost values
+ * @wifi_tx_prio_boost: SW boost of wifi tx priority
+ * @wifi_rx_prio_boost: SW boost of wifi rx priority
+ * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
+ * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
+ */
+struct iwl_bt_coex_sw_boost {
+       __le32 wifi_tx_prio_boost;
+       __le32 wifi_rx_prio_boost;
+       __le32 kill_ack_msk;
+       __le32 kill_cts_msk;
+};
+
+/**
+ * struct iwl_bt_coex_sw_boost_update_cmd - command to update the SW boost
+ * @boost_values: check struct  %iwl_bt_coex_sw_boost - one for each channel
+ *     primary / secondary / low priority
+ */
+struct iwl_bt_coex_sw_boost_update_cmd {
+       struct iwl_bt_coex_sw_boost boost_values[3];
+} __packed; /* BT_COEX_UPDATE_SW_BOOST_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_reduced_txp_update_cmd
+ * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
+ *     bits are the sta_id (value)
+ */
+struct iwl_bt_coex_reduced_txp_update_cmd {
+       __le32 reduced_txp;
+} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */
+
 /**
  * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
  * @bt_primary_ci:
- * @bt_secondary_ci:
- * @co_run_bw_primary:
- * @co_run_bw_secondary:
  * @primary_ch_phy_id:
+ * @bt_secondary_ci:
  * @secondary_ch_phy_id:
  *
  * Used for BT_COEX_CI command
  */
 struct iwl_bt_coex_ci_cmd {
        __le64 bt_primary_ci;
-       __le64 bt_secondary_ci;
+       __le32 primary_ch_phy_id;
 
-       u8 co_run_bw_primary;
-       u8 co_run_bw_secondary;
-       u8 primary_ch_phy_id;
-       u8 secondary_ch_phy_id;
-} __packed; /* BT_CI_MSG_API_S_VER_1 */
+       __le64 bt_secondary_ci;
+       __le32 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_2 */
 
 #define BT_MBOX(n_dw, _msg, _pos, _nbits)      \
        BT_MBOX##n_dw##_##_msg##_POS = (_pos),  \
@@ -288,35 +385,44 @@ enum iwl_bt_activity_grading {
        BT_ON_NO_CONNECTION     = 1,
        BT_LOW_TRAFFIC          = 2,
        BT_HIGH_TRAFFIC         = 3,
+
+       BT_MAX_AG,
 }; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
 
+enum iwl_bt_ci_compliance {
+       BT_CI_COMPLIANCE_NONE           = 0,
+       BT_CI_COMPLIANCE_PRIMARY        = 1,
+       BT_CI_COMPLIANCE_SECONDARY      = 2,
+       BT_CI_COMPLIANCE_BOTH           = 3,
+}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */
+
+#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id)   \
+               (_ttc_rrc_status & BIT(_phy_id))
+
+#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id)   \
+               ((_ttc_rrc_status >> 4) & BIT(_phy_id))
+
 /**
  * struct iwl_bt_coex_profile_notif - notification about BT coex
  * @mbox_msg: message from BT to WiFi
  * @msg_idx: the index of the message
- * @bt_status: 0 - off, 1 - on
- * @bt_open_conn: number of BT connections open
- * @bt_traffic_load: load of BT traffic
- * @bt_agg_traffic_load: aggregated load of BT traffic
- * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
- * @primary_ch_lut: LUT used for primary channel
- * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_ci_compliance: enum %iwl_bt_ci_compliance
+ * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type
+ * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type
  * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
+ * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY
  */
 struct iwl_bt_coex_profile_notif {
        __le32 mbox_msg[4];
        __le32 msg_idx;
-       u8 bt_status;
-       u8 bt_open_conn;
-       u8 bt_traffic_load;
-       u8 bt_agg_traffic_load;
-       u8 bt_ci_compliance;
-       u8 reserved[3];
+       __le32 bt_ci_compliance;
 
        __le32 primary_ch_lut;
        __le32 secondary_ch_lut;
        __le32 bt_activity_grading;
-} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
+       u8 ttc_rrc_status;
+       u8 reserved[3];
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */
 
 enum iwl_bt_coex_prio_table_event {
        BT_COEX_PRIO_TBL_EVT_INIT_CALIB1                = 0,
@@ -355,4 +461,54 @@ struct iwl_bt_coex_prio_tbl_cmd {
        u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
 } __packed;
 
+/**
+ * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command
+ * @bt_primary_ci:
+ * @bt_secondary_ci:
+ * @co_run_bw_primary:
+ * @co_run_bw_secondary:
+ * @primary_ch_phy_id:
+ * @secondary_ch_phy_id:
+ *
+ * Used for BT_COEX_CI command
+ */
+struct iwl_bt_coex_ci_cmd_old {
+       __le64 bt_primary_ci;
+       __le64 bt_secondary_ci;
+
+       u8 co_run_bw_primary;
+       u8 co_run_bw_secondary;
+       u8 primary_ch_phy_id;
+       u8 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_profile_notif_old - notification about BT coex
+ * @mbox_msg: message from BT to WiFi
+ * @msg_idx: the index of the message
+ * @bt_status: 0 - off, 1 - on
+ * @bt_open_conn: number of BT connections open
+ * @bt_traffic_load: load of BT traffic
+ * @bt_agg_traffic_load: aggregated load of BT traffic
+ * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ * @primary_ch_lut: LUT used for primary channel
+ * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
+ */
+struct iwl_bt_coex_profile_notif_old {
+       __le32 mbox_msg[4];
+       __le32 msg_idx;
+       u8 bt_status;
+       u8 bt_open_conn;
+       u8 bt_traffic_load;
+       u8 bt_agg_traffic_load;
+       u8 bt_ci_compliance;
+       u8 ttc_enabled;
+       __le16 reserved;
+
+       __le32 primary_ch_lut;
+       __le32 secondary_ch_lut;
+       __le32 bt_activity_grading;
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
+
 #endif /* __fw_api_bt_coex_h__ */
index cbbcd8e284e4bec620eb7e272b02941b3805aa0b..c3a8c86b550d45aaca72a274252960d8397d0ef6 100644 (file)
@@ -336,7 +336,7 @@ struct iwl_beacon_filter_cmd {
 #define IWL_BF_DEBUG_FLAG_D0I3 0
 
 #define IWL_BF_ESCAPE_TIMER_DEFAULT 50
-#define IWL_BF_ESCAPE_TIMER_D0I3 1024
+#define IWL_BF_ESCAPE_TIMER_D0I3 0
 #define IWL_BF_ESCAPE_TIMER_MAX 1024
 #define IWL_BF_ESCAPE_TIMER_MIN 0
 
index 6959fda3fe09d09e34d5fe19c7ded403fba79c37..c02a9e45ec5eaec78ce9ef49c47957c6fcd2c7da 100644 (file)
@@ -169,19 +169,13 @@ enum iwl_scan_type {
        SCAN_TYPE_DISCOVERY_FORCED      = 6,
 }; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
 
-/**
- * Maximal number of channels to scan
- * it should be equal to:
- * max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000)
- */
-#define MAX_NUM_SCAN_CHANNELS 50
-
 /**
  * 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)
+ * @channel_count: num of channels in channel list
+ *     (1 - ucode_capa.n_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)
@@ -345,7 +339,7 @@ struct iwl_scan_results_notif {
  * @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
+ * @results: array of scan results, only "scanned_channels" of them are valid
  */
 struct iwl_scan_complete_notif {
        u8 scanned_channels;
@@ -354,11 +348,10 @@ struct iwl_scan_complete_notif {
        u8 last_channel;
        __le32 tsf_low;
        __le32 tsf_high;
-       struct iwl_scan_results_notif results[MAX_NUM_SCAN_CHANNELS];
+       struct iwl_scan_results_notif results[];
 } __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_SHORT_BLACKLIST_LEN   16
 #define IWL_SCAN_MAX_PROFILES          11
@@ -423,36 +416,24 @@ enum iwl_scan_offload_channel_flags {
        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.
+/* channel configuration for struct iwl_scan_offload_cfg. Each channels needs:
+ * __le32 type:        bitmap; bits 1-20 are for directed scan to i'th ssid and
+ *     see enum iwl_scan_offload_channel_flags.
+ * __le16 channel_number: channel number 1-13 etc.
+ * __le16 iter_count: repetition count for the channel.
+ * __le32 iter_interval: interval between two innteration on one channel.
+ * u8 active_dwell.
+ * u8 passive_dwell.
  */
-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;
+#define IWL_SCAN_CHAN_SIZE 14
 
 /**
  * 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)
+ * @data:              scan channel configuration and probe request frames
  */
 struct iwl_scan_offload_cfg {
        struct iwl_scan_offload_cmd scan_cmd;
-       struct iwl_scan_channel_cfg channel_cfg;
        u8 data[0];
 } __packed;
 
@@ -528,7 +509,7 @@ struct iwl_scan_offload_profile_cfg {
  * @full_scan_mul:     number of partial scans before each full scan
  */
 struct iwl_scan_offload_schedule {
-       u16 delay;
+       __le16 delay;
        u8 iterations;
        u8 full_scan_mul;
 } __packed;
@@ -601,4 +582,211 @@ struct iwl_sched_scan_results {
        u8 reserved;
 };
 
+/* Unified LMAC scan API */
+
+#define IWL_MVM_BASIC_PASSIVE_DWELL 110
+
+/**
+ * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S
+ * @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
+ * @reserved: for alignment and future use
+ */
+struct iwl_scan_req_tx_cmd {
+       __le32 tx_flags;
+       __le32 rate_n_flags;
+       u8 sta_id;
+       u8 reserved[3];
+} __packed;
+
+enum iwl_scan_channel_flags_lmac {
+       IWL_UNIFIED_SCAN_CHANNEL_FULL           = BIT(27),
+       IWL_UNIFIED_SCAN_CHANNEL_PARTIAL        = BIT(28),
+};
+
+/**
+ * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2
+ * @flags:             bits 1-20: directed scan to i'th ssid
+ *                     other bits &enum iwl_scan_channel_flags_lmac
+ * @channel_number:    channel number 1-13 etc
+ * @iter_count:                scan iteration on this channel
+ * @iter_interval:     interval in seconds between iterations on one channel
+ */
+struct iwl_scan_channel_cfg_lmac {
+       __le32 flags;
+       __le16 channel_num;
+       __le16 iter_count;
+       __le32 iter_interval;
+} __packed;
+
+/*
+ * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1
+ * @offset: offset in the data block
+ * @len: length of the segment
+ */
+struct iwl_scan_probe_segment {
+       __le16 offset;
+       __le16 len;
+} __packed;
+
+/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2
+ * @mac_header: first (and common) part of the probe
+ * @band_data: band specific data
+ * @common_data: last (and common) part of the probe
+ * @buf: raw data block
+ */
+struct iwl_scan_probe_req {
+       struct iwl_scan_probe_segment mac_header;
+       struct iwl_scan_probe_segment band_data[2];
+       struct iwl_scan_probe_segment common_data;
+       u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE];
+} __packed;
+
+enum iwl_scan_channel_flags {
+       IWL_SCAN_CHANNEL_FLAG_EBS               = BIT(0),
+       IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE      = BIT(1),
+       IWL_SCAN_CHANNEL_FLAG_CACHE_ADD         = BIT(2),
+};
+
+/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S
+ * @flags: enum iwl_scan_channel_flgs
+ * @non_ebs_ratio: how many regular scan iteration before EBS
+ */
+struct iwl_scan_channel_opt {
+       __le16 flags;
+       __le16 non_ebs_ratio;
+} __packed;
+
+/**
+ * iwl_mvm_lmac_scan_flags
+ * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses
+ *     without filtering.
+ * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels
+ * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan
+ * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification
+ * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching
+ * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented
+ */
+enum iwl_mvm_lmac_scan_flags {
+       IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL         = BIT(0),
+       IWL_MVM_LMAC_SCAN_FLAG_PASSIVE          = BIT(1),
+       IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION   = BIT(2),
+       IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE    = BIT(3),
+       IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS   = BIT(4),
+       IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED       = BIT(5),
+};
+
+enum iwl_scan_priority {
+       IWL_SCAN_PRIORITY_LOW,
+       IWL_SCAN_PRIORITY_MEDIUM,
+       IWL_SCAN_PRIORITY_HIGH,
+};
+
+/**
+ * iwl_scan_req_unified_lmac - SCAN_REQUEST_CMD_API_S_VER_1
+ * @reserved1: for alignment and future use
+ * @channel_num: num of channels to scan
+ * @active-dwell: dwell time for active channels
+ * @passive-dwell: dwell time for passive channels
+ * @fragmented-dwell: dwell time for fragmented passive scan
+ * @reserved2: for alignment and future use
+ * @rx_chain_selct: PHY_RX_CHAIN_* flags
+ * @scan_flags: &enum iwl_mvm_lmac_scan_flags
+ * @max_out_time: max time (in TU) to be out of associated channel
+ * @suspend_time: pause scan this long (TUs) when returning to service channel
+ * @flags: RXON flags
+ * @filter_flags: RXON filter
+ * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz
+ * @direct_scan: list of SSIDs for directed active scan
+ * @scan_prio: enum iwl_scan_priority
+ * @iter_num: number of scan iterations
+ * @delay: delay in seconds before first iteration
+ * @schedule: two scheduling plans. The first one is finite, the second one can
+ *     be infinite.
+ * @channel_opt: channel optimization options, for full and partial scan
+ * @data: channel configuration and probe request packet.
+ */
+struct iwl_scan_req_unified_lmac {
+       /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */
+       __le32 reserved1;
+       u8 n_channels;
+       u8 active_dwell;
+       u8 passive_dwell;
+       u8 fragmented_dwell;
+       __le16 reserved2;
+       __le16 rx_chain_select;
+       __le32 scan_flags;
+       __le32 max_out_time;
+       __le32 suspend_time;
+       /* RX_ON_FLAGS_API_S_VER_1 */
+       __le32 flags;
+       __le32 filter_flags;
+       struct iwl_scan_req_tx_cmd tx_cmd[2];
+       struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+       __le32 scan_prio;
+       /* SCAN_REQ_PERIODIC_PARAMS_API_S */
+       __le32 iter_num;
+       __le32 delay;
+       struct iwl_scan_offload_schedule schedule[2];
+       struct iwl_scan_channel_opt channel_opt[2];
+       u8 data[];
+} __packed;
+
+/**
+ * struct iwl_lmac_scan_results_notif - scan results for one channel -
+ *     SCAN_RESULT_NTF_API_S_VER_3
+ * @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
+ */
+struct iwl_lmac_scan_results_notif {
+       u8 channel;
+       u8 band;
+       u8 probe_status;
+       u8 num_probe_not_sent;
+       __le32 duration;
+} __packed;
+
+/**
+ * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels)
+ *     SCAN_COMPLETE_NTF_API_S_VER_3
+ * @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: an array of scan results, only "scanned_channels" of them are valid
+ */
+struct iwl_lmac_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[];
+} __packed;
+
+/**
+ * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2
+ * @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_complete_status
+ * @ebs_status: EBS success status &enum iwl_scan_ebs_status
+ * @time_after_last_iter; time in seconds elapsed after last iteration
+ */
+struct iwl_periodic_scan_complete {
+       u8 last_schedule_line;
+       u8 last_schedule_iteration;
+       u8 status;
+       u8 ebs_status;
+       __le32 time_after_last_iter;
+       __le32 reserved;
+} __packed;
+
 #endif
index 39cebee8016feaab62f005e5e843447784594429..47bd0406355d2738f00477d237c57bcd3d2706f2 100644 (file)
@@ -67,7 +67,7 @@
  * 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_DISABLE_TX: set if TX should be disabled
  * @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
@@ -91,10 +91,7 @@ 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_DISABLE_TX              = BIT(4),
 
        STA_FLG_PS                      = BIT(8),
        STA_FLG_DRAIN_FLOW              = BIT(12),
index 6cc5f52b807f1bc343ea632674e215423c3abb1d..d6073f67b212e12c2c7565cadf44c50268763cd8 100644 (file)
  * @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_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
@@ -103,7 +99,6 @@ 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),
@@ -113,11 +108,9 @@ enum iwl_tx_flags {
        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),
@@ -191,8 +184,6 @@ enum iwl_tx_flags {
  * 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_*
@@ -210,8 +201,6 @@ enum iwl_tx_flags {
  * @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)
@@ -241,8 +230,7 @@ struct iwl_tx_cmd {
        u8 initial_rate_index;
        u8 reserved2;
        u8 key[16];
-       __le16 next_frame_flags;
-       __le16 reserved3;
+       __le32 reserved3;
        __le32 life_time;
        __le32 dram_lsb_ptr;
        u8 dram_msb_ptr;
@@ -250,7 +238,7 @@ struct iwl_tx_cmd {
        u8 data_retry_limit;
        u8 tid_tspec;
        __le16 pm_frame_timeout;
-       __le16 driver_txop;
+       __le16 reserved4;
        u8 payload[0];
        struct ieee80211_hdr hdr[0];
 } __packed; /* TX_CMD_API_S_VER_3 */
@@ -548,6 +536,20 @@ struct iwl_beacon_notif {
        __le32 ibss_mgr_status;
 } __packed;
 
+/**
+ * struct iwl_extended_beacon_notif - notifies about beacon transmission
+ * @beacon_notify_hdr: tx response command associated with the beacon
+ * @tsf: last beacon tsf
+ * @ibss_mgr_status: whether IBSS is manager
+ * @gp2: last beacon time in gp2
+ */
+struct iwl_extended_beacon_notif {
+       struct iwl_mvm_tx_resp beacon_notify_hdr;
+       __le64 tsf;
+       __le32 ibss_mgr_status;
+       __le32 gp2;
+} __packed; /* BEACON_NTFY_API_S_VER_5 */
+
 /**
  * enum iwl_dump_control - dump (flush) control flags
  * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
index 309a9b9a94fecc26918f967e7b9e7a01374d43b3..95f5b3274efb516f280eee74eb8f9649c51d599a 100644 (file)
@@ -86,6 +86,8 @@ enum {
 
 #define IWL_MVM_STATION_COUNT  16
 
+#define IWL_MVM_TDLS_STA_COUNT 4
+
 /* commands */
 enum {
        MVM_ALIVE = 0x1,
@@ -131,10 +133,12 @@ enum {
        /* Scan offload */
        SCAN_OFFLOAD_REQUEST_CMD = 0x51,
        SCAN_OFFLOAD_ABORT_CMD = 0x52,
+       HOT_SPOT_CMD = 0x53,
        SCAN_OFFLOAD_COMPLETE = 0x6D,
        SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
        SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
        MATCH_FOUND_NOTIFICATION = 0xd9,
+       SCAN_ITERATION_COMPLETE = 0xe7,
 
        /* Phy */
        PHY_CONFIGURATION_CMD = 0x6a,
@@ -163,7 +167,6 @@ enum {
        BEACON_NOTIFICATION = 0x90,
        BEACON_TEMPLATE_CMD = 0x91,
        TX_ANT_CONFIGURATION_CMD = 0x98,
-       BT_CONFIG = 0x9b,
        STATISTICS_NOTIFICATION = 0x9d,
        EOSP_NOTIFICATION = 0x9e,
        REDUCE_TX_POWER_CMD = 0x9f,
@@ -185,6 +188,10 @@ enum {
        BT_COEX_PRIO_TABLE = 0xcc,
        BT_COEX_PROT_ENV = 0xcd,
        BT_PROFILE_NOTIFICATION = 0xce,
+       BT_CONFIG = 0x9b,
+       BT_COEX_UPDATE_SW_BOOST = 0x5a,
+       BT_COEX_UPDATE_CORUN_LUT = 0x5b,
+       BT_COEX_UPDATE_REDUCED_TXP = 0x5c,
        BT_COEX_CI = 0x5d,
 
        REPLY_SF_CFG_CMD = 0xd1,
@@ -534,6 +541,9 @@ enum iwl_time_event_type {
        /* WiDi Sync Events */
        TE_WIDI_TX_SYNC,
 
+       /* Channel Switch NoA */
+       TE_P2P_GO_CSA_NOA,
+
        TE_MAX
 }; /* MAC_EVENT_TYPE_API_E_VER_1 */
 
@@ -901,6 +911,72 @@ struct iwl_phy_context_cmd {
        __le32 dsp_cfg_flags;
 } __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
 
+/*
+ * Aux ROC command
+ *
+ * Command requests the firmware to create a time event for a certain duration
+ * and remain on the given channel. This is done by using the Aux framework in
+ * the FW.
+ * The command was first used for Hot Spot issues - but can be used regardless
+ * to Hot Spot.
+ *
+ * ( HOT_SPOT_CMD 0x53 )
+ *
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, one of FW_CTXT_ACTION_*
+ * @event_unique_id: If the action FW_CTXT_ACTION_REMOVE then the
+ *     event_unique_id should be the id of the time event assigned by ucode.
+ *     Otherwise ignore the event_unique_id.
+ * @sta_id_and_color: station id and color, resumed during "Remain On Channel"
+ *     activity.
+ * @channel_info: channel info
+ * @node_addr: Our MAC Address
+ * @reserved: reserved for alignment
+ * @apply_time: GP2 value to start (should always be the current GP2 value)
+ * @apply_time_max_delay: Maximum apply time delay value in TU. Defines max
+ *     time by which start of the event is allowed to be postponed.
+ * @duration: event duration in TU To calculate event duration:
+ *     timeEventDuration = min(duration, remainingQuota)
+ */
+struct iwl_hs20_roc_req {
+       /* COMMON_INDEX_HDR_API_S_VER_1 hdr */
+       __le32 id_and_color;
+       __le32 action;
+       __le32 event_unique_id;
+       __le32 sta_id_and_color;
+       struct iwl_fw_channel_info channel_info;
+       u8 node_addr[ETH_ALEN];
+       __le16 reserved;
+       __le32 apply_time;
+       __le32 apply_time_max_delay;
+       __le32 duration;
+} __packed; /* HOT_SPOT_CMD_API_S_VER_1 */
+
+/*
+ * values for AUX ROC result values
+ */
+enum iwl_mvm_hot_spot {
+       HOT_SPOT_RSP_STATUS_OK,
+       HOT_SPOT_RSP_STATUS_TOO_MANY_EVENTS,
+       HOT_SPOT_MAX_NUM_OF_SESSIONS,
+};
+
+/*
+ * Aux ROC command response
+ *
+ * In response to iwl_hs20_roc_req the FW sends this command to notify the
+ * driver the uid of the timevent.
+ *
+ * ( HOT_SPOT_CMD 0x53 )
+ *
+ * @event_unique_id: Unique ID of time event assigned by ucode
+ * @status: Return status 0 is success, all the rest used for specific errors
+ */
+struct iwl_hs20_roc_res {
+       __le32 event_unique_id;
+       __le32 status;
+} __packed; /* HOT_SPOT_RSP_API_S_VER_1 */
+
 #define IWL_RX_INFO_PHY_CNT 8
 #define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
 #define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
index 8b79081d4885122d8264bbed3c896150b4e206a9..0e523e28cabfc15b8beb63af3659cdc20d30b401 100644 (file)
@@ -67,6 +67,7 @@
 #include "iwl-prph.h"
 #include "fw-api.h"
 #include "mvm.h"
+#include "time-event.h"
 
 const u8 iwl_mvm_ac_to_tx_fifo[] = {
        IWL_MVM_TX_FIFO_VO,
@@ -903,7 +904,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
        struct iwl_mac_beacon_cmd beacon_cmd = {};
        struct ieee80211_tx_info *info;
        u32 beacon_skb_len;
-       u32 rate;
+       u32 rate, tx_flags;
 
        if (WARN_ON(!beacon))
                return -EINVAL;
@@ -913,14 +914,17 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
        /* 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);
+       info = IEEE80211_SKB_CB(beacon);
 
        /* 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);
+       tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF;
+       tx_flags |=
+               iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) <<
+                                               TX_CMD_FLG_BT_PRIO_POS;
+       beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags);
 
        mvm->mgmt_last_antenna_idx =
                iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
@@ -930,8 +934,6 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
                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 {
@@ -968,7 +970,7 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
        WARN_ON(vif->type != NL80211_IFTYPE_AP &&
                vif->type != NL80211_IFTYPE_ADHOC);
 
-       beacon = ieee80211_beacon_get(mvm->hw, vif);
+       beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL);
        if (!beacon)
                return -ENOMEM;
 
@@ -1210,31 +1212,94 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        return 0;
 }
 
+static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *csa_vif, u32 gp2)
+{
+       struct iwl_mvm_vif *mvmvif =
+                       iwl_mvm_vif_from_mac80211(csa_vif);
+
+       if (!ieee80211_csa_is_complete(csa_vif)) {
+               int c = ieee80211_csa_update_counter(csa_vif);
+
+               iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
+               if (csa_vif->p2p &&
+                   !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) {
+                       u32 rel_time = (c + 1) *
+                                      csa_vif->bss_conf.beacon_int -
+                                      IWL_MVM_CHANNEL_SWITCH_TIME;
+                       u32 apply_time = gp2 + rel_time * 1024;
+
+                       iwl_mvm_schedule_csa_noa(mvm, csa_vif,
+                                                IWL_MVM_CHANNEL_SWITCH_TIME -
+                                                IWL_MVM_CHANNEL_SWITCH_MARGIN,
+                                                apply_time);
+               }
+       } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) {
+               /* we don't have CSA NoA scheduled yet, switch now */
+               ieee80211_csa_finish(csa_vif);
+               RCU_INIT_POINTER(mvm->csa_vif, NULL);
+       }
+}
+
 int iwl_mvm_rx_beacon_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_beacon_notif *beacon = (void *)pkt->data;
-       u16 status __maybe_unused =
-               le16_to_cpu(beacon->beacon_notify_hdr.status.status);
-       u32 rate __maybe_unused =
-               le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
+       struct iwl_mvm_tx_resp *beacon_notify_hdr;
+       struct ieee80211_vif *csa_vif;
+       struct ieee80211_vif *tx_blocked_vif;
+       u64 tsf;
 
        lockdep_assert_held(&mvm->mutex);
 
-       IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
-                    status & TX_STATUS_MSK,
-                    beacon->beacon_notify_hdr.failure_frame,
-                    le64_to_cpu(beacon->tsf),
-                    rate);
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_CAPA_EXTENDED_BEACON) {
+               struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
 
-       if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) {
-               if (!ieee80211_csa_is_complete(mvm->csa_vif)) {
-                       iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif);
-               } else {
-                       ieee80211_csa_finish(mvm->csa_vif);
-                       mvm->csa_vif = NULL;
+               beacon_notify_hdr = &beacon->beacon_notify_hdr;
+               tsf = le64_to_cpu(beacon->tsf);
+               mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
+       } else {
+               struct iwl_beacon_notif *beacon = (void *)pkt->data;
+
+               beacon_notify_hdr = &beacon->beacon_notify_hdr;
+               tsf = le64_to_cpu(beacon->tsf);
+       }
+
+       IWL_DEBUG_RX(mvm,
+                    "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
+                    le16_to_cpu(beacon_notify_hdr->status.status) &
+                                                               TX_STATUS_MSK,
+                    beacon_notify_hdr->failure_frame, tsf,
+                    mvm->ap_last_beacon_gp2,
+                    le32_to_cpu(beacon_notify_hdr->initial_rate));
+
+       csa_vif = rcu_dereference_protected(mvm->csa_vif,
+                                           lockdep_is_held(&mvm->mutex));
+       if (unlikely(csa_vif && csa_vif->csa_active))
+               iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
+
+       tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
+                                               lockdep_is_held(&mvm->mutex));
+       if (unlikely(tx_blocked_vif)) {
+               struct iwl_mvm_vif *mvmvif =
+                       iwl_mvm_vif_from_mac80211(tx_blocked_vif);
+
+               /*
+                * The channel switch is started and we have blocked the
+                * stations. If this is the first beacon (the timeout wasn't
+                * set), set the unblock timeout, otherwise countdown
+                */
+               if (!mvm->csa_tx_block_bcn_timeout)
+                       mvm->csa_tx_block_bcn_timeout =
+                               IWL_MVM_CS_UNBLOCK_TX_TIMEOUT;
+               else
+                       mvm->csa_tx_block_bcn_timeout--;
+
+               /* Check if the timeout is expired, and unblock tx */
+               if (mvm->csa_tx_block_bcn_timeout == 0) {
+                       iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
+                       RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
                }
        }
 
index 98556d03c1edabf8e521998d4eec0f4a41a89fdf..0d6a8b768a686fb4630528682635c7ee865ce97c 100644 (file)
@@ -80,6 +80,8 @@
 #include "fw-api-scan.h"
 #include "iwl-phy-db.h"
 #include "testmode.h"
+#include "iwl-fw-error-dump.h"
+#include "iwl-prph.h"
 
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
@@ -209,7 +211,9 @@ void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
                return;
 
        IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
-       WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap));
+       spin_lock_bh(&mvm->refs_lock);
+       mvm->refs[ref_type]++;
+       spin_unlock_bh(&mvm->refs_lock);
        iwl_trans_ref(mvm->trans);
 }
 
@@ -219,26 +223,47 @@ void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
                return;
 
        IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
-       WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap));
+       spin_lock_bh(&mvm->refs_lock);
+       WARN_ON(!mvm->refs[ref_type]--);
+       spin_unlock_bh(&mvm->refs_lock);
        iwl_trans_unref(mvm->trans);
 }
 
-static void
-iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
+static void iwl_mvm_unref_all_except(struct iwl_mvm *mvm,
+                                    enum iwl_mvm_ref_type except_ref)
 {
-       int i;
+       int i, j;
 
        if (!iwl_mvm_is_d0i3_supported(mvm))
                return;
 
-       for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) {
-               if (ref == i)
+       spin_lock_bh(&mvm->refs_lock);
+       for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
+               if (except_ref == i || !mvm->refs[i])
                        continue;
 
-               IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i);
-               clear_bit(i, mvm->ref_bitmap);
-               iwl_trans_unref(mvm->trans);
+               IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d (%d)\n",
+                             i, mvm->refs[i]);
+               for (j = 0; j < mvm->refs[i]; j++)
+                       iwl_trans_unref(mvm->trans);
+               mvm->refs[i] = 0;
+       }
+       spin_unlock_bh(&mvm->refs_lock);
+}
+
+int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+       iwl_mvm_ref(mvm, ref_type);
+
+       if (!wait_event_timeout(mvm->d0i3_exit_waitq,
+                               !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status),
+                               HZ)) {
+               WARN_ON_ONCE(1);
+               iwl_mvm_unref(mvm, ref_type);
+               return -EIO;
        }
+
+       return 0;
 }
 
 static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
@@ -276,6 +301,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TIMING_BEACON_ONLY |
                    IEEE80211_HW_CONNECTION_MONITOR |
+                   IEEE80211_HW_CHANCTX_STA_CSA |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
                    IEEE80211_HW_SUPPORTS_STATIC_SMPS;
 
@@ -303,6 +329,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
        }
 
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
        hw->chanctx_data_size = sizeof(u16);
@@ -374,6 +403,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
 
        hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
+                              NL80211_FEATURE_LOW_PRIORITY_SCAN |
                               NL80211_FEATURE_P2P_GO_OPPPS;
 
        mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
@@ -549,9 +579,6 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
        case IEEE80211_AMPDU_TX_STOP_FLUSH:
        case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
        case IEEE80211_AMPDU_TX_OPERATIONAL:
-               iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
-               tx_agg_ref = true;
-
                /*
                 * for tx start, wait synchronously until D0i3 exit to
                 * get the correct sequence number for the tid.
@@ -560,12 +587,11 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                 * by the trans layer (unlike commands), so wait for
                 * d0i3 exit in these cases as well.
                 */
-               if (!wait_event_timeout(mvm->d0i3_exit_waitq,
-                         !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
-                       WARN_ON_ONCE(1);
-                       iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
-                       return -EIO;
-               }
+               ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG);
+               if (ret)
+                       return ret;
+
+               tx_agg_ref = true;
                break;
        default:
                break;
@@ -635,8 +661,106 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
        spin_unlock_bh(&mvm->time_event_lock);
 
        mvmvif->phy_ctxt = NULL;
+       memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
 }
 
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
+{
+       struct iwl_fw_error_dump_file *dump_file;
+       struct iwl_fw_error_dump_data *dump_data;
+       struct iwl_fw_error_dump_info *dump_info;
+       struct iwl_mvm_dump_ptrs *fw_error_dump;
+       const struct fw_img *img;
+       u32 sram_len, sram_ofs;
+       u32 file_len, rxf_len;
+       unsigned long flags;
+       int reg_val;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (mvm->fw_error_dump)
+               return;
+
+       fw_error_dump = kzalloc(sizeof(*mvm->fw_error_dump), GFP_KERNEL);
+       if (!fw_error_dump)
+               return;
+
+       img = &mvm->fw->img[mvm->cur_ucode];
+       sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+       sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+       /* reading buffer size */
+       reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
+       rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
+
+       /* the register holds the value divided by 128 */
+       rxf_len = rxf_len << 7;
+
+       file_len = sizeof(*dump_file) +
+                  sizeof(*dump_data) * 3 +
+                  sram_len +
+                  rxf_len +
+                  sizeof(*dump_info);
+
+       dump_file = vzalloc(file_len);
+       if (!dump_file) {
+               kfree(fw_error_dump);
+               return;
+       }
+
+       fw_error_dump->op_mode_ptr = dump_file;
+
+       dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
+       dump_data = (void *)dump_file->data;
+
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
+       dump_data->len = cpu_to_le32(sizeof(*dump_info));
+       dump_info = (void *) dump_data->data;
+       dump_info->device_family =
+               mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
+                       cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
+                       cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
+       memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
+              sizeof(dump_info->fw_human_readable));
+       strncpy(dump_info->dev_human_readable, mvm->cfg->name,
+               sizeof(dump_info->dev_human_readable));
+       strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
+               sizeof(dump_info->bus_human_readable));
+
+       dump_data = iwl_fw_error_next_data(dump_data);
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+       dump_data->len = cpu_to_le32(rxf_len);
+
+       if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
+               u32 *rxf = (void *)dump_data->data;
+               int i;
+
+               for (i = 0; i < (rxf_len / sizeof(u32)); i++) {
+                       iwl_trans_write_prph(mvm->trans,
+                                            RXF_LD_FENCE_OFFSET_ADDR,
+                                            i * sizeof(u32));
+                       rxf[i] = iwl_trans_read_prph(mvm->trans,
+                                                    RXF_FIFO_RD_FENCE_ADDR);
+               }
+               iwl_trans_release_nic_access(mvm->trans, &flags);
+       }
+
+       dump_data = iwl_fw_error_next_data(dump_data);
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
+       dump_data->len = cpu_to_le32(sram_len);
+       iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
+                                sram_len);
+
+       fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans);
+       fw_error_dump->op_mode_len = file_len;
+       if (fw_error_dump->trans_ptr)
+               file_len += fw_error_dump->trans_ptr->len;
+       dump_file->file_len = cpu_to_le32(file_len);
+       mvm->fw_error_dump = fw_error_dump;
+}
+#endif
+
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -665,6 +789,12 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
        iwl_mvm_reset_phy_ctxts(mvm);
        memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
        memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
+       memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
+       memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
+       memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
+       memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
+       memset(&mvm->bt_ack_kill_msk, 0, sizeof(mvm->bt_ack_kill_msk));
+       memset(&mvm->bt_cts_kill_msk, 0, sizeof(mvm->bt_cts_kill_msk));
 
        ieee80211_wake_queues(mvm->hw);
 
@@ -688,6 +818,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
                iwl_mvm_restart_cleanup(mvm);
 
        ret = iwl_mvm_up(mvm);
+
+       if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               /* Something went wrong - we need to finish some cleanup
+                * that normally iwl_mvm_mac_restart_complete() below
+                * would do.
+                */
+               clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+               iwl_mvm_d0i3_enable_tx(mvm, NULL);
+       }
+
        mutex_unlock(&mvm->mutex);
 
        return ret;
@@ -785,6 +925,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
+       /*
+        * make sure D0i3 exit is completed, otherwise a target access
+        * during tx queue configuration could be done when still in
+        * D0i3 state.
+        */
+       ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF);
+       if (ret)
+               return ret;
+
        /*
         * Not much to do here. The stack will not allow interface
         * types or combinations that we didn't advertise, so we
@@ -899,6 +1048,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
  out_unlock:
        mutex_unlock(&mvm->mutex);
 
+       iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF);
+
        return ret;
 }
 
@@ -1255,6 +1406,28 @@ static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
 }
 #endif
 
+static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (!sta || IS_ERR(sta) || !sta->tdls)
+                       continue;
+
+               mvmsta = iwl_mvm_sta_from_mac80211(sta);
+               ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
+                               NL80211_TDLS_TEARDOWN,
+                               WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
+                               GFP_KERNEL);
+       }
+}
+
 static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                             struct ieee80211_vif *vif,
                                             struct ieee80211_bss_conf *bss_conf,
@@ -1278,7 +1451,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        if (changes & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
                        /* add quota for this interface */
-                       ret = iwl_mvm_update_quotas(mvm, vif);
+                       ret = iwl_mvm_update_quotas(mvm, NULL);
                        if (ret) {
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
@@ -1350,14 +1523,18 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                 */
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
-               iwl_mvm_sf_update(mvm, vif, false);
-               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
        } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
                              BSS_CHANGED_QOS)) {
                ret = iwl_mvm_power_update_mac(mvm);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
        }
+
+       if (changes & BSS_CHANGED_BEACON_INFO) {
+               iwl_mvm_sf_update(mvm, vif, false);
+               WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
+       }
+
        if (changes & BSS_CHANGED_TXPOWER) {
                IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
                                bss_conf->txpower);
@@ -1389,6 +1566,14 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
+       /*
+        * iwl_mvm_mac_ctxt_add() might read directly from the device
+        * (the system time), so make sure it is available.
+        */
+       ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_START_AP);
+       if (ret)
+               return ret;
+
        mutex_lock(&mvm->mutex);
 
        /* Send the beacon template */
@@ -1425,7 +1610,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        /* power updated needs to be done before quotas */
        iwl_mvm_power_update_mac(mvm);
 
-       ret = iwl_mvm_update_quotas(mvm, vif);
+       ret = iwl_mvm_update_quotas(mvm, NULL);
        if (ret)
                goto out_quota_failed;
 
@@ -1437,6 +1622,10 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
 
        iwl_mvm_bt_coex_vif_change(mvm);
 
+       /* we don't support TDLS during DCM */
+       if (iwl_mvm_phy_ctx_count(mvm) > 1)
+               iwl_mvm_teardown_tdls_peers(mvm);
+
        mutex_unlock(&mvm->mutex);
        return 0;
 
@@ -1450,6 +1639,7 @@ out_remove:
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 out_unlock:
        mutex_unlock(&mvm->mutex);
+       iwl_mvm_unref(mvm, IWL_MVM_REF_START_AP);
        return ret;
 }
 
@@ -1463,7 +1653,20 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       /* Handle AP stop while in CSA */
+       if (rcu_access_pointer(mvm->csa_vif) == vif) {
+               iwl_mvm_remove_time_event(mvm, mvmvif,
+                                         &mvmvif->time_event_data);
+               RCU_INIT_POINTER(mvm->csa_vif, NULL);
+       }
+
+       if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
+               RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
+               mvm->csa_tx_block_bcn_timeout = 0;
+       }
+
        mvmvif->ap_ibss_active = false;
+       mvm->ap_last_beacon_gp2 = 0;
 
        iwl_mvm_bt_coex_vif_change(mvm);
 
@@ -1514,10 +1717,18 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
+       /*
+        * iwl_mvm_bss_info_changed_station() might call
+        * iwl_mvm_protect_session(), which reads directly from
+        * the device (the system time), so make sure it is available.
+        */
+       if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_BSS_CHANGED))
+               return;
+
        mutex_lock(&mvm->mutex);
 
        if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
-               iwl_mvm_sched_scan_stop(mvm, true);
+               iwl_mvm_scan_offload_stop(mvm, true);
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
@@ -1533,44 +1744,84 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
        }
 
        mutex_unlock(&mvm->mutex);
+       iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED);
 }
 
+static int iwl_mvm_cancel_scan_wait_notif(struct iwl_mvm *mvm,
+                                         enum iwl_scan_status scan_type)
+{
+       int ret;
+       bool wait_for_handlers = false;
+
+       mutex_lock(&mvm->mutex);
+
+       if (mvm->scan_status != scan_type) {
+               ret = 0;
+               /* make sure there are no pending notifications */
+               wait_for_handlers = true;
+               goto out;
+       }
+
+       switch (scan_type) {
+       case IWL_MVM_SCAN_SCHED:
+               ret = iwl_mvm_scan_offload_stop(mvm, true);
+               break;
+       case IWL_MVM_SCAN_OS:
+               ret = iwl_mvm_cancel_scan(mvm);
+               break;
+       case IWL_MVM_SCAN_NONE:
+       default:
+               WARN_ON_ONCE(1);
+               ret = -EINVAL;
+               break;
+       }
+       if (ret)
+               goto out;
+
+       wait_for_handlers = true;
+out:
+       mutex_unlock(&mvm->mutex);
+
+       /* make sure we consume the completion notification */
+       if (wait_for_handlers)
+               iwl_mvm_wait_for_async_handlers(mvm);
+
+       return ret;
+}
 static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
-                              struct cfg80211_scan_request *req)
+                              struct ieee80211_scan_request *hw_req)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct cfg80211_scan_request *req = &hw_req->req;
        int ret;
 
-       if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
+       if (req->n_channels == 0 ||
+           req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
                return -EINVAL;
 
+       ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
+       if (ret)
+               return ret;
+
        mutex_lock(&mvm->mutex);
 
-       switch (mvm->scan_status) {
-       case IWL_MVM_SCAN_SCHED:
-               ret = iwl_mvm_sched_scan_stop(mvm, true);
-               if (ret) {
-                       ret = -EBUSY;
-                       goto out;
-               }
-               break;
-       case IWL_MVM_SCAN_NONE:
-               break;
-       default:
+       if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
                ret = -EBUSY;
                goto out;
        }
 
        iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
 
-       ret = iwl_mvm_scan_request(mvm, vif, req);
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
+       else
+               ret = iwl_mvm_scan_request(mvm, vif, req);
+
        if (ret)
                iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
 out:
        mutex_unlock(&mvm->mutex);
-       /* make sure to flush the Rx handler before the next scan arrives */
-       iwl_mvm_wait_for_async_handlers(mvm);
        return ret;
 }
 
@@ -1680,6 +1931,48 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
        mutex_unlock(&mvm->mutex);
 }
 
+int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvmsta;
+       int count = 0;
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (!sta || IS_ERR(sta) || !sta->tdls)
+                       continue;
+
+               if (vif) {
+                       mvmsta = iwl_mvm_sta_from_mac80211(sta);
+                       if (mvmsta->vif != vif)
+                               continue;
+               }
+
+               count++;
+       }
+
+       return count;
+}
+
+static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif,
+                                     bool sta_added)
+{
+       int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
+
+       /*
+        * Disable ps when the first TDLS sta is added and re-enable it
+        * when the last TDLS sta is removed
+        */
+       if ((tdls_sta_cnt == 1 && sta_added) ||
+           (tdls_sta_cnt == 0 && !sta_added))
+               iwl_mvm_power_update_mac(mvm);
+}
+
 static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_sta *sta,
@@ -1718,7 +2011,20 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                        ret = -EINVAL;
                        goto out_unlock;
                }
+
+               if (sta->tdls &&
+                   (vif->p2p ||
+                    iwl_mvm_tdls_sta_count(mvm, NULL) ==
+                                               IWL_MVM_TDLS_STA_COUNT ||
+                    iwl_mvm_phy_ctx_count(mvm) > 1)) {
+                       IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
+                       ret = -EBUSY;
+                       goto out_unlock;
+               }
+
                ret = iwl_mvm_add_sta(mvm, vif, sta);
+               if (sta->tdls && ret == 0)
+                       iwl_mvm_recalc_tdls_state(mvm, vif, true);
        } else if (old_state == IEEE80211_STA_NONE &&
                   new_state == IEEE80211_STA_AUTH) {
                /*
@@ -1736,6 +2042,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                             true);
        } else if (old_state == IEEE80211_STA_ASSOC &&
                   new_state == IEEE80211_STA_AUTHORIZED) {
+
+               /* we don't support TDLS during DCM */
+               if (iwl_mvm_phy_ctx_count(mvm) > 1)
+                       iwl_mvm_teardown_tdls_peers(mvm);
+
                /* enable beacon filtering */
                WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
                ret = 0;
@@ -1753,6 +2064,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
        } else if (old_state == IEEE80211_STA_NONE &&
                   new_state == IEEE80211_STA_NOTEXIST) {
                ret = iwl_mvm_rm_sta(mvm, vif, sta);
+               if (sta->tdls)
+                       iwl_mvm_recalc_tdls_state(mvm, vif, false);
        } else {
                ret = -EIO;
        }
@@ -1818,20 +2131,54 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
        if (WARN_ON_ONCE(vif->bss_conf.assoc))
                return;
 
+       /*
+        * iwl_mvm_protect_session() reads directly from the device
+        * (the system time), so make sure it is available.
+        */
+       if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PREPARE_TX))
+               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, 500);
        mutex_unlock(&mvm->mutex);
+
+       iwl_mvm_unref(mvm, IWL_MVM_REF_PREPARE_TX);
+}
+
+static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
+                                                 struct ieee80211_vif *vif)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int;
+
+       /*
+        * iwl_mvm_protect_session() reads directly from the device
+        * (the system time), so make sure it is available.
+        */
+       if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_TDLS))
+               return;
+
+       mutex_lock(&mvm->mutex);
+       /* Protect the session to hear the TDLS setup response on the channel */
+       iwl_mvm_protect_session(mvm, vif, duration, duration, 100);
+       mutex_unlock(&mvm->mutex);
+
+       iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS);
 }
 
 static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
                                        struct ieee80211_vif *vif,
                                        struct cfg80211_sched_scan_request *req,
-                                       struct ieee80211_sched_scan_ies *ies)
+                                       struct ieee80211_scan_ies *ies)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
 
+       ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
+       if (ret)
+               return ret;
+
        mutex_lock(&mvm->mutex);
 
        if (!iwl_mvm_is_idle(mvm)) {
@@ -1839,49 +2186,34 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
                goto out;
        }
 
-       switch (mvm->scan_status) {
-       case IWL_MVM_SCAN_OS:
-               IWL_DEBUG_SCAN(mvm, "Stopping previous scan for sched_scan\n");
-               ret = iwl_mvm_cancel_scan(mvm);
-               if (ret) {
-                       ret = -EBUSY;
-                       goto out;
-               }
-
-               /*
-                * iwl_mvm_rx_scan_complete() will be called soon but will
-                * not reset the scan status as it won't be IWL_MVM_SCAN_OS
-                * any more since we queue the next scan immediately (below).
-                * We make sure it is called before the next scan starts by
-                * flushing the async-handlers work.
-                */
-               break;
-       case IWL_MVM_SCAN_NONE:
-               break;
-       default:
+       if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
                ret = -EBUSY;
                goto out;
        }
 
        mvm->scan_status = IWL_MVM_SCAN_SCHED;
 
-       ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
-       if (ret)
-               goto err;
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+               ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
+               if (ret)
+                       goto err;
+       }
 
        ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
        if (ret)
                goto err;
 
-       ret = iwl_mvm_sched_scan_start(mvm, req);
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
+       else
+               ret = iwl_mvm_sched_scan_start(mvm, req);
+
        if (!ret)
                goto out;
 err:
        mvm->scan_status = IWL_MVM_SCAN_NONE;
 out:
        mutex_unlock(&mvm->mutex);
-       /* make sure to flush the Rx handler before the next scan arrives */
-       iwl_mvm_wait_for_async_handlers(mvm);
        return ret;
 }
 
@@ -1892,7 +2224,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
        int ret;
 
        mutex_lock(&mvm->mutex);
-       ret = iwl_mvm_sched_scan_stop(mvm, false);
+       ret = iwl_mvm_scan_offload_stop(mvm, false);
        mutex_unlock(&mvm->mutex);
        iwl_mvm_wait_for_async_handlers(mvm);
 
@@ -2001,6 +2333,119 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw,
 }
 
 
+static bool iwl_mvm_rx_aux_roc(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_hs20_roc_res *resp;
+       int resp_len = iwl_rx_packet_payload_len(pkt);
+       struct iwl_mvm_time_event_data *te_data = data;
+
+       if (WARN_ON(pkt->hdr.cmd != HOT_SPOT_CMD))
+               return true;
+
+       if (WARN_ON_ONCE(resp_len != sizeof(*resp))) {
+               IWL_ERR(mvm, "Invalid HOT_SPOT_CMD response\n");
+               return true;
+       }
+
+       resp = (void *)pkt->data;
+
+       IWL_DEBUG_TE(mvm,
+                    "Aux ROC: Recieved response from ucode: status=%d uid=%d\n",
+                    resp->status, resp->event_unique_id);
+
+       te_data->uid = le32_to_cpu(resp->event_unique_id);
+       IWL_DEBUG_TE(mvm, "TIME_EVENT_CMD response - UID = 0x%x\n",
+                    te_data->uid);
+
+       spin_lock_bh(&mvm->time_event_lock);
+       list_add_tail(&te_data->list, &mvm->aux_roc_te_list);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       return true;
+}
+
+#define AUX_ROC_MAX_DELAY_ON_CHANNEL 5000
+static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
+                                   struct ieee80211_channel *channel,
+                                   struct ieee80211_vif *vif,
+                                   int duration)
+{
+       int res, time_reg = DEVICE_SYSTEM_TIME_REG;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->hs_time_event_data;
+       static const u8 time_event_response[] = { HOT_SPOT_CMD };
+       struct iwl_notification_wait wait_time_event;
+       struct iwl_hs20_roc_req aux_roc_req = {
+               .action = cpu_to_le32(FW_CTXT_ACTION_ADD),
+               .id_and_color =
+                       cpu_to_le32(FW_CMD_ID_AND_COLOR(MAC_INDEX_AUX, 0)),
+               .sta_id_and_color = cpu_to_le32(mvm->aux_sta.sta_id),
+               /* Set the channel info data */
+               .channel_info.band = (channel->band == IEEE80211_BAND_2GHZ) ?
+                       PHY_BAND_24 : PHY_BAND_5,
+               .channel_info.channel = channel->hw_value,
+               .channel_info.width = PHY_VHT_CHANNEL_MODE20,
+               /* Set the time and duration */
+               .apply_time = cpu_to_le32(iwl_read_prph(mvm->trans, time_reg)),
+               .apply_time_max_delay =
+                       cpu_to_le32(MSEC_TO_TU(AUX_ROC_MAX_DELAY_ON_CHANNEL)),
+               .duration = cpu_to_le32(MSEC_TO_TU(duration)),
+        };
+
+       /* Set the node address */
+       memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
+
+       te_data->vif = vif;
+       te_data->duration = duration;
+       te_data->id = HOT_SPOT_CMD;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       spin_lock_bh(&mvm->time_event_lock);
+       list_add_tail(&te_data->list, &mvm->time_event_list);
+       spin_unlock_bh(&mvm->time_event_lock);
+
+       /*
+        * Use a notification wait, which really just processes the
+        * command response and doesn't wait for anything, in order
+        * to be able to process the response and get the UID inside
+        * the RX path. Using CMD_WANT_SKB doesn't work because it
+        * stores the buffer and then wakes up this thread, by which
+        * time another notification (that the time event started)
+        * might already be processed unsuccessfully.
+        */
+       iwl_init_notification_wait(&mvm->notif_wait, &wait_time_event,
+                                  time_event_response,
+                                  ARRAY_SIZE(time_event_response),
+                                  iwl_mvm_rx_aux_roc, te_data);
+
+       res = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0, sizeof(aux_roc_req),
+                                  &aux_roc_req);
+
+       if (res) {
+               IWL_ERR(mvm, "Couldn't send HOT_SPOT_CMD: %d\n", res);
+               iwl_remove_notification(&mvm->notif_wait, &wait_time_event);
+               goto out_clear_te;
+       }
+
+       /* No need to wait for anything, so just pass 1 (0 isn't valid) */
+       res = iwl_wait_notification(&mvm->notif_wait, &wait_time_event, 1);
+       /* should never fail */
+       WARN_ON_ONCE(res);
+
+       if (res) {
+ out_clear_te:
+               spin_lock_bh(&mvm->time_event_lock);
+               iwl_mvm_te_clear_data(mvm, te_data);
+               spin_unlock_bh(&mvm->time_event_lock);
+       }
+
+       return res;
+}
+
 static int iwl_mvm_roc(struct ieee80211_hw *hw,
                       struct ieee80211_vif *vif,
                       struct ieee80211_channel *channel,
@@ -2016,8 +2461,17 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
        IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
                           duration, type);
 
-       if (vif->type != NL80211_IFTYPE_P2P_DEVICE) {
-               IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type);
+       switch (vif->type) {
+       case NL80211_IFTYPE_STATION:
+               /* Use aux roc framework (HS20) */
+               ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
+                                              vif, duration);
+               return ret;
+       case NL80211_IFTYPE_P2P_DEVICE:
+               /* handle below */
+               break;
+       default:
+               IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type);
                return -EINVAL;
        }
 
@@ -2126,17 +2580,17 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
        return 0;
 }
 
-static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
-                              struct ieee80211_chanctx_conf *ctx)
+static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
+                                struct ieee80211_chanctx_conf *ctx)
 {
-       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
        struct iwl_mvm_phy_ctxt *phy_ctxt;
        int ret;
 
+       lockdep_assert_held(&mvm->mutex);
+
        IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
 
-       mutex_lock(&mvm->mutex);
        phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
        if (!phy_ctxt) {
                ret = -ENOSPC;
@@ -2154,19 +2608,40 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
        iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
        *phy_ctxt_id = phy_ctxt->id;
 out:
+       return ret;
+}
+
+static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
+                              struct ieee80211_chanctx_conf *ctx)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       mutex_lock(&mvm->mutex);
+       ret = __iwl_mvm_add_chanctx(mvm, ctx);
        mutex_unlock(&mvm->mutex);
+
        return ret;
 }
 
+static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
+                                    struct ieee80211_chanctx_conf *ctx)
+{
+       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+
+       lockdep_assert_held(&mvm->mutex);
+
+       iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
+}
+
 static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
                                   struct ieee80211_chanctx_conf *ctx)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
-       struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
+       __iwl_mvm_remove_chanctx(mvm, ctx);
        mutex_unlock(&mvm->mutex);
 }
 
@@ -2195,17 +2670,17 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
        mutex_unlock(&mvm->mutex);
 }
 
-static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
-                                     struct ieee80211_vif *vif,
-                                     struct ieee80211_chanctx_conf *ctx)
+static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_chanctx_conf *ctx,
+                                       bool switching_chanctx)
 {
-       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
        struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       mutex_lock(&mvm->mutex);
+       lockdep_assert_held(&mvm->mutex);
 
        mvmvif->phy_ctxt = phy_ctxt;
 
@@ -2222,18 +2697,18 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
                 * (in bss_info_changed), similarly for IBSS.
                 */
                ret = 0;
-               goto out_unlock;
+               goto out;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_MONITOR:
                break;
        default:
                ret = -EINVAL;
-               goto out_unlock;
+               goto out;
        }
 
        ret = iwl_mvm_binding_add_vif(mvm, vif);
        if (ret)
-               goto out_unlock;
+               goto out;
 
        /*
         * Power state must be updated before quotas,
@@ -2247,67 +2722,168 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
         */
        if (vif->type == NL80211_IFTYPE_MONITOR) {
                mvmvif->monitor_active = true;
-               ret = iwl_mvm_update_quotas(mvm, vif);
+               ret = iwl_mvm_update_quotas(mvm, NULL);
                if (ret)
                        goto out_remove_binding;
        }
 
        /* Handle binding during CSA */
-       if (vif->type == NL80211_IFTYPE_AP) {
-               iwl_mvm_update_quotas(mvm, vif);
+       if ((vif->type == NL80211_IFTYPE_AP) ||
+           (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
+               iwl_mvm_update_quotas(mvm, NULL);
                iwl_mvm_mac_ctxt_changed(mvm, vif, false);
        }
 
-       goto out_unlock;
+       goto out;
 
- out_remove_binding:
+out_remove_binding:
        iwl_mvm_binding_remove_vif(mvm, vif);
        iwl_mvm_power_update_mac(mvm);
- out_unlock:
-       mutex_unlock(&mvm->mutex);
+out:
        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)
+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_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
 
        mutex_lock(&mvm->mutex);
+       ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false);
+       mutex_unlock(&mvm->mutex);
+
+       return ret;
+}
+
+static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
+                                          struct ieee80211_vif *vif,
+                                          struct ieee80211_chanctx_conf *ctx,
+                                          bool switching_chanctx)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_vif *disabled_vif = NULL;
+
+       lockdep_assert_held(&mvm->mutex);
 
        iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
 
        switch (vif->type) {
        case NL80211_IFTYPE_ADHOC:
-               goto out_unlock;
+               goto out;
        case NL80211_IFTYPE_MONITOR:
                mvmvif->monitor_active = false;
-               iwl_mvm_update_quotas(mvm, NULL);
                break;
        case NL80211_IFTYPE_AP:
                /* This part is triggered only during CSA */
                if (!vif->csa_active || !mvmvif->ap_ibss_active)
-                       goto out_unlock;
+                       goto out;
+
+               /* Set CS bit on all the stations */
+               iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
+
+               /* Save blocked iface, the timeout is set on the next beacon */
+               rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
 
                mvmvif->ap_ibss_active = false;
-               iwl_mvm_update_quotas(mvm, NULL);
-               /*TODO: bt_coex notification here? */
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (!switching_chanctx)
+                       break;
+
+               disabled_vif = vif;
+
+               iwl_mvm_mac_ctxt_changed(mvm, vif, true);
+               break;
        default:
                break;
        }
 
+       iwl_mvm_update_quotas(mvm, disabled_vif);
        iwl_mvm_binding_remove_vif(mvm, vif);
 
-out_unlock:
+out:
        mvmvif->phy_ctxt = NULL;
        iwl_mvm_power_update_mac(mvm);
+}
+
+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);
+
+       mutex_lock(&mvm->mutex);
+       __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false);
        mutex_unlock(&mvm->mutex);
 }
 
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif_chanctx_switch *vifs,
+                                     int n_vifs,
+                                     enum ieee80211_chanctx_switch_mode mode)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       /* we only support SWAP_CONTEXTS and with a single-vif right now */
+       if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&mvm->mutex);
+       __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+       __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
+
+       ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx);
+       if (ret) {
+               IWL_ERR(mvm, "failed to add new_ctx during channel switch\n");
+               goto out_reassign;
+       }
+
+       ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
+                                          true);
+       if (ret) {
+               IWL_ERR(mvm,
+                       "failed to assign new_ctx during channel switch\n");
+               goto out_remove;
+       }
+
+       /* we don't support TDLS during DCM - can be caused by channel switch */
+       if (iwl_mvm_phy_ctx_count(mvm) > 1)
+               iwl_mvm_teardown_tdls_peers(mvm);
+
+       goto out;
+
+out_remove:
+       __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
+
+out_reassign:
+       ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx);
+       if (ret) {
+               IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
+               goto out_restart;
+       }
+
+       ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+                                          true);
+       if (ret) {
+               IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+               goto out_restart;
+       }
+
+       goto out;
+
+out_restart:
+       /* things keep failing, better restart the hw */
+       iwl_mvm_nic_restart(mvm, false);
+
+out:
+       mutex_unlock(&mvm->mutex);
+       return ret;
+}
+
 static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
                           struct ieee80211_sta *sta,
                           bool set)
@@ -2395,15 +2971,19 @@ static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
                                          struct cfg80211_chan_def *chandef)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct ieee80211_vif *csa_vif;
 
        mutex_lock(&mvm->mutex);
-       if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active,
+
+       csa_vif = rcu_dereference_protected(mvm->csa_vif,
+                                           lockdep_is_held(&mvm->mutex));
+       if (WARN(csa_vif && csa_vif->csa_active,
                 "Another CSA is already in progress"))
                goto out_unlock;
 
        IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
                           chandef->center_freq1);
-       mvm->csa_vif = vif;
+       rcu_assign_pointer(mvm->csa_vif, vif);
 
 out_unlock:
        mutex_unlock(&mvm->mutex);
@@ -2460,6 +3040,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
        .sta_rc_update = iwl_mvm_sta_rc_update,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+       .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
        .flush = iwl_mvm_mac_flush,
        .sched_scan_start = iwl_mvm_mac_sched_scan_start,
        .sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
@@ -2472,6 +3053,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
        .change_chanctx = iwl_mvm_change_chanctx,
        .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
        .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
+       .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx,
 
        .start_ap = iwl_mvm_start_ap_ibss,
        .stop_ap = iwl_mvm_stop_ap_ibss,
index fcc6c29482d0ef516bba48459b09230b9ead4007..2e73d3bd7757605e2483fa00f147086abe32b09c 100644 (file)
 /* RSSI offset for WkP */
 #define IWL_RSSI_OFFSET 50
 #define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
+/* A TimeUnit is 1024 microsecond */
+#define MSEC_TO_TU(_msec)      (_msec*1000/1024)
+
+/*
+ * The CSA NoA is scheduled IWL_MVM_CHANNEL_SWITCH_TIME TUs before "beacon 0"
+ * TBTT. This value should be big enough to ensure that we switch in time.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME 40
+
+/*
+ * This value (in TUs) is used to fine tune the CSA NoA end time which should
+ * be just before "beacon 0" TBTT.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4
+
+/*
+ * Number of beacons to transmit on a new channel until we unblock tx to
+ * the stations, even if we didn't identify them on a new channel
+ */
+#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3
 
 enum iwl_mvm_tx_fifo {
        IWL_MVM_TX_FIFO_BK = 0,
@@ -108,6 +128,21 @@ struct iwl_mvm_mod_params {
 };
 extern struct iwl_mvm_mod_params iwlmvm_mod_params;
 
+/**
+ * struct iwl_mvm_dump_ptrs - set of pointers needed for the fw-error-dump
+ *
+ * @op_mode_ptr: pointer to the buffer coming from the mvm op_mode
+ * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the
+ *     transport's data.
+ * @trans_len: length of the valid data in trans_ptr
+ * @op_mode_len: length of the valid data in op_mode_ptr
+ */
+struct iwl_mvm_dump_ptrs {
+       struct iwl_trans_dump_data *trans_ptr;
+       void *op_mode_ptr;
+       u32 op_mode_len;
+};
+
 struct iwl_mvm_phy_ctxt {
        u16 id;
        u16 color;
@@ -230,11 +265,30 @@ enum iwl_mvm_ref_type {
        IWL_MVM_REF_USER,
        IWL_MVM_REF_TX,
        IWL_MVM_REF_TX_AGG,
+       IWL_MVM_REF_ADD_IF,
+       IWL_MVM_REF_START_AP,
+       IWL_MVM_REF_BSS_CHANGED,
+       IWL_MVM_REF_PREPARE_TX,
+       IWL_MVM_REF_PROTECT_TDLS,
+       IWL_MVM_REF_CHECK_CTKILL,
+       IWL_MVM_REF_PRPH_READ,
+       IWL_MVM_REF_PRPH_WRITE,
+       IWL_MVM_REF_NMI,
+       IWL_MVM_REF_TM_CMD,
        IWL_MVM_REF_EXIT_WORK,
 
        IWL_MVM_REF_COUNT,
 };
 
+enum iwl_bt_force_ant_mode {
+       BT_FORCE_ANT_DIS = 0,
+       BT_FORCE_ANT_AUTO,
+       BT_FORCE_ANT_BT,
+       BT_FORCE_ANT_WIFI,
+
+       BT_FORCE_ANT_MAX,
+};
+
 /**
 * struct iwl_mvm_vif_bf_data - beacon filtering related data
 * @bf_enabled: indicates if beacon filtering is enabled
@@ -299,6 +353,7 @@ struct iwl_mvm_vif {
         */
        struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
        struct iwl_mvm_time_event_data time_event_data;
+       struct iwl_mvm_time_event_data hs_time_event_data;
 
        struct iwl_mvm_int_sta bcast_sta;
 
@@ -523,7 +578,7 @@ struct iwl_mvm {
 
        /* Scan status, cmd (pre-allocated) and auxiliary station */
        enum iwl_scan_status scan_status;
-       struct iwl_scan_cmd *scan_cmd;
+       void *scan_cmd;
        struct iwl_mcast_filter_cmd *mcast_filter_cmd;
 
        /* rx chain antennas set through debugfs for the scan command */
@@ -578,18 +633,15 @@ struct iwl_mvm {
         */
        unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
 
-       /* A bitmap of reference types taken by the driver. */
-       unsigned long ref_bitmap[BITS_TO_LONGS(IWL_MVM_REF_COUNT)];
+       /* references taken by the driver and spinlock protecting them */
+       spinlock_t refs_lock;
+       u8 refs[IWL_MVM_REF_COUNT];
 
        u8 vif_count;
 
        /* -1 for always, 0 for never, >0 for that many times */
        s8 restart_fw;
-       void *fw_error_dump;
-       void *fw_error_sram;
-       u32 fw_error_sram_len;
-       u32 *fw_error_rxf;
-       u32 fw_error_rxf_len;
+       struct iwl_mvm_dump_ptrs *fw_error_dump;
 
 #ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
@@ -623,12 +675,21 @@ struct iwl_mvm {
        wait_queue_head_t d0i3_exit_waitq;
 
        /* BT-Coex */
-       u8 bt_kill_msk;
+       u8 bt_ack_kill_msk[NUM_PHY_CTX];
+       u8 bt_cts_kill_msk[NUM_PHY_CTX];
+
+       struct iwl_bt_coex_profile_notif_old last_bt_notif_old;
+       struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old;
        struct iwl_bt_coex_profile_notif last_bt_notif;
        struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
+
        u32 last_ant_isol;
        u8 last_corun_lut;
        u8 bt_tx_prio;
+       enum iwl_bt_force_ant_mode bt_force_ant_mode;
+
+       /* Aux ROC */
+       struct list_head aux_roc_te_list;
 
        /* Thermal Throttling and CTkill */
        struct iwl_mvm_tt_mgmt thermal_throttle;
@@ -647,7 +708,12 @@ struct iwl_mvm {
        /* Indicate if device power save is allowed */
        bool ps_disabled;
 
-       struct ieee80211_vif *csa_vif;
+       struct ieee80211_vif __rcu *csa_vif;
+       struct ieee80211_vif __rcu *csa_tx_blocked_vif;
+       u8 csa_tx_block_bcn_timeout;
+
+       /* system time of last beacon (for AP/GO interface) */
+       u32 ap_last_beacon_gp2;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -663,6 +729,7 @@ enum iwl_mvm_status {
        IWL_MVM_STATUS_ROC_RUNNING,
        IWL_MVM_STATUS_IN_HW_RESTART,
        IWL_MVM_STATUS_IN_D0I3,
+       IWL_MVM_STATUS_ROC_AUX_RUNNING,
 };
 
 static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
@@ -719,11 +786,6 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
                               struct ieee80211_tx_rate *r);
 u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
 void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
-void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm);
-void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm);
-#endif
 u8 first_antenna(u8 mask);
 u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
 
@@ -809,6 +871,7 @@ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
                          struct iwl_mvm_phy_ctxt *ctxt);
 void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
                            struct iwl_mvm_phy_ctxt *ctxt);
+int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
 
 /* MAC (virtual interface) programming */
 int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -835,7 +898,8 @@ 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);
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+                         struct ieee80211_vif *disabled_vif);
 
 /* Scanning */
 int iwl_mvm_scan_request(struct iwl_mvm *mvm,
@@ -854,15 +918,24 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
 int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
                              struct ieee80211_vif *vif,
                              struct cfg80211_sched_scan_request *req,
-                             struct ieee80211_sched_scan_ies *ies);
+                             struct ieee80211_scan_ies *ies);
 int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
                                       struct cfg80211_sched_scan_request *req);
 int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                             struct cfg80211_sched_scan_request *req);
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify);
-int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
-                                 struct iwl_rx_cmd_buffer *rxb,
-                                 struct iwl_device_cmd *cmd);
+int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify);
+int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb,
+                                   struct iwl_device_cmd *cmd);
+
+/* Unified scan */
+int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_scan_request *req);
+int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   struct cfg80211_sched_scan_request *req,
+                                   struct ieee80211_scan_ies *ies);
 
 /* MVM debugfs */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -948,6 +1021,7 @@ int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 /* D0i3 */
 void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
 void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
 int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
 
@@ -963,19 +1037,40 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
                                struct ieee80211_sta *sta);
 bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
                                     struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm);
 bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
                                    enum ieee80211_band band);
 u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
                           struct ieee80211_tx_info *info, u8 ac);
 
+bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
+void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
+int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
+int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
+                                struct iwl_rx_cmd_buffer *rxb,
+                                struct iwl_device_cmd *cmd);
+void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                              enum ieee80211_rssi_event rssi_event);
+u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
+                                   struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
+                                        struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
+                                       enum ieee80211_band band);
+int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
+                                     struct iwl_rx_cmd_buffer *rxb,
+                                     struct iwl_device_cmd *cmd);
+
 enum iwl_bt_kill_msk {
        BT_KILL_MSK_DEFAULT,
-       BT_KILL_MSK_SCO_HID_A2DP,
-       BT_KILL_MSK_REDUCED_TXPOW,
+       BT_KILL_MSK_NEVER,
+       BT_KILL_MSK_ALWAYS,
        BT_KILL_MSK_MAX,
 };
-extern const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX];
-extern const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX];
+
+extern const u8 iwl_bt_ack_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT];
+extern const u8 iwl_bt_cts_kill_msk[BT_MAX_AG][BT_COEX_MAX_LUT];
+extern const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX];
 
 /* beacon filtering */
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -1039,4 +1134,9 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
 int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                      bool added_vif);
 
+/* TDLS */
+int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
+
 #endif /* __IWL_MVM_H__ */
index 808f78f6fbf9fe478553bd54264f784d98c01597..cfdd314fdd5decd15b57bf02b3ad63eb1ab16cd0 100644 (file)
@@ -69,7 +69,9 @@
 
 /* Default NVM size to read */
 #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
-#define IWL_MAX_NVM_SECTION_SIZE 7000
+#define IWL_MAX_NVM_SECTION_SIZE       0x1b58
+#define IWL_MAX_NVM_8000A_SECTION_SIZE 0xffc
+#define IWL_MAX_NVM_8000B_SECTION_SIZE 0x1ffc
 
 #define NVM_WRITE_OPCODE 1
 #define NVM_READ_OPCODE 0
@@ -219,7 +221,7 @@ static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
  * without overflowing, so no check is needed.
  */
 static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
-                               u8 *data)
+                               u8 *data, u32 size_read)
 {
        u16 length, offset = 0;
        int ret;
@@ -231,6 +233,13 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
 
        /* Read the NVM until exhausted (reading less than requested) */
        while (ret == length) {
+               /* Check no memory assumptions fail and cause an overflow */
+               if ((size_read + offset + length) >
+                   mvm->cfg->base_params->eeprom_size) {
+                       IWL_ERR(mvm, "EEPROM size is too small for NVM\n");
+                       return -ENOBUFS;
+               }
+
                ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
                if (ret < 0) {
                        IWL_DEBUG_EEPROM(mvm->trans->dev,
@@ -256,7 +265,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
        if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) {
                if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
                    !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
-                       IWL_ERR(mvm, "Can't parse empty NVM sections\n");
+                       IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n");
                        return NULL;
                }
        } else {
@@ -264,7 +273,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
                if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
                    !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) {
                        IWL_ERR(mvm,
-                               "Can't parse empty family 8000 NVM sections\n");
+                               "Can't parse empty family 8000 OTP/NVM sections\n");
                        return NULL;
                }
                /* MAC_OVERRIDE or at least HW section must exist */
@@ -326,6 +335,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
                u8 data[];
        } *file_sec;
        const u8 *eof, *temp;
+       int max_section_size;
 
 #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
 #define NVM_WORD2_ID(x) (x >> 12)
@@ -334,6 +344,14 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
 
        IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
 
+       /* Maximal size depends on HW family and step */
+       if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+               max_section_size = IWL_MAX_NVM_SECTION_SIZE;
+       else if ((mvm->trans->hw_rev & 0xc) == 0) /* Family 8000 A-step */
+               max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE;
+       else /* Family 8000 B-step */
+               max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE;
+
        /*
         * Obtain NVM image via request_firmware. Since we already used
         * request_firmware_nowait() for the firmware binary load and only
@@ -392,7 +410,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
                                                le16_to_cpu(file_sec->word1));
                }
 
-               if (section_size > IWL_MAX_NVM_SECTION_SIZE) {
+               if (section_size > max_section_size) {
                        IWL_ERR(mvm, "ERROR - section too large (%d)\n",
                                section_size);
                        ret = -EINVAL;
@@ -459,6 +477,7 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
 int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
 {
        int ret, section;
+       u32 size_read = 0;
        u8 *nvm_buffer, *temp;
 
        if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
@@ -475,9 +494,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
                        return -ENOMEM;
                for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) {
                        /* we override the constness for initial read */
-                       ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
+                       ret = iwl_nvm_read_section(mvm, section, nvm_buffer,
+                                                  size_read);
                        if (ret < 0)
                                continue;
+                       size_read += ret;
                        temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
                        if (!temp) {
                                ret = -ENOMEM;
@@ -509,6 +530,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
                        }
 #endif
                }
+               if (!size_read)
+                       IWL_ERR(mvm, "OTP is blank\n");
                kfree(nvm_buffer);
        }
 
index cc2f7de396deb396d2b20e261b4c2877137dfa3e..610dbcb0dc279d97dda32ded4df2b387a4577333 100644 (file)
@@ -166,8 +166,15 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
        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;
+       /*
+        * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and
+        * shouldn't be set to any non-zero value. The same is supposed to be
+        * true of the other HW, but unsetting them (such as the 7260) causes
+        * automatic tests to fail on seemingly unrelated errors. Need to
+        * further investigate this, but for now we'll separate cases.
+        */
+       if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+               reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
 
        iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
                                CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
@@ -233,7 +240,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
        RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true),
        RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
                   iwl_mvm_rx_scan_offload_complete_notif, true),
-       RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results,
+       RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results,
                   false),
 
        RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
@@ -282,8 +289,10 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(MATCH_FOUND_NOTIFICATION),
        CMD(SCAN_OFFLOAD_REQUEST_CMD),
        CMD(SCAN_OFFLOAD_ABORT_CMD),
+       CMD(HOT_SPOT_CMD),
        CMD(SCAN_OFFLOAD_COMPLETE),
        CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
+       CMD(SCAN_ITERATION_COMPLETE),
        CMD(POWER_TABLE_CMD),
        CMD(WEP_KEY),
        CMD(REPLY_RX_PHY_CMD),
@@ -324,6 +333,9 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(REPLY_THERMAL_MNG_BACKOFF),
        CMD(MAC_PM_POWER_TABLE),
        CMD(BT_COEX_CI),
+       CMD(BT_COEX_UPDATE_SW_BOOST),
+       CMD(BT_COEX_UPDATE_CORUN_LUT),
+       CMD(BT_COEX_UPDATE_REDUCED_TXP),
        CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
        CMD(ANTENNA_COUPLING_NOTIFICATION),
 };
@@ -380,6 +392,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (!hw)
                return NULL;
 
+       if (cfg->max_rx_agg_size)
+               hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size;
+
        op_mode = hw->priv;
        op_mode->ops = &iwl_mvm_ops;
 
@@ -405,6 +420,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        mutex_init(&mvm->d0i3_suspend_mutex);
        spin_lock_init(&mvm->async_handlers_lock);
        INIT_LIST_HEAD(&mvm->time_event_list);
+       INIT_LIST_HEAD(&mvm->aux_roc_te_list);
        INIT_LIST_HEAD(&mvm->async_handlers_list);
        spin_lock_init(&mvm->time_event_lock);
 
@@ -414,6 +430,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
 
        spin_lock_init(&mvm->d0i3_tx_lock);
+       spin_lock_init(&mvm->refs_lock);
        skb_queue_head_init(&mvm->d0i3_tx);
        init_waitqueue_head(&mvm->d0i3_exit_waitq);
 
@@ -502,9 +519,17 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                }
        }
 
-       scan_size = sizeof(struct iwl_scan_cmd) +
-               mvm->fw->ucode_capa.max_probe_length +
-               (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel));
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               scan_size = sizeof(struct iwl_scan_req_unified_lmac) +
+                       sizeof(struct iwl_scan_channel_cfg_lmac) *
+                               mvm->fw->ucode_capa.n_scan_channels +
+                       sizeof(struct iwl_scan_probe_req);
+       else
+               scan_size = sizeof(struct iwl_scan_cmd) +
+                       mvm->fw->ucode_capa.max_probe_length +
+                       mvm->fw->ucode_capa.n_scan_channels *
+                               sizeof(struct iwl_scan_channel);
+
        mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
        if (!mvm->scan_cmd)
                goto out_free;
@@ -520,7 +545,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
 
        /* rpm starts with a taken ref. only set the appropriate bit here. */
-       set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap);
+       mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1;
 
        return op_mode;
 
@@ -548,9 +573,11 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        ieee80211_unregister_hw(mvm->hw);
 
        kfree(mvm->scan_cmd);
-       vfree(mvm->fw_error_dump);
-       kfree(mvm->fw_error_sram);
-       kfree(mvm->fw_error_rxf);
+       if (mvm->fw_error_dump) {
+               vfree(mvm->fw_error_dump->op_mode_ptr);
+               vfree(mvm->fw_error_dump->trans_ptr);
+               kfree(mvm->fw_error_dump);
+       }
        kfree(mvm->mcast_filter_cmd);
        mvm->mcast_filter_cmd = NULL;
 
@@ -754,7 +781,7 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk)
        module_put(THIS_MODULE);
 }
 
-static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
+void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
 {
        iwl_abort_notification_waits(&mvm->notif_wait);
 
@@ -811,93 +838,24 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
                reprobe->dev = mvm->trans->dev;
                INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
                schedule_work(&reprobe->work);
-       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) {
+       } else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
+                  (!fw_error || mvm->restart_fw)) {
                /* don't let the transport/FW power down */
                iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
 
-               if (mvm->restart_fw > 0)
+               if (fw_error && mvm->restart_fw > 0)
                        mvm->restart_fw--;
                ieee80211_restart_hw(mvm->hw);
        }
 }
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
-{
-       struct iwl_fw_error_dump_file *dump_file;
-       struct iwl_fw_error_dump_data *dump_data;
-       u32 file_len;
-       u32 trans_len;
-
-       lockdep_assert_held(&mvm->mutex);
-
-       if (mvm->fw_error_dump)
-               return;
-
-       file_len = mvm->fw_error_sram_len +
-                  mvm->fw_error_rxf_len +
-                  sizeof(*dump_file) +
-                  sizeof(*dump_data) * 2;
-
-       trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
-       if (trans_len)
-               file_len += trans_len;
-
-       dump_file = vmalloc(file_len);
-       if (!dump_file)
-               return;
-
-       mvm->fw_error_dump = dump_file;
-
-       dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
-       dump_file->file_len = cpu_to_le32(file_len);
-       dump_data = (void *)dump_file->data;
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
-       dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len);
-       memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len);
-
-       dump_data = iwl_mvm_fw_error_next_data(dump_data);
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
-       dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
-
-       /*
-        * No need for lock since at the stage the FW isn't loaded. So it
-        * can't assert - we are the only one who can possibly be accessing
-        * mvm->fw_error_sram right now.
-        */
-       memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len);
-
-       kfree(mvm->fw_error_rxf);
-       mvm->fw_error_rxf = NULL;
-       mvm->fw_error_rxf_len = 0;
-
-       kfree(mvm->fw_error_sram);
-       mvm->fw_error_sram = NULL;
-       mvm->fw_error_sram_len = 0;
-
-       if (trans_len) {
-               void *buf = iwl_mvm_fw_error_next_data(dump_data);
-               u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
-                                                        trans_len);
-               dump_data = (void *)((u8 *)buf + real_trans_len);
-               dump_file->file_len =
-                       cpu_to_le32(file_len - trans_len + real_trans_len);
-       }
-}
-#endif
-
 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);
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-       iwl_mvm_fw_error_sram_dump(mvm);
-       iwl_mvm_fw_error_rxf_dump(mvm);
-#endif
-
-       iwl_mvm_nic_restart(mvm);
+       iwl_mvm_nic_restart(mvm, true);
 }
 
 static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
@@ -905,7 +863,7 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
 
        WARN_ON(1);
-       iwl_mvm_nic_restart(mvm);
+       iwl_mvm_nic_restart(mvm, true);
 }
 
 struct iwl_d0i3_iter_data {
index 539f3a942d437565ab6ba9accd06f71874985af4..6cc243f7cf602b8f926baa11d4a59c30db965e0c 100644 (file)
@@ -261,3 +261,29 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
 
        ctxt->ref--;
 }
+
+static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       unsigned long *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (!mvmvif->phy_ctxt)
+               return;
+
+       if (vif->type == NL80211_IFTYPE_STATION ||
+           vif->type == NL80211_IFTYPE_AP)
+               __set_bit(mvmvif->phy_ctxt->id, data);
+}
+
+int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
+{
+       unsigned long phy_ctxt_counter = 0;
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_binding_iterator,
+                                                  &phy_ctxt_counter);
+
+       return hweight8(phy_ctxt_counter);
+}
index c182a8baf685857d3c2857443d53ae978e8646a8..2b2d10800a55e1b90f3f4da4c91bd6cfe09629a8 100644 (file)
@@ -246,30 +246,10 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
                IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
 }
 
-static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
-                                     struct ieee80211_vif *vif)
-{
-       unsigned long *data = _data;
-       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
-       if (!mvmvif->phy_ctxt)
-               return;
-
-       if (vif->type == NL80211_IFTYPE_STATION ||
-           vif->type == NL80211_IFTYPE_AP)
-               __set_bit(mvmvif->phy_ctxt->id, data);
-}
-
 static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
                                       struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       unsigned long phy_ctxt_counter = 0;
-
-       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
-                                                  IEEE80211_IFACE_ITER_NORMAL,
-                                                  iwl_mvm_binding_iterator,
-                                                  &phy_ctxt_counter);
 
        if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
                    ETH_ALEN))
@@ -291,7 +271,7 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
         * Avoid using uAPSD if client is in DCM -
         * low latency issue in Miracast
         */
-       if (hweight8(phy_ctxt_counter) >= 2)
+       if (iwl_mvm_phy_ctx_count(mvm) >= 2)
                return false;
 
        return true;
@@ -503,6 +483,7 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
 }
 
 struct iwl_power_vifs {
+       struct iwl_mvm *mvm;
        struct ieee80211_vif *bf_vif;
        struct ieee80211_vif *bss_vif;
        struct ieee80211_vif *p2p_vif;
@@ -512,6 +493,8 @@ struct iwl_power_vifs {
        bool bss_active;
        bool ap_active;
        bool monitor_active;
+       bool bss_tdls;
+       bool p2p_tdls;
 };
 
 static void iwl_mvm_power_iterator(void *_data, u8 *mac,
@@ -548,6 +531,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac,
                /* only a single MAC of the same type */
                WARN_ON(power_iterator->p2p_vif);
                power_iterator->p2p_vif = vif;
+               power_iterator->p2p_tdls =
+                       !!iwl_mvm_tdls_sta_count(power_iterator->mvm, vif);
                if (mvmvif->phy_ctxt)
                        if (mvmvif->phy_ctxt->id < MAX_PHYS)
                                power_iterator->p2p_active = true;
@@ -557,6 +542,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac,
                /* only a single MAC of the same type */
                WARN_ON(power_iterator->bss_vif);
                power_iterator->bss_vif = vif;
+               power_iterator->bss_tdls =
+                       !!iwl_mvm_tdls_sta_count(power_iterator->mvm, vif);
                if (mvmvif->phy_ctxt)
                        if (mvmvif->phy_ctxt->id < MAX_PHYS)
                                power_iterator->bss_active = true;
@@ -599,13 +586,15 @@ iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
                ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
 
        /* enable PM on bss if bss stand alone */
-       if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
+       if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active &&
+           !vifs->bss_tdls) {
                bss_mvmvif->pm_enabled = true;
                return;
        }
 
        /* enable PM on p2p if p2p stand alone */
-       if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
+       if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active &&
+           !vifs->p2p_tdls) {
                if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
                        p2p_mvmvif->pm_enabled = true;
                return;
@@ -831,7 +820,9 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
 int iwl_mvm_power_update_mac(struct iwl_mvm *mvm)
 {
        struct iwl_mvm_vif *mvmvif;
-       struct iwl_power_vifs vifs = {};
+       struct iwl_power_vifs vifs = {
+               .mvm = mvm,
+       };
        bool ba_enable;
        int ret;
 
index ba68d7b8450508d9c7b123500b654d2195b5613a..4e20b3ce2b6a320e7fd163fe34a02674788645fc 100644 (file)
@@ -73,7 +73,7 @@ struct iwl_mvm_quota_iterator_data {
        int colors[MAX_BINDINGS];
        int low_latency[MAX_BINDINGS];
        int n_low_latency_bindings;
-       struct ieee80211_vif *new_vif;
+       struct ieee80211_vif *disabled_vif;
 };
 
 static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
@@ -83,13 +83,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
        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)
+       /* skip disabled interfaces here immediately */
+       if (vif == data->disabled_vif)
                return;
 
        if (!mvmvif->phy_ctxt)
@@ -104,11 +99,6 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
        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)
@@ -130,6 +120,11 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
                return;
        }
 
+       if (data->colors[id] < 0)
+               data->colors[id] = mvmvif->phy_ctxt->color;
+       else
+               WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
+
        data->n_interfaces[id]++;
 
        if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
@@ -171,14 +166,15 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
 #endif
 }
 
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+                         struct ieee80211_vif *disabled_vif)
 {
        struct iwl_time_quota_cmd cmd = {};
        int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat;
        struct iwl_mvm_quota_iterator_data data = {
                .n_interfaces = {},
                .colors = { -1, -1, -1, -1 },
-               .new_vif = newvif,
+               .disabled_vif = disabled_vif,
        };
 
        lockdep_assert_held(&mvm->mutex);
@@ -193,10 +189,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
        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);
-       }
 
        /*
         * The FW's scheduling session consists of
@@ -285,6 +277,14 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
 
        iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
 
+       /* check that we have non-zero quota for all valid bindings */
+       for (i = 0; i < MAX_BINDINGS; i++) {
+               if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID))
+                       continue;
+               WARN_ONCE(cmd.quotas[i].quota == 0,
+                         "zero quota on binding %d\n", i);
+       }
+
        ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
                                   sizeof(cmd), &cmd);
        if (ret)
index 306a6caa486889b4dc27ea53fc350f246a7433f8..c70e959bf0e3d443b17ca8e055ee3dd57fda30a2 100644 (file)
@@ -927,7 +927,7 @@ static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
        u8 low;
        u16 high_low;
        u16 rate_mask;
-       struct iwl_mvm *mvm = lq_sta->drv;
+       struct iwl_mvm *mvm = lq_sta->pers.drv;
 
        rate_mask = rs_get_supported_rates(lq_sta, rate);
        high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask,
@@ -946,7 +946,7 @@ static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
 static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
                                          struct rs_rate *rate)
 {
-       struct iwl_mvm *mvm = lq_sta->drv;
+       struct iwl_mvm *mvm = lq_sta->pers.drv;
 
        if (is_legacy(rate)) {
                /* No column to downgrade from Legacy */
@@ -1026,14 +1026,14 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        if (!lq_sta) {
                IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
                return;
-       } else if (!lq_sta->drv) {
+       } else if (!lq_sta->pers.drv) {
                IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
                return;
        }
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        /* Disable last tx check if we are debugging with fixed rate */
-       if (lq_sta->dbg_fixed_rate) {
+       if (lq_sta->pers.dbg_fixed_rate) {
                IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n");
                return;
        }
@@ -1405,7 +1405,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
        int flush_interval_passed = 0;
        struct iwl_mvm *mvm;
 
-       mvm = lq_sta->drv;
+       mvm = lq_sta->pers.drv;
        active_tbl = lq_sta->active_tbl;
 
        tbl = &(lq_sta->lq_info[active_tbl]);
@@ -1865,11 +1865,11 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm,
        int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-       if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
+       if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
                IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n",
-                              lq_sta->dbg_fixed_txp_reduction);
-               lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction;
-               return cur != lq_sta->dbg_fixed_txp_reduction;
+                              lq_sta->pers.dbg_fixed_txp_reduction);
+               lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction;
+               return cur != lq_sta->pers.dbg_fixed_txp_reduction;
        }
 #endif
 
@@ -2382,7 +2382,7 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
        }
 
        /* Treat uninitialized rate scaling data same as non-existing. */
-       if (lq_sta && !lq_sta->drv) {
+       if (lq_sta && !lq_sta->pers.drv) {
                IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
                mvm_sta = NULL;
        }
@@ -2401,12 +2401,18 @@ 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);
+       struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate;
+       struct iwl_mvm *mvm  = IWL_OP_MODE_GET_MVM(op_mode);
+       struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
 
        IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
 
+       lq_sta->pers.drv = mvm;
+#ifdef CONFIG_MAC80211_DEBUGFS
+       lq_sta->pers.dbg_fixed_rate = 0;
+       lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID;
+#endif
+
        return &sta_priv->lq_sta;
 }
 
@@ -2552,7 +2558,9 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
        lq_sta = &sta_priv->lq_sta;
-       memset(lq_sta, 0, sizeof(*lq_sta));
+
+       /* clear all non-persistent lq data */
+       memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
 
        sband = hw->wiphy->bands[band];
 
@@ -2630,17 +2638,12 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
 
        /* 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;
-       lq_sta->dbg_fixed_txp_reduction = TPC_INVALID;
-#endif
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats);
 #endif
@@ -2811,12 +2814,12 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
        u8 ant = initial_rate->ant;
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-       if (lq_sta->dbg_fixed_rate) {
+       if (lq_sta->pers.dbg_fixed_rate) {
                rs_build_rates_table_from_fixed(mvm, lq_cmd,
                                                lq_sta->band,
-                                               lq_sta->dbg_fixed_rate);
+                                               lq_sta->pers.dbg_fixed_rate);
                lq_cmd->reduced_tpc = 0;
-               ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
+               ant = (lq_sta->pers.dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
                        RATE_MCS_ANT_POS;
        } else
 #endif
@@ -2926,14 +2929,14 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
        lq_sta->active_mimo2_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);
+                      lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate);
 
-       if (lq_sta->dbg_fixed_rate) {
+       if (lq_sta->pers.dbg_fixed_rate) {
                struct rs_rate rate;
-               rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate,
+               rs_rate_from_ucode_rate(lq_sta->pers.dbg_fixed_rate,
                                        lq_sta->band, &rate);
                rs_fill_lq_cmd(mvm, NULL, lq_sta, &rate);
-               iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false);
+               iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false);
        }
 }
 
@@ -2946,16 +2949,16 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
        size_t buf_size;
        u32 parsed_rate;
 
-       mvm = lq_sta->drv;
+       mvm = lq_sta->pers.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;
+               lq_sta->pers.dbg_fixed_rate = parsed_rate;
        else
-               lq_sta->dbg_fixed_rate = 0;
+               lq_sta->pers.dbg_fixed_rate = 0;
 
        rs_program_fix_rate(mvm, lq_sta);
 
@@ -2974,7 +2977,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
        struct iwl_mvm *mvm;
        struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
        struct rs_rate *rate = &tbl->rate;
-       mvm = lq_sta->drv;
+       mvm = lq_sta->pers.drv;
        buff = kmalloc(2048, GFP_KERNEL);
        if (!buff)
                return -ENOMEM;
@@ -2984,7 +2987,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
                        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);
+                       lq_sta->pers.dbg_fixed_rate);
        desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
            (mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "",
            (mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "",
@@ -3182,31 +3185,20 @@ static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = {
 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_drv_tx_stats_file =
-               debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
-                                   lq_sta, &rs_sta_dbgfs_drv_tx_stats_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);
-       lq_sta->rs_sta_dbgfs_reduced_txp_file =
-               debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
-                                 &lq_sta->dbg_fixed_txp_reduction);
+       debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+                           lq_sta, &rs_sta_dbgfs_scale_table_ops);
+       debugfs_create_file("rate_stats_table", S_IRUSR, dir,
+                           lq_sta, &rs_sta_dbgfs_stats_table_ops);
+       debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
+                           lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
+       debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+                         &lq_sta->tx_agg_tid_en);
+       debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
+                         &lq_sta->pers.dbg_fixed_txp_reduction);
 }
 
 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_drv_tx_stats_file);
-       debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
-       debugfs_remove(lq_sta->rs_sta_dbgfs_reduced_txp_file);
 }
 #endif
 
index 374a83d7db25a98dd76da34d3fdff9557e48664f..f27b9d687a25e9e1854c6e6a9a04331e8994717e 100644 (file)
@@ -349,16 +349,6 @@ struct iwl_lq_sta {
        struct iwl_lq_cmd lq;
        struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
        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_drv_tx_stats_file;
-       struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
-       struct dentry *rs_sta_dbgfs_reduced_txp_file;
-       u32 dbg_fixed_rate;
-       u8 dbg_fixed_txp_reduction;
-#endif
-       struct iwl_mvm *drv;
 
        /* used to be in sta_info */
        int last_txrate_idx;
@@ -369,6 +359,15 @@ struct iwl_lq_sta {
 
        /* tx power reduce for this sta */
        int tpc_reduce;
+
+       /* persistent fields - initialized only once - keep last! */
+       struct {
+#ifdef CONFIG_MAC80211_DEBUGFS
+               u32 dbg_fixed_rate;
+               u8 dbg_fixed_txp_reduction;
+#endif
+               struct iwl_mvm *drv;
+       } pers;
 };
 
 /* Initialize station's rate scaling information after adding station */
index cf7276967acdec6439392c82e44e94de52d453c8..4b98987fc4133f6b7d7c0a21a1d11e57658a35f7 100644 (file)
@@ -258,6 +258,23 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
 
        memset(&rx_status, 0, sizeof(rx_status));
 
+       /*
+        * We have tx blocked stations (with CS bit). If we heard frames from
+        * a blocked station on a new channel we can TX to it again.
+        */
+       if (unlikely(mvm->csa_tx_block_bcn_timeout)) {
+               struct ieee80211_sta *sta;
+
+               rcu_read_lock();
+
+               sta = ieee80211_find_sta(
+                       rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2);
+               if (sta)
+                       iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+
+               rcu_read_unlock();
+       }
+
        /*
         * drop the packet if it has failed being decrypted by HW
         */
index eac2b424f6a06447a79ba20e9447d4d32fbe5cd6..004b1f5d031429a2798cc1d35464daa6ae59db00 100644 (file)
@@ -97,10 +97,9 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
        return cpu_to_le16(rx_chain);
 }
 
-static inline __le32
-iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req)
+static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band)
 {
-       if (req->channels[0]->band == IEEE80211_BAND_2GHZ)
+       if (band == IEEE80211_BAND_2GHZ)
                return cpu_to_le32(PHY_BAND_24);
        else
                return cpu_to_le32(PHY_BAND_5);
@@ -130,19 +129,19 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
  * 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 first)
+static void iwl_mvm_scan_fill_ssids(struct iwl_ssid_ie *cmd_ssid,
+                                   struct cfg80211_ssid *ssids,
+                                   int n_ssids, int first)
 {
        int fw_idx, req_idx;
 
-       for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx >= first;
+       for (req_idx = n_ssids - 1, fw_idx = 0; req_idx >= first;
             req_idx--, fw_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);
+               cmd_ssid[fw_idx].id = WLAN_EID_SSID;
+               cmd_ssid[fw_idx].len = ssids[req_idx].ssid_len;
+               memcpy(cmd_ssid[fw_idx].ssid,
+                      ssids[req_idx].ssid,
+                      ssids[req_idx].ssid_len);
        }
 }
 
@@ -204,7 +203,8 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
  */
 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,
+                                 const u8 *band_ie, int band_ie_len,
+                                 const u8 *common_ie, int common_ie_len,
                                  int left)
 {
        int len = 0;
@@ -244,12 +244,19 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
 
        len += ssid_len + 2;
 
-       if (WARN_ON(left < ie_len))
+       if (WARN_ON(left < band_ie_len + common_ie_len))
                return len;
 
-       if (ie && ie_len) {
-               memcpy(pos, ie, ie_len);
-               len += ie_len;
+       if (band_ie && band_ie_len) {
+               memcpy(pos, band_ie, band_ie_len);
+               pos += band_ie_len;
+               len += band_ie_len;
+       }
+
+       if (common_ie && common_ie_len) {
+               memcpy(pos, common_ie, common_ie_len);
+               pos += common_ie_len;
+               len += common_ie_len;
        }
 
        return (u16)len;
@@ -267,7 +274,7 @@ static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac,
 
 static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm,
                                     struct ieee80211_vif *vif,
-                                    int n_ssids,
+                                    int n_ssids, u32 flags,
                                     struct iwl_mvm_scan_params *params)
 {
        bool global_bound = false;
@@ -289,6 +296,9 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm,
                params->max_out_time = 250;
        }
 
+       if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
+               params->max_out_time = 200;
+
 not_bound:
 
        for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
@@ -325,22 +335,20 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
 
        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)));
+       memset(cmd, 0, ksize(cmd));
 
        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);
 
-       iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, &params);
+       iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags, &params);
        cmd->max_out_time = cpu_to_le32(params.max_out_time);
        cmd->suspend_time = cpu_to_le32(params.suspend_time);
        if (params.passive_fragmented)
                cmd->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN;
 
-       cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req);
+       cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band);
        cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
                                        MAC_FILTER_IN_BEACON);
 
@@ -367,7 +375,8 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
        }
 
-       iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0);
+       iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->ssids, req->n_ssids,
+                               basic_ssid ? 1 : 0);
 
        cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
                                           TX_CMD_FLG_BT_DIS);
@@ -382,7 +391,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
                            (struct ieee80211_mgmt *)cmd->data,
                            vif->addr,
                            req->n_ssids, ssid, ssid_len,
-                           req->ie, req->ie_len,
+                           req->ie, req->ie_len, NULL, 0,
                            mvm->fw->ucode_capa.max_probe_length));
 
        iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, &params);
@@ -441,16 +450,27 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
        return 0;
 }
 
-int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
-                                 struct iwl_rx_cmd_buffer *rxb,
-                                 struct iwl_device_cmd *cmd)
+int iwl_mvm_rx_scan_offload_results(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_sched_scan_results *notif = (void *)pkt->data;
+       u8 client_bitmap = 0;
 
-       if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
-               IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
-               ieee80211_sched_scan_results(mvm->hw);
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+               struct iwl_sched_scan_results *notif = (void *)pkt->data;
+
+               client_bitmap = notif->client_bitmap;
+       }
+
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
+           client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
+               if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
+                       IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+                       ieee80211_sched_scan_results(mvm->hw);
+               } else {
+                       IWL_DEBUG_SCAN(mvm, "Scan results\n");
+               }
        }
 
        return 0;
@@ -494,7 +514,7 @@ static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
        };
 }
 
-int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+static int iwl_mvm_cancel_regular_scan(struct iwl_mvm *mvm)
 {
        struct iwl_notification_wait wait_scan_abort;
        static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD,
@@ -535,33 +555,52 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
                                           struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data;
+       u8 status, ebs_status;
+
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) {
+               struct iwl_periodic_scan_complete *scan_notif;
+
+               scan_notif = (void *)pkt->data;
+               status = scan_notif->status;
+               ebs_status = scan_notif->ebs_status;
+       } else  {
+               struct iwl_scan_offload_complete *scan_notif;
 
+               scan_notif = (void *)pkt->data;
+               status = scan_notif->status;
+               ebs_status = scan_notif->ebs_status;
+       }
        /* scan status must be locked for proper checking */
        lockdep_assert_held(&mvm->mutex);
 
        IWL_DEBUG_SCAN(mvm,
-                      "Scheduled scan completed, status %s EBS status %s:%d\n",
-                      scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
-                      "completed" : "aborted", scan_notif->ebs_status ==
-                      IWL_SCAN_EBS_SUCCESS ? "success" : "failed",
-                      scan_notif->ebs_status);
+                      "%s completed, status %s, EBS status %s\n",
+                      mvm->scan_status == IWL_MVM_SCAN_SCHED ?
+                               "Scheduled scan" : "Scan",
+                      status == IWL_SCAN_OFFLOAD_COMPLETED ?
+                               "completed" : "aborted",
+                      ebs_status == IWL_SCAN_EBS_SUCCESS ?
+                               "success" : "failed");
 
 
        /* only call mac80211 completion if the stop was initiated by FW */
        if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
                mvm->scan_status = IWL_MVM_SCAN_NONE;
                ieee80211_sched_scan_stopped(mvm->hw);
+       } else if (mvm->scan_status == IWL_MVM_SCAN_OS) {
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               ieee80211_scan_completed(mvm->hw,
+                                        status == IWL_SCAN_OFFLOAD_ABORTED);
        }
 
-       mvm->last_ebs_successful = !scan_notif->ebs_status;
+       mvm->last_ebs_successful = !ebs_status;
 
        return 0;
 }
 
 static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
                                          struct ieee80211_vif *vif,
-                                         struct ieee80211_sched_scan_ies *ies,
+                                         struct ieee80211_scan_ies *ies,
                                          enum ieee80211_band band,
                                          struct iwl_tx_cmd *cmd,
                                          u8 *data)
@@ -577,7 +616,8 @@ static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
        cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data,
                                         vif->addr,
                                         1, NULL, 0,
-                                        ies->ie[band], ies->len[band],
+                                        ies->ies[band], ies->len[band],
+                                        ies->common_ies, ies->common_ie_len,
                                         SCAN_OFFLOAD_PROBE_REQ_SIZE);
        cmd->len = cpu_to_le16(cmd_len);
 }
@@ -621,8 +661,8 @@ static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
 }
 
 static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
-                                       struct iwl_scan_offload_cmd *scan,
-                                       u32 *ssid_bitmap)
+                                       struct iwl_ssid_ie *direct_scan,
+                                       u32 *ssid_bitmap, bool basic_ssid)
 {
        int i, j;
        int index;
@@ -636,10 +676,10 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
                /* skip empty SSID matchsets */
                if (!req->match_sets[i].ssid.ssid_len)
                        continue;
-               scan->direct_scan[i].id = WLAN_EID_SSID;
-               scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
-               memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
-                      scan->direct_scan[i].len);
+               direct_scan[i].id = WLAN_EID_SSID;
+               direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
+               memcpy(direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
+                      direct_scan[i].len);
        }
 
        /* add SSIDs from scan SSID list */
@@ -647,14 +687,14 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
        for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) {
                index = iwl_ssid_exist(req->ssids[j].ssid,
                                       req->ssids[j].ssid_len,
-                                      scan->direct_scan);
+                                      direct_scan);
                if (index < 0) {
-                       if (!req->ssids[j].ssid_len)
+                       if (!req->ssids[j].ssid_len && basic_ssid)
                                continue;
-                       scan->direct_scan[i].id = WLAN_EID_SSID;
-                       scan->direct_scan[i].len = req->ssids[j].ssid_len;
-                       memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid,
-                              scan->direct_scan[i].len);
+                       direct_scan[i].id = WLAN_EID_SSID;
+                       direct_scan[i].len = req->ssids[j].ssid_len;
+                       memcpy(direct_scan[i].ssid, req->ssids[j].ssid,
+                              direct_scan[i].len);
                        *ssid_bitmap |= BIT(i + 1);
                        i++;
                } else {
@@ -665,12 +705,19 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
 
 static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
                                  struct cfg80211_sched_scan_request *req,
-                                 struct iwl_scan_channel_cfg *channels,
+                                 u8 *channels_buffer,
                                  enum ieee80211_band band,
                                  int *head,
                                  u32 ssid_bitmap,
                                  struct iwl_mvm_scan_params *params)
 {
+       u32 n_channels = mvm->fw->ucode_capa.n_scan_channels;
+       __le32 *type = (__le32 *)channels_buffer;
+       __le16 *channel_number = (__le16 *)(type + n_channels);
+       __le16 *iter_count = channel_number + n_channels;
+       __le32 *iter_interval = (__le32 *)(iter_count + n_channels);
+       u8 *active_dwell = (u8 *)(iter_interval + n_channels);
+       u8 *passive_dwell = active_dwell + n_channels;
        int i, index = 0;
 
        for (i = 0; i < req->n_channels; i++) {
@@ -682,34 +729,33 @@ static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
                index = *head;
                (*head)++;
 
-               channels->channel_number[index] = cpu_to_le16(chan->hw_value);
-               channels->dwell_time[index][0] = params->dwell[band].active;
-               channels->dwell_time[index][1] = params->dwell[band].passive;
+               channel_number[index] = cpu_to_le16(chan->hw_value);
+               active_dwell[index] = params->dwell[band].active;
+               passive_dwell[index] = params->dwell[band].passive;
 
-               channels->iter_count[index] = cpu_to_le16(1);
-               channels->iter_interval[index] = 0;
+               iter_count[index] = cpu_to_le16(1);
+               iter_interval[index] = 0;
 
                if (!(chan->flags & IEEE80211_CHAN_NO_IR))
-                       channels->type[index] |=
+                       type[index] |=
                                cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE);
 
-               channels->type[index] |=
-                               cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL |
-                                           IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL);
+               type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL |
+                                          IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL);
 
                if (chan->flags & IEEE80211_CHAN_NO_HT40)
-                       channels->type[index] |=
+                       type[index] |=
                                cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW);
 
                /* scan for all SSIDs from req->ssids */
-               channels->type[index] |= cpu_to_le32(ssid_bitmap);
+               type[index] |= cpu_to_le32(ssid_bitmap);
        }
 }
 
 int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
                              struct ieee80211_vif *vif,
                              struct cfg80211_sched_scan_request *req,
-                             struct ieee80211_sched_scan_ies *ies)
+                             struct ieee80211_scan_ies *ies)
 {
        int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels;
        int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
@@ -717,6 +763,9 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
        u32 ssid_bitmap;
        int cmd_len;
        int ret;
+       u8 *probes;
+       bool basic_ssid = !(mvm->fw->ucode_capa.flags &
+                           IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID);
 
        struct iwl_scan_offload_cfg *scan_cfg;
        struct iwl_host_cmd cmd = {
@@ -727,24 +776,29 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
        lockdep_assert_held(&mvm->mutex);
 
        cmd_len = sizeof(struct iwl_scan_offload_cfg) +
+                 mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE +
                  2 * SCAN_OFFLOAD_PROBE_REQ_SIZE;
 
        scan_cfg = kzalloc(cmd_len, GFP_KERNEL);
        if (!scan_cfg)
                return -ENOMEM;
 
-       iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, &params);
+       probes = scan_cfg->data +
+               mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE;
+
+       iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, &params);
        iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, &params);
        scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len);
 
-       iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap);
+       iwl_scan_offload_build_ssid(req, scan_cfg->scan_cmd.direct_scan,
+                                   &ssid_bitmap, basic_ssid);
        /* build tx frames for supported bands */
        if (band_2ghz) {
                iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
                                              IEEE80211_BAND_2GHZ,
                                              &scan_cfg->scan_cmd.tx_cmd[0],
-                                             scan_cfg->data);
-               iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
+                                             probes);
+               iwl_build_channel_cfg(mvm, req, scan_cfg->data,
                                      IEEE80211_BAND_2GHZ, &head,
                                      ssid_bitmap, &params);
        }
@@ -752,9 +806,9 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
                iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
                                              IEEE80211_BAND_5GHZ,
                                              &scan_cfg->scan_cmd.tx_cmd[1],
-                                             scan_cfg->data +
+                                             probes +
                                                SCAN_OFFLOAD_PROBE_REQ_SIZE);
-               iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
+               iwl_build_channel_cfg(mvm, req, scan_cfg->data,
                                      IEEE80211_BAND_5GHZ, &head,
                                      ssid_bitmap, &params);
        }
@@ -845,11 +899,11 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                .watchdog = IWL_SCHED_SCAN_WATCHDOG,
 
                .schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS,
-               .schedule_line[0].delay = req->interval / 1000,
+               .schedule_line[0].delay = cpu_to_le16(req->interval / 1000),
                .schedule_line[0].full_scan_mul = 1,
 
                .schedule_line[1].iterations = 0xff,
-               .schedule_line[1].delay = req->interval / 1000,
+               .schedule_line[1].delay = cpu_to_le16(req->interval / 1000),
                .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER,
        };
 
@@ -872,7 +926,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
                                    sizeof(scan_req), &scan_req);
 }
 
-static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
+static int iwl_mvm_send_scan_offload_abort(struct iwl_mvm *mvm)
 {
        int ret;
        struct iwl_host_cmd cmd = {
@@ -883,7 +937,9 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
        /* Exit instantly with error when device is not ready
         * to receive scan abort command or it does not perform
         * scheduled scan currently */
-       if (mvm->scan_status != IWL_MVM_SCAN_SCHED)
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+           (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
+            mvm->scan_status != IWL_MVM_SCAN_OS))
                return -EIO;
 
        ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status);
@@ -905,16 +961,19 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
        return ret;
 }
 
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
+int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify)
 {
        int ret;
        struct iwl_notification_wait wait_scan_done;
        static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, };
+       bool sched = mvm->scan_status == IWL_MVM_SCAN_SCHED;
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (mvm->scan_status != IWL_MVM_SCAN_SCHED) {
-               IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n");
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+           (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
+            mvm->scan_status != IWL_MVM_SCAN_OS)) {
+               IWL_DEBUG_SCAN(mvm, "No scan to stop\n");
                return 0;
        }
 
@@ -923,14 +982,16 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
                                   ARRAY_SIZE(scan_done_notif),
                                   NULL, NULL);
 
-       ret = iwl_mvm_send_sched_scan_abort(mvm);
+       ret = iwl_mvm_send_scan_offload_abort(mvm);
        if (ret) {
-               IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret);
+               IWL_DEBUG_SCAN(mvm, "Send stop %sscan failed %d\n",
+                              sched ? "offloaded " : "", ret);
                iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
                return ret;
        }
 
-       IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n");
+       IWL_DEBUG_SCAN(mvm, "Successfully sent stop %sscan\n",
+                      sched ? "offloaded " : "");
 
        ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
        if (ret)
@@ -943,8 +1004,317 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
         */
        mvm->scan_status = IWL_MVM_SCAN_NONE;
 
-       if (notify)
-               ieee80211_sched_scan_stopped(mvm->hw);
+       if (notify) {
+               if (sched)
+                       ieee80211_sched_scan_stopped(mvm->hw);
+               else
+                       ieee80211_scan_completed(mvm->hw, true);
+       }
 
        return 0;
 }
+
+static void iwl_mvm_unified_scan_fill_tx_cmd(struct iwl_mvm *mvm,
+                                            struct iwl_scan_req_tx_cmd *tx_cmd,
+                                            bool no_cck)
+{
+       tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+                                        TX_CMD_FLG_BT_DIS);
+       tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
+                                                          IEEE80211_BAND_2GHZ,
+                                                          no_cck);
+       tx_cmd[0].sta_id = mvm->aux_sta.sta_id;
+
+       tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+                                        TX_CMD_FLG_BT_DIS);
+       tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
+                                                          IEEE80211_BAND_5GHZ,
+                                                          no_cck);
+       tx_cmd[1].sta_id = mvm->aux_sta.sta_id;
+}
+
+static void
+iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm,
+                              struct ieee80211_channel **channels,
+                              int n_channels, u32 ssid_bitmap,
+                              struct iwl_scan_req_unified_lmac *cmd)
+{
+       struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data;
+       int i;
+
+       for (i = 0; i < n_channels; i++) {
+               channel_cfg[i].channel_num =
+                       cpu_to_le16(channels[i]->hw_value);
+               channel_cfg[i].iter_count = cpu_to_le16(1);
+               channel_cfg[i].iter_interval = 0;
+               channel_cfg[i].flags =
+                       cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL |
+                                   ssid_bitmap);
+       }
+}
+
+static void
+iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                                struct ieee80211_scan_ies *ies,
+                                struct iwl_scan_req_unified_lmac *cmd)
+{
+       struct iwl_scan_probe_req *preq = (void *)(cmd->data +
+               sizeof(struct iwl_scan_channel_cfg_lmac) *
+                       mvm->fw->ucode_capa.n_scan_channels);
+       struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf;
+       u8 *pos;
+
+       frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+       eth_broadcast_addr(frame->da);
+       memcpy(frame->sa, vif->addr, ETH_ALEN);
+       eth_broadcast_addr(frame->bssid);
+       frame->seq_ctrl = 0;
+
+       pos = frame->u.probe_req.variable;
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = 0;
+
+       preq->mac_header.offset = 0;
+       preq->mac_header.len = cpu_to_le16(24 + 2);
+
+       memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ],
+              ies->len[IEEE80211_BAND_2GHZ]);
+       preq->band_data[0].offset = cpu_to_le16(pos - preq->buf);
+       preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]);
+       pos += ies->len[IEEE80211_BAND_2GHZ];
+
+       memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
+              ies->len[IEEE80211_BAND_5GHZ]);
+       preq->band_data[1].offset = cpu_to_le16(pos - preq->buf);
+       preq->band_data[1].len = cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]);
+       pos += ies->len[IEEE80211_BAND_5GHZ];
+
+       memcpy(pos, ies->common_ies, ies->common_ie_len);
+       preq->common_data.offset = cpu_to_le16(pos - preq->buf);
+       preq->common_data.len = cpu_to_le16(ies->common_ie_len);
+}
+
+static void
+iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm,
+                                      struct iwl_scan_req_unified_lmac *cmd,
+                                      struct iwl_mvm_scan_params *params)
+{
+       memset(cmd, 0, ksize(cmd));
+       cmd->active_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].active;
+       cmd->passive_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].passive;
+       /* TODO: Use params; now fragmented isn't used. */
+       cmd->fragmented_dwell = 0;
+       cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm);
+       cmd->max_out_time = cpu_to_le32(params->max_out_time);
+       cmd->suspend_time = cpu_to_le32(params->suspend_time);
+       cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+       cmd->iter_num = cpu_to_le32(1);
+
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT &&
+           mvm->last_ebs_successful) {
+               cmd->channel_opt[0].flags =
+                       cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+                                   IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+                                   IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+               cmd->channel_opt[1].flags =
+                       cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+                                   IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+                                   IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+       }
+}
+
+int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             struct ieee80211_scan_request *req)
+{
+       struct iwl_host_cmd hcmd = {
+               .id = SCAN_OFFLOAD_REQUEST_CMD,
+               .len = { sizeof(struct iwl_scan_req_unified_lmac) +
+                        sizeof(struct iwl_scan_channel_cfg_lmac) *
+                               mvm->fw->ucode_capa.n_scan_channels +
+                        sizeof(struct iwl_scan_probe_req), },
+               .data = { mvm->scan_cmd, },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+       struct iwl_mvm_scan_params params = {};
+       u32 flags;
+       int ssid_bitmap = 0;
+       int ret, i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* we should have failed registration if scan_cmd was NULL */
+       if (WARN_ON(mvm->scan_cmd == NULL))
+               return -ENOMEM;
+
+       if (WARN_ON_ONCE(req->req.n_ssids > PROBE_OPTION_MAX ||
+                        req->ies.common_ie_len + req->ies.len[0] +
+                               req->ies.len[1] + 24 + 2 >
+                                       SCAN_OFFLOAD_PROBE_REQ_SIZE ||
+                        req->req.n_channels >
+                               mvm->fw->ucode_capa.n_scan_channels))
+               return -1;
+
+       mvm->scan_status = IWL_MVM_SCAN_OS;
+
+       iwl_mvm_scan_calc_params(mvm, vif, req->req.n_ssids, req->req.flags,
+                                &params);
+
+       iwl_mvm_build_generic_unified_scan_cmd(mvm, cmd, &params);
+
+       cmd->n_channels = (u8)req->req.n_channels;
+
+       flags = IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
+
+       if (req->req.n_ssids == 1 && req->req.ssids[0].ssid_len != 0)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
+
+       if (params.passive_fragmented)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
+
+       if (req->req.n_ssids == 0)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
+
+       cmd->scan_flags = cpu_to_le32(flags);
+
+       cmd->flags = iwl_mvm_scan_rxon_flags(req->req.channels[0]->band);
+       cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+                                       MAC_FILTER_IN_BEACON);
+       iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, req->req.no_cck);
+       iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->req.ssids,
+                               req->req.n_ssids, 0);
+
+       cmd->schedule[0].delay = 0;
+       cmd->schedule[0].iterations = 1;
+       cmd->schedule[0].full_scan_mul = 0;
+       cmd->schedule[1].delay = 0;
+       cmd->schedule[1].iterations = 0;
+       cmd->schedule[1].full_scan_mul = 0;
+
+       for (i = 1; i <= req->req.n_ssids; i++)
+               ssid_bitmap |= BIT(i);
+
+       iwl_mvm_lmac_scan_cfg_channels(mvm, req->req.channels,
+                                      req->req.n_channels, ssid_bitmap,
+                                      cmd);
+
+       iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, cmd);
+
+       ret = iwl_mvm_send_cmd(mvm, &hcmd);
+       if (!ret) {
+               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! ret %d\n", ret);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               ret = -EIO;
+       }
+       return ret;
+}
+
+int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif,
+                                   struct cfg80211_sched_scan_request *req,
+                                   struct ieee80211_scan_ies *ies)
+{
+       struct iwl_host_cmd hcmd = {
+               .id = SCAN_OFFLOAD_REQUEST_CMD,
+               .len = { sizeof(struct iwl_scan_req_unified_lmac) +
+                        sizeof(struct iwl_scan_channel_cfg_lmac) *
+                               mvm->fw->ucode_capa.n_scan_channels +
+                        sizeof(struct iwl_scan_probe_req), },
+               .data = { mvm->scan_cmd, },
+               .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+       };
+       struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+       struct iwl_mvm_scan_params params = {};
+       int ret;
+       u32 flags = 0, ssid_bitmap = 0;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* we should have failed registration if scan_cmd was NULL */
+       if (WARN_ON(mvm->scan_cmd == NULL))
+               return -ENOMEM;
+
+       if (WARN_ON_ONCE(req->n_ssids > PROBE_OPTION_MAX ||
+                        ies->common_ie_len + ies->len[0] + ies->len[1] + 24 + 2
+                               > SCAN_OFFLOAD_PROBE_REQ_SIZE ||
+                        req->n_channels > mvm->fw->ucode_capa.n_scan_channels))
+               return -ENOBUFS;
+
+       iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, &params);
+
+       iwl_mvm_build_generic_unified_scan_cmd(mvm, cmd, &params);
+
+       cmd->n_channels = (u8)req->n_channels;
+
+       if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sending scheduled scan with filtering, n_match_sets %d\n",
+                              req->n_match_sets);
+       } else {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sending Scheduled scan without filtering\n");
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
+       }
+
+       if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
+
+       if (params.passive_fragmented)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
+
+       if (req->n_ssids == 0)
+               flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
+
+       cmd->scan_flags = cpu_to_le32(flags);
+
+       cmd->flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band);
+       cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+                                       MAC_FILTER_IN_BEACON);
+       iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, false);
+       iwl_scan_offload_build_ssid(req, cmd->direct_scan, &ssid_bitmap, false);
+
+       cmd->schedule[0].delay = cpu_to_le16(req->interval / MSEC_PER_SEC);
+       cmd->schedule[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS;
+       cmd->schedule[0].full_scan_mul = 1;
+
+       cmd->schedule[1].delay = cpu_to_le16(req->interval / MSEC_PER_SEC);
+       cmd->schedule[1].iterations = 0xff;
+       cmd->schedule[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER;
+
+       iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels,
+                                      ssid_bitmap, cmd);
+
+       iwl_mvm_build_unified_scan_probe(mvm, vif, ies, cmd);
+
+       ret = iwl_mvm_send_cmd(mvm, &hcmd);
+       if (!ret) {
+               IWL_DEBUG_SCAN(mvm,
+                              "Sched 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, "Sched scan failed! ret %d\n", ret);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               ret = -EIO;
+       }
+       return ret;
+}
+
+
+int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+{
+       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+               return iwl_mvm_scan_offload_stop(mvm, true);
+       return iwl_mvm_cancel_regular_scan(mvm);
+}
index 1fb01ea2e7047201324faefdc1f181408a77d070..7635488803999cba22a501ef2990f1d96fcc974a 100644 (file)
@@ -98,23 +98,21 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                           bool update)
 {
        struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
-       struct iwl_mvm_add_sta_cmd add_sta_cmd;
+       struct iwl_mvm_add_sta_cmd add_sta_cmd = {
+               .sta_id = mvm_sta->sta_id,
+               .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color),
+               .add_modify = update ? 1 : 0,
+               .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK |
+                                                STA_FLG_MIMO_EN_MSK),
+       };
        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);
        if (!update) {
                add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk);
                memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN);
        }
-       add_sta_cmd.add_modify = update ? 1 : 0;
-
-       add_sta_cmd.station_flags_msk |= cpu_to_le32(STA_FLG_FAT_EN_MSK |
-                                                    STA_FLG_MIMO_EN_MSK);
 
        switch (sta->bandwidth) {
        case IEEE80211_STA_RX_BW_160:
@@ -528,8 +526,12 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       /* Add the aux station, but without any queues */
-       ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0,
+       /* Map Aux queue to fifo - needs to happen before adding Aux station */
+       iwl_trans_ac_txq_enable(mvm->trans, mvm->aux_queue,
+                               IWL_MVM_TX_FIFO_MCAST);
+
+       /* Allocate aux station and assign to it the aux queue */
+       ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue),
                                       NL80211_IFTYPE_UNSPECIFIED);
        if (ret)
                return ret;
@@ -1448,3 +1450,77 @@ int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
 
        return 0;
 }
+
+void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
+                                  struct iwl_mvm_sta *mvmsta, bool disable)
+{
+       struct iwl_mvm_add_sta_cmd cmd = {
+               .add_modify = STA_MODE_MODIFY,
+               .sta_id = mvmsta->sta_id,
+               .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0,
+               .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX),
+               .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
+       };
+       int ret;
+
+       if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_DISABLE_STA_TX))
+               return;
+
+       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_disable_tx_ap(struct iwl_mvm *mvm,
+                                     struct ieee80211_sta *sta,
+                                     bool disable)
+{
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
+       spin_lock_bh(&mvm_sta->lock);
+
+       if (mvm_sta->disable_tx == disable) {
+               spin_unlock_bh(&mvm_sta->lock);
+               return;
+       }
+
+       mvm_sta->disable_tx = disable;
+
+       /*
+        * Tell mac80211 to start/stop queueing tx for this station,
+        * but don't stop queueing if there are still pending frames
+        * for this station.
+        */
+       if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
+               ieee80211_sta_block_awake(mvm->hw, sta, disable);
+
+       iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable);
+
+       spin_unlock_bh(&mvm_sta->lock);
+}
+
+void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
+                                      struct iwl_mvm_vif *mvmvif,
+                                      bool disable)
+{
+       struct ieee80211_sta *sta;
+       struct iwl_mvm_sta *mvm_sta;
+       int i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       /* Block/unblock all the stations of the given mvmvif */
+       for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+               sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+                                               lockdep_is_held(&mvm->mutex));
+               if (IS_ERR_OR_NULL(sta))
+                       continue;
+
+               mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+               if (mvm_sta->mac_id_n_color !=
+                   FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
+                       continue;
+
+               iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
+       }
+}
index d98e8a2142b8c6b1e3b9e0568956e0bcaa7a357c..3b1c8bd6cb54356c41c102402657cf765f617a50 100644 (file)
@@ -73,6 +73,7 @@
 #include "rs.h"
 
 struct iwl_mvm;
+struct iwl_mvm_vif;
 
 /**
  * DOC: station table - introduction
@@ -295,6 +296,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
  * @tid_data: per tid data. Look at %iwl_mvm_tid_data.
  * @tx_protection: reference counter for controlling the Tx protection.
  * @tt_tx_protection: is thermal throttling enable Tx protection?
+ * @disable_tx: is tx to this STA disabled?
  *
  * 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
@@ -317,6 +319,8 @@ struct iwl_mvm_sta {
        /* Temporary, until the new TLC will control the Tx protection */
        s8 tx_protection;
        bool tt_tx_protection;
+
+       bool disable_tx;
 };
 
 static inline struct iwl_mvm_sta *
@@ -404,5 +408,13 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
                                       bool agg);
 int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
                      bool drain);
+void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
+                                  struct iwl_mvm_sta *mvmsta, bool disable);
+void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
+                                     struct ieee80211_sta *sta,
+                                     bool disable);
+void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
+                                      struct iwl_mvm_vif *mvmvif,
+                                      bool disable);
 
 #endif /* __sta_h__ */
index 80100f6cc12a85a79fc71bd83cfe2f6962f8f1c0..33e5041f1efc191d69b847ad729c3bb434784dfc 100644 (file)
@@ -72,9 +72,6 @@
 #include "iwl-io.h"
 #include "iwl-prph.h"
 
-/* A TimeUnit is 1024 microsecond */
-#define MSEC_TO_TU(_msec)      (_msec*1000/1024)
-
 /*
  * For the high priority TE use a time event type that has similar priority to
  * the FW's action scan priority.
@@ -100,6 +97,21 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
 void iwl_mvm_roc_done_wk(struct work_struct *wk)
 {
        struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
+       u32 queues = 0;
+
+       /*
+        * Clear the ROC_RUNNING /ROC_AUX_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).
+        */
+       if (test_and_clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
+               queues |= BIT(IWL_MVM_OFFCHANNEL_QUEUE);
+       if (test_and_clear_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
+               queues |= BIT(mvm->aux_queue);
+
+       iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
 
        synchronize_net();
 
@@ -113,21 +125,11 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)
         * 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_MVM_OFFCHANNEL_QUEUE), false);
+       iwl_mvm_flush_tx_path(mvm, queues, 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);
-       iwl_mvm_unref(mvm, IWL_MVM_REF_ROC);
-
        /*
         * Of course, our status bit is just as racy as mac80211, so in
         * addition, fire off the work struct which will drop all frames
@@ -138,6 +140,41 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
        schedule_work(&mvm->roc_done_wk);
 }
 
+static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
+{
+       struct ieee80211_vif *csa_vif;
+
+       rcu_read_lock();
+
+       csa_vif = rcu_dereference(mvm->csa_vif);
+       if (!csa_vif || !csa_vif->csa_active)
+               goto out_unlock;
+
+       IWL_DEBUG_TE(mvm, "CSA NOA started\n");
+
+       /*
+        * CSA NoA is started but we still have beacons to
+        * transmit on the current channel.
+        * So we just do nothing here and the switch
+        * will be performed on the last TBTT.
+        */
+       if (!ieee80211_csa_is_complete(csa_vif)) {
+               IWL_WARN(mvm, "CSA NOA started too early\n");
+               goto out_unlock;
+       }
+
+       ieee80211_csa_finish(csa_vif);
+
+       rcu_read_unlock();
+
+       RCU_INIT_POINTER(mvm->csa_vif, NULL);
+
+       return;
+
+out_unlock:
+       rcu_read_unlock();
+}
+
 static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
                                        struct ieee80211_vif *vif,
                                        const char *errmsg)
@@ -213,12 +250,74 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
                        set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
                        iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
                        ieee80211_ready_on_channel(mvm->hw);
+               } else if (te_data->vif->type == NL80211_IFTYPE_AP) {
+                       if (le32_to_cpu(notif->status))
+                               iwl_mvm_csa_noa_start(mvm);
+                       else
+                               IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");
+
+                       /* we don't need it anymore */
+                       iwl_mvm_te_clear_data(mvm, te_data);
                }
        } else {
                IWL_WARN(mvm, "Got TE with unknown action\n");
        }
 }
 
+/*
+ * Handle A Aux ROC time event
+ */
+static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
+                                          struct iwl_time_event_notif *notif)
+{
+       struct iwl_mvm_time_event_data *te_data, *tmp;
+       bool aux_roc_te = false;
+
+       list_for_each_entry_safe(te_data, tmp, &mvm->aux_roc_te_list, list) {
+               if (le32_to_cpu(notif->unique_id) == te_data->uid) {
+                       aux_roc_te = true;
+                       break;
+               }
+       }
+       if (!aux_roc_te) /* Not a Aux ROC time event */
+               return -EINVAL;
+
+       if (!le32_to_cpu(notif->status)) {
+               IWL_DEBUG_TE(mvm,
+                            "ERROR: Aux ROC Time Event %s notification failure\n",
+                            (le32_to_cpu(notif->action) &
+                             TE_V2_NOTIF_HOST_EVENT_START) ? "start" : "end");
+               return -EINVAL;
+       }
+
+       IWL_DEBUG_TE(mvm,
+                    "Aux ROC time event notification  - UID = 0x%x action %d\n",
+                    le32_to_cpu(notif->unique_id),
+                    le32_to_cpu(notif->action));
+
+       if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_END) {
+               /* End TE, notify mac80211 */
+               ieee80211_remain_on_channel_expired(mvm->hw);
+               iwl_mvm_roc_finished(mvm); /* flush aux queue */
+               list_del(&te_data->list); /* remove from list */
+               te_data->running = false;
+               te_data->vif = NULL;
+               te_data->uid = 0;
+       } else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
+               set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
+               set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
+               te_data->running = true;
+               ieee80211_ready_on_channel(mvm->hw); /* Start TE */
+       } else {
+               IWL_DEBUG_TE(mvm,
+                            "ERROR: Unknown Aux ROC Time Event (action = %d)\n",
+                            le32_to_cpu(notif->action));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 /*
  * The Rx handler for time event notifications
  */
@@ -235,10 +334,15 @@ int iwl_mvm_rx_time_event_notif(struct iwl_mvm *mvm,
                     le32_to_cpu(notif->action));
 
        spin_lock_bh(&mvm->time_event_lock);
+       /* This time event is triggered for Aux ROC request */
+       if (!iwl_mvm_aux_roc_te_handle_notif(mvm, notif))
+               goto unlock;
+
        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);
        }
+unlock:
        spin_unlock_bh(&mvm->time_event_lock);
 
        return 0;
@@ -538,3 +642,33 @@ void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
 
        iwl_mvm_roc_finished(mvm);
 }
+
+int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm,
+                             struct ieee80211_vif *vif,
+                             u32 duration, u32 apply_time)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+       struct iwl_time_event_cmd time_cmd = {};
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (te_data->running) {
+               IWL_DEBUG_TE(mvm, "CS NOA is already scheduled\n");
+               return -EBUSY;
+       }
+
+       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_GO_CSA_NOA);
+       time_cmd.apply_time = cpu_to_le32(apply_time);
+       time_cmd.max_frags = TE_V2_FRAG_NONE;
+       time_cmd.duration = cpu_to_le32(duration);
+       time_cmd.repeat = 1;
+       time_cmd.interval = cpu_to_le32(1);
+       time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+                                     TE_V2_ABSENCE);
+
+       return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
+}
index 4a61c8c02372824cd08b3901f2e26c2a0353f35d..2f48a90d4ad3e61b010609e2c1d13357c92b3427 100644 (file)
@@ -214,4 +214,33 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
 
 void iwl_mvm_roc_done_wk(struct work_struct *wk);
 
+/**
+ * iwl_mvm_schedule_csa_noa - request NoA for channel switch
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the channel switch is issued
+ * @duration: the duration of the NoA in TU.
+ * @apply_time: NoA start time in GP2.
+ *
+ * This function is used to schedule NoA time event and is used to perform
+ * the channel switch flow.
+ */
+int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm,
+                            struct ieee80211_vif *vif,
+                            u32 duration, u32 apply_time);
+
+/**
+ * iwl_mvm_te_scheduled - check if the fw received the TE cmd
+ * @te_data: the time event data that corresponds to that time event
+ *
+ * This function returns true iff this TE is added to the fw.
+ */
+static inline bool
+iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data)
+{
+       if (!te_data)
+               return false;
+
+       return !!te_data->uid;
+}
+
 #endif /* __time_event_h__ */
index 868561512783956617f5cae55d294f7c5207918d..0464599c111e07522fa5efb36fa3828639c17b0c 100644 (file)
@@ -140,9 +140,9 @@ static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm)
 
        /* TODO: move parsing to NVM code */
        calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data;
-       ptat = calib[OTP_DTS_DIODE_DEVIATION];
-       pa1 = calib[OTP_DTS_DIODE_DEVIATION + 1];
-       pa2 = calib[OTP_DTS_DIODE_DEVIATION + 2];
+       ptat = calib[OTP_DTS_DIODE_DEVIATION * 2];
+       pa1 = calib[OTP_DTS_DIODE_DEVIATION * 2 + 1];
+       pa2 = calib[OTP_DTS_DIODE_DEVIATION * 2 + 2];
 
        /* get the median: */
        if (ptat > pa1) {
@@ -338,10 +338,16 @@ static void check_exit_ctkill(struct work_struct *work)
 
        duration = tt->params->ct_kill_duration;
 
+       /* make sure the device is available for direct read/writes */
+       if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_CHECK_CTKILL))
+               goto reschedule;
+
        iwl_trans_start_hw(mvm->trans);
        temp = check_nic_temperature(mvm);
        iwl_trans_stop_device(mvm->trans);
 
+       iwl_mvm_unref(mvm, IWL_MVM_REF_CHECK_CTKILL);
+
        if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) {
                IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n");
                goto reschedule;
index 3846a6c41eb165ffbb8ede0ff102547f36911e65..dbc870713882937c381a90ea2a1c31a2a9f60af8 100644 (file)
@@ -131,7 +131,6 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
            !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);
@@ -205,7 +204,13 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
        mvm->mgmt_last_antenna_idx =
                iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
                                     mvm->mgmt_last_antenna_idx);
-       rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
+
+       if (info->band == IEEE80211_BAND_2GHZ &&
+           !iwl_mvm_bt_coex_is_shared_ant_avail(mvm))
+               rate_flags = BIT(ANT_A) << RATE_MCS_ANT_POS;
+       else
+               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))
@@ -305,6 +310,16 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb)
                          info->hw_queue != info->control.vif->cab_queue)))
                return -1;
 
+       /*
+        * IWL_MVM_OFFCHANNEL_QUEUE is used for ROC packets that can be used
+        * in 2 different types of vifs, P2P & STATION. P2P uses the offchannel
+        * queue. STATION (HS2.0) uses the auxiliary context of the FW,
+        * and hence needs to be sent on the aux queue
+        */
+       if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
+           info->control.vif->type == NL80211_IFTYPE_STATION)
+               IEEE80211_SKB_CB(skb)->hw_queue = mvm->aux_queue;
+
        /*
         * If the interface on which frame is sent is the P2P_DEVICE
         * or an AP/GO interface use the broadcast station associated
@@ -717,18 +732,26 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
        /* We can't free more than one frame at once on a shared queue */
        WARN_ON(skb_freed > 1);
 
-       /* If we have still frames from this STA nothing to do here */
+       /* If we have still frames for this STA nothing to do here */
        if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id]))
                goto out;
 
        if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
+
                /*
-                * If there are no pending frames for this STA, notify
-                * mac80211 that this station can go to sleep in its
+                * If there are no pending frames for this STA and
+                * the tx to this station is not disabled, notify
+                * mac80211 that this station can now wake up in its
                 * STA table.
                 * If mvmsta is not NULL, sta is valid.
                 */
-               ieee80211_sta_block_awake(mvm->hw, sta, false);
+
+               spin_lock_bh(&mvmsta->lock);
+
+               if (!mvmsta->disable_tx)
+                       ieee80211_sta_block_awake(mvm->hw, sta, false);
+
+               spin_unlock_bh(&mvmsta->lock);
        }
 
        if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {
index aa9fc77e8413b607861e370169e0b55ba4b697d1..ac249da8a22b0840ce8de74638ba1969a119c67c 100644 (file)
@@ -519,71 +519,6 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                iwl_mvm_dump_umac_error_log(mvm);
 }
 
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
-{
-       const struct fw_img *img;
-       u32 ofs, sram_len;
-       void *sram;
-
-       if (!mvm->ucode_loaded || mvm->fw_error_sram || mvm->fw_error_dump)
-               return;
-
-       img = &mvm->fw->img[mvm->cur_ucode];
-       ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
-       sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
-
-       sram = kzalloc(sram_len, GFP_ATOMIC);
-       if (!sram)
-               return;
-
-       iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len);
-       mvm->fw_error_sram = sram;
-       mvm->fw_error_sram_len = sram_len;
-}
-
-void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm)
-{
-       int i, reg_val;
-       unsigned long flags;
-
-       if (!mvm->ucode_loaded || mvm->fw_error_rxf || mvm->fw_error_dump)
-               return;
-
-       /* reading buffer size */
-       reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
-       mvm->fw_error_rxf_len =
-               (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
-
-       /* the register holds the value divided by 128 */
-       mvm->fw_error_rxf_len = mvm->fw_error_rxf_len << 7;
-
-       if (!mvm->fw_error_rxf_len)
-               return;
-
-       mvm->fw_error_rxf =  kzalloc(mvm->fw_error_rxf_len, GFP_ATOMIC);
-       if (!mvm->fw_error_rxf) {
-               mvm->fw_error_rxf_len = 0;
-               return;
-       }
-
-       if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
-               kfree(mvm->fw_error_rxf);
-               mvm->fw_error_rxf = NULL;
-               mvm->fw_error_rxf_len = 0;
-               return;
-       }
-
-       for (i = 0; i < (mvm->fw_error_rxf_len / sizeof(u32)); i++) {
-               iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR,
-                                    i * sizeof(u32));
-               mvm->fw_error_rxf[i] =
-                       iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_ADDR);
-       }
-       iwl_trans_release_nic_access(mvm->trans, &flags);
-}
-#endif
-
 /**
  * iwl_mvm_send_lq_cmd() - Send link quality command
  * @init: This command is sent as part of station initialization right
index 6c22b23a2845723c33df6757a1ced2c545747e94..78f72c34438aaabd7be9f94386a4e093689a2906 100644 (file)
@@ -260,6 +260,9 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
  * @wd_timeout: queue watchdog timeout (jiffies)
  * @reg_lock: protect hw register access
  * @cmd_in_flight: true when we have a host command in flight
+ * @fw_mon_phys: physical address of the buffer for the firmware monitor
+ * @fw_mon_page: points to the first page of the buffer for the firmware monitor
+ * @fw_mon_size: size of the buffer for the firmware monitor
  */
 struct iwl_trans_pcie {
        struct iwl_rxq rxq;
@@ -312,6 +315,10 @@ struct iwl_trans_pcie {
        /*protect hw register */
        spinlock_t reg_lock;
        bool cmd_in_flight;
+
+       dma_addr_t fw_mon_phys;
+       struct page *fw_mon_page;
+       u32 fw_mon_size;
 };
 
 #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
index 788085bc65d78e3382c7fa76ca74de30abd4cd84..06e04aaf61eea9609d619be4e0afdf407dd307d4 100644 (file)
@@ -67,6 +67,7 @@
 #include <linux/sched.h>
 #include <linux/bitops.h>
 #include <linux/gfp.h>
+#include <linux/vmalloc.h>
 
 #include "iwl-drv.h"
 #include "iwl-trans.h"
 #include "iwl-fw-error-dump.h"
 #include "internal.h"
 
+static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       if (!trans_pcie->fw_mon_page)
+               return;
+
+       dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys,
+                      trans_pcie->fw_mon_size, DMA_FROM_DEVICE);
+       __free_pages(trans_pcie->fw_mon_page,
+                    get_order(trans_pcie->fw_mon_size));
+       trans_pcie->fw_mon_page = NULL;
+       trans_pcie->fw_mon_phys = 0;
+       trans_pcie->fw_mon_size = 0;
+}
+
+static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       struct page *page;
+       dma_addr_t phys;
+       u32 size;
+       u8 power;
+
+       if (trans_pcie->fw_mon_page) {
+               dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys,
+                                          trans_pcie->fw_mon_size,
+                                          DMA_FROM_DEVICE);
+               return;
+       }
+
+       phys = 0;
+       for (power = 26; power >= 11; power--) {
+               int order;
+
+               size = BIT(power);
+               order = get_order(size);
+               page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO,
+                                  order);
+               if (!page)
+                       continue;
+
+               phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order,
+                                   DMA_FROM_DEVICE);
+               if (dma_mapping_error(trans->dev, phys)) {
+                       __free_pages(page, order);
+                       continue;
+               }
+               IWL_INFO(trans,
+                        "Allocated 0x%08x bytes (order %d) for firmware monitor.\n",
+                        size, order);
+               break;
+       }
+
+       if (!page)
+               return;
+
+       trans_pcie->fw_mon_page = page;
+       trans_pcie->fw_mon_phys = phys;
+       trans_pcie->fw_mon_size = size;
+}
+
 static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
 {
        iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
@@ -675,6 +738,7 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
 static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
                                const struct fw_img *image)
 {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret = 0;
        int first_ucode_section;
 
@@ -733,6 +797,20 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
                        return ret;
        }
 
+       /* supported for 7000 only for the moment */
+       if (iwlwifi_mod_params.fw_monitor &&
+           trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+               iwl_pcie_alloc_fw_monitor(trans);
+
+               if (trans_pcie->fw_mon_size) {
+                       iwl_write_prph(trans, MON_BUFF_BASE_ADDR,
+                                      trans_pcie->fw_mon_phys >> 4);
+                       iwl_write_prph(trans, MON_BUFF_END_ADDR,
+                                      (trans_pcie->fw_mon_phys +
+                                       trans_pcie->fw_mon_size) >> 4);
+               }
+       }
+
        /* release CPU reset */
        if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
                iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
@@ -1126,6 +1204,8 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
        if (trans_pcie->napi.poll)
                netif_napi_del(&trans_pcie->napi);
 
+       iwl_pcie_free_fw_monitor(trans);
+
        kfree(trans);
 }
 
@@ -1494,10 +1574,12 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
                txq = &trans_pcie->txq[cnt];
                q = &txq->q;
                pos += scnprintf(buf + pos, bufsz - pos,
-                               "hwq %.2d: read=%u write=%u use=%d stop=%d\n",
+                               "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d%s\n",
                                cnt, q->read_ptr, q->write_ptr,
                                !!test_bit(cnt, trans_pcie->queue_used),
-                               !!test_bit(cnt, trans_pcie->queue_stopped));
+                                !!test_bit(cnt, trans_pcie->queue_stopped),
+                                txq->need_update,
+                                (cnt == trans_pcie->cmd_queue ? " HCMD" : ""));
        }
        ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
        kfree(buf);
@@ -1519,6 +1601,10 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
                                                rxq->read);
        pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n",
                                                rxq->write);
+       pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n",
+                                               rxq->write_actual);
+       pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n",
+                                               rxq->need_update);
        pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n",
                                                rxq->free_count);
        if (rxq->rb_stts) {
@@ -1688,23 +1774,207 @@ static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
        return cmdlen;
 }
 
-static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
-                                   void *buf, u32 buflen)
+static const struct {
+       u32 start, end;
+} iwl_prph_dump_addr[] = {
+       { .start = 0x00a00000, .end = 0x00a00000 },
+       { .start = 0x00a0000c, .end = 0x00a00024 },
+       { .start = 0x00a0002c, .end = 0x00a0003c },
+       { .start = 0x00a00410, .end = 0x00a00418 },
+       { .start = 0x00a00420, .end = 0x00a00420 },
+       { .start = 0x00a00428, .end = 0x00a00428 },
+       { .start = 0x00a00430, .end = 0x00a0043c },
+       { .start = 0x00a00444, .end = 0x00a00444 },
+       { .start = 0x00a004c0, .end = 0x00a004cc },
+       { .start = 0x00a004d8, .end = 0x00a004d8 },
+       { .start = 0x00a004e0, .end = 0x00a004f0 },
+       { .start = 0x00a00840, .end = 0x00a00840 },
+       { .start = 0x00a00850, .end = 0x00a00858 },
+       { .start = 0x00a01004, .end = 0x00a01008 },
+       { .start = 0x00a01010, .end = 0x00a01010 },
+       { .start = 0x00a01018, .end = 0x00a01018 },
+       { .start = 0x00a01024, .end = 0x00a01024 },
+       { .start = 0x00a0102c, .end = 0x00a01034 },
+       { .start = 0x00a0103c, .end = 0x00a01040 },
+       { .start = 0x00a01048, .end = 0x00a01094 },
+       { .start = 0x00a01c00, .end = 0x00a01c20 },
+       { .start = 0x00a01c58, .end = 0x00a01c58 },
+       { .start = 0x00a01c7c, .end = 0x00a01c7c },
+       { .start = 0x00a01c28, .end = 0x00a01c54 },
+       { .start = 0x00a01c5c, .end = 0x00a01c5c },
+       { .start = 0x00a01c84, .end = 0x00a01c84 },
+       { .start = 0x00a01ce0, .end = 0x00a01d0c },
+       { .start = 0x00a01d18, .end = 0x00a01d20 },
+       { .start = 0x00a01d2c, .end = 0x00a01d30 },
+       { .start = 0x00a01d40, .end = 0x00a01d5c },
+       { .start = 0x00a01d80, .end = 0x00a01d80 },
+       { .start = 0x00a01d98, .end = 0x00a01d98 },
+       { .start = 0x00a01dc0, .end = 0x00a01dfc },
+       { .start = 0x00a01e00, .end = 0x00a01e2c },
+       { .start = 0x00a01e40, .end = 0x00a01e60 },
+       { .start = 0x00a01e84, .end = 0x00a01e90 },
+       { .start = 0x00a01e9c, .end = 0x00a01ec4 },
+       { .start = 0x00a01ed0, .end = 0x00a01ed0 },
+       { .start = 0x00a01f00, .end = 0x00a01f14 },
+       { .start = 0x00a01f44, .end = 0x00a01f58 },
+       { .start = 0x00a01f80, .end = 0x00a01fa8 },
+       { .start = 0x00a01fb0, .end = 0x00a01fbc },
+       { .start = 0x00a01ff8, .end = 0x00a01ffc },
+       { .start = 0x00a02000, .end = 0x00a02048 },
+       { .start = 0x00a02068, .end = 0x00a020f0 },
+       { .start = 0x00a02100, .end = 0x00a02118 },
+       { .start = 0x00a02140, .end = 0x00a0214c },
+       { .start = 0x00a02168, .end = 0x00a0218c },
+       { .start = 0x00a021c0, .end = 0x00a021c0 },
+       { .start = 0x00a02400, .end = 0x00a02410 },
+       { .start = 0x00a02418, .end = 0x00a02420 },
+       { .start = 0x00a02428, .end = 0x00a0242c },
+       { .start = 0x00a02434, .end = 0x00a02434 },
+       { .start = 0x00a02440, .end = 0x00a02460 },
+       { .start = 0x00a02468, .end = 0x00a024b0 },
+       { .start = 0x00a024c8, .end = 0x00a024cc },
+       { .start = 0x00a02500, .end = 0x00a02504 },
+       { .start = 0x00a0250c, .end = 0x00a02510 },
+       { .start = 0x00a02540, .end = 0x00a02554 },
+       { .start = 0x00a02580, .end = 0x00a025f4 },
+       { .start = 0x00a02600, .end = 0x00a0260c },
+       { .start = 0x00a02648, .end = 0x00a02650 },
+       { .start = 0x00a02680, .end = 0x00a02680 },
+       { .start = 0x00a026c0, .end = 0x00a026d0 },
+       { .start = 0x00a02700, .end = 0x00a0270c },
+       { .start = 0x00a02804, .end = 0x00a02804 },
+       { .start = 0x00a02818, .end = 0x00a0281c },
+       { .start = 0x00a02c00, .end = 0x00a02db4 },
+       { .start = 0x00a02df4, .end = 0x00a02fb0 },
+       { .start = 0x00a03000, .end = 0x00a03014 },
+       { .start = 0x00a0301c, .end = 0x00a0302c },
+       { .start = 0x00a03034, .end = 0x00a03038 },
+       { .start = 0x00a03040, .end = 0x00a03048 },
+       { .start = 0x00a03060, .end = 0x00a03068 },
+       { .start = 0x00a03070, .end = 0x00a03074 },
+       { .start = 0x00a0307c, .end = 0x00a0307c },
+       { .start = 0x00a03080, .end = 0x00a03084 },
+       { .start = 0x00a0308c, .end = 0x00a03090 },
+       { .start = 0x00a03098, .end = 0x00a03098 },
+       { .start = 0x00a030a0, .end = 0x00a030a0 },
+       { .start = 0x00a030a8, .end = 0x00a030b4 },
+       { .start = 0x00a030bc, .end = 0x00a030bc },
+       { .start = 0x00a030c0, .end = 0x00a0312c },
+       { .start = 0x00a03c00, .end = 0x00a03c5c },
+       { .start = 0x00a04400, .end = 0x00a04454 },
+       { .start = 0x00a04460, .end = 0x00a04474 },
+       { .start = 0x00a044c0, .end = 0x00a044ec },
+       { .start = 0x00a04500, .end = 0x00a04504 },
+       { .start = 0x00a04510, .end = 0x00a04538 },
+       { .start = 0x00a04540, .end = 0x00a04548 },
+       { .start = 0x00a04560, .end = 0x00a0457c },
+       { .start = 0x00a04590, .end = 0x00a04598 },
+       { .start = 0x00a045c0, .end = 0x00a045f4 },
+};
+
+static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans,
+                                   struct iwl_fw_error_dump_data **data)
+{
+       struct iwl_fw_error_dump_prph *prph;
+       unsigned long flags;
+       u32 prph_len = 0, i;
+
+       if (!iwl_trans_grab_nic_access(trans, false, &flags))
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
+               /* The range includes both boundaries */
+               int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
+                        iwl_prph_dump_addr[i].start + 4;
+               int reg;
+               __le32 *val;
+
+               prph_len += sizeof(*data) + sizeof(*prph) +
+                       num_bytes_in_chunk;
+
+               (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
+               (*data)->len = cpu_to_le32(sizeof(*prph) +
+                                       num_bytes_in_chunk);
+               prph = (void *)(*data)->data;
+               prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
+               val = (void *)prph->data;
+
+               for (reg = iwl_prph_dump_addr[i].start;
+                    reg <= iwl_prph_dump_addr[i].end;
+                    reg += 4)
+                       *val++ = cpu_to_le32(iwl_trans_pcie_read_prph(trans,
+                                                                     reg));
+               *data = iwl_fw_error_next_data(*data);
+       }
+
+       iwl_trans_release_nic_access(trans, &flags);
+
+       return prph_len;
+}
+
+#define IWL_CSR_TO_DUMP (0x250)
+
+static u32 iwl_trans_pcie_dump_csr(struct iwl_trans *trans,
+                                  struct iwl_fw_error_dump_data **data)
+{
+       u32 csr_len = sizeof(**data) + IWL_CSR_TO_DUMP;
+       __le32 *val;
+       int i;
+
+       (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR);
+       (*data)->len = cpu_to_le32(IWL_CSR_TO_DUMP);
+       val = (void *)(*data)->data;
+
+       for (i = 0; i < IWL_CSR_TO_DUMP; i += 4)
+               *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
+
+       *data = iwl_fw_error_next_data(*data);
+
+       return csr_len;
+}
+
+static
+struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_fw_error_dump_data *data;
        struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue];
        struct iwl_fw_error_dump_txcmd *txcmd;
+       struct iwl_trans_dump_data *dump_data;
        u32 len;
        int i, ptr;
 
-       if (!buf)
-               return sizeof(*data) +
-                      cmdq->q.n_window * (sizeof(*txcmd) +
-                                          TFD_MAX_PAYLOAD_SIZE);
+       /* transport dump header */
+       len = sizeof(*dump_data);
+
+       /* host commands */
+       len += sizeof(*data) +
+               cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
+
+       /* CSR registers */
+       len += sizeof(*data) + IWL_CSR_TO_DUMP;
+
+       /* PRPH registers */
+       for (i = 0; i < ARRAY_SIZE(iwl_prph_dump_addr); i++) {
+               /* The range includes both boundaries */
+               int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
+                       iwl_prph_dump_addr[i].start + 4;
+
+               len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_prph) +
+                       num_bytes_in_chunk;
+       }
+
+       /* FW monitor */
+       if (trans_pcie->fw_mon_page)
+               len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
+                       trans_pcie->fw_mon_size;
+
+       dump_data = vzalloc(len);
+       if (!dump_data)
+               return NULL;
 
        len = 0;
-       data = buf;
+       data = (void *)dump_data->data;
        data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD);
        txcmd = (void *)data->data;
        spin_lock_bh(&cmdq->lock);
@@ -1729,7 +1999,46 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
        spin_unlock_bh(&cmdq->lock);
 
        data->len = cpu_to_le32(len);
-       return sizeof(*data) + len;
+       len += sizeof(*data);
+       data = iwl_fw_error_next_data(data);
+
+       len += iwl_trans_pcie_dump_prph(trans, &data);
+       len += iwl_trans_pcie_dump_csr(trans, &data);
+       /* data is already pointing to the next section */
+
+       if (trans_pcie->fw_mon_page) {
+               struct iwl_fw_error_dump_fw_mon *fw_mon_data;
+
+               data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
+               data->len = cpu_to_le32(trans_pcie->fw_mon_size +
+                                       sizeof(*fw_mon_data));
+               fw_mon_data = (void *)data->data;
+               fw_mon_data->fw_mon_wr_ptr =
+                       cpu_to_le32(iwl_read_prph(trans, MON_BUFF_WRPTR));
+               fw_mon_data->fw_mon_cycle_cnt =
+                       cpu_to_le32(iwl_read_prph(trans, MON_BUFF_CYCLE_CNT));
+               fw_mon_data->fw_mon_base_ptr =
+                       cpu_to_le32(iwl_read_prph(trans, MON_BUFF_BASE_ADDR));
+
+               /*
+                * The firmware is now asserted, it won't write anything to
+                * the buffer. CPU can take ownership to fetch the data.
+                * The buffer will be handed back to the device before the
+                * firmware will be restarted.
+                */
+               dma_sync_single_for_cpu(trans->dev, trans_pcie->fw_mon_phys,
+                                       trans_pcie->fw_mon_size,
+                                       DMA_FROM_DEVICE);
+               memcpy(fw_mon_data->data, page_address(trans_pcie->fw_mon_page),
+                      trans_pcie->fw_mon_size);
+
+               len += sizeof(*data) + sizeof(*fw_mon_data) +
+                       trans_pcie->fw_mon_size;
+       }
+
+       dump_data->len = len;
+
+       return dump_data;
 }
 #else
 static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
@@ -1870,6 +2179,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        }
 
        trans->hw_rev = iwl_read32(trans, CSR_HW_REV);
+       /*
+        * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
+        * changed, and now the revision step also includes bit 0-1 (no more
+        * "dash" value). To keep hw_rev backwards compatible - we'll store it
+        * in the old format.
+        */
+       if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+               trans->hw_rev = (trans->hw_rev & 0xfff0) |
+                               ((trans->hw_rev << 2) & 0xc);
+
        trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
        snprintf(trans->hw_id_str, sizeof(trans->hw_id_str),
                 "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device);
index 038940afbdc57d8d176908bb4869fc8791f250eb..6acccb19c4f3030956e0d701a037c1227d7121b1 100644 (file)
@@ -1438,6 +1438,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
                                   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
                        spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
                        trans_pcie->cmd_in_flight = false;
+                       IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
                        idx = -EIO;
                        goto out;
                }
index 0485c99575757841edb0f929205d4963c6d082a2..e6268ceacbf1e5ddeb8e0d6c2ab321523e289801 100644 (file)
@@ -16,7 +16,7 @@ config LIBERTAS_USB
 
 config LIBERTAS_CS
        tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards"
-       depends on LIBERTAS && PCMCIA
+       depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP
        ---help---
          A driver for Marvell Libertas 8385 CompactFlash devices.
 
index aaa297315c47102df73b6fd3948e4b8fd4cbba25..0387a5b380c80f5eeafd05c53c3ec12fc5b892f5 100644 (file)
@@ -1111,6 +1111,7 @@ int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
 
        cmd.hdr.size = cpu_to_le16(sizeof(cmd));
        cmd.action = cpu_to_le16(CMD_ACT_SET);
+       cmd.control = 0;
 
        /* Only v8 and below support setting the preamble */
        if (priv->fwrelease < 0x09000000) {
index a312c653d1163fcc5c4ff394a54b0c7a96370d8f..f39f504cc3a0870bd57605c0f944d59bb82f9524 100644 (file)
@@ -685,11 +685,16 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
        struct mac80211_hwsim_data *data = hw->priv;
        u64 now = mac80211_hwsim_get_tsf(hw, vif);
        u32 bcn_int = data->beacon_int;
-       s64 delta = tsf - now;
+       u64 delta = abs64(tsf - now);
 
-       data->tsf_offset += delta;
        /* adjust after beaconing with new timestamp at old TBTT */
-       data->bcn_delta = do_div(delta, bcn_int);
+       if (tsf > now) {
+               data->tsf_offset += delta;
+               data->bcn_delta = do_div(delta, bcn_int);
+       } else {
+               data->tsf_offset -= delta;
+               data->bcn_delta = -do_div(delta, bcn_int);
+       }
 }
 
 static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
@@ -781,6 +786,36 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan,
        netif_rx(skb);
 }
 
+struct mac80211_hwsim_addr_match_data {
+       u8 addr[ETH_ALEN];
+       bool ret;
+};
+
+static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct mac80211_hwsim_addr_match_data *md = data;
+
+       if (memcmp(mac, md->addr, ETH_ALEN) == 0)
+               md->ret = true;
+}
+
+static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
+                                     const u8 *addr)
+{
+       struct mac80211_hwsim_addr_match_data md = {
+               .ret = false,
+       };
+
+       memcpy(md.addr, addr, ETH_ALEN);
+
+       ieee80211_iterate_active_interfaces_atomic(data->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  mac80211_hwsim_addr_iter,
+                                                  &md);
+
+       return md.ret;
+}
 
 static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
                           struct sk_buff *skb)
@@ -798,8 +833,7 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
                /* Allow unicast frames to own address if there is a pending
                 * PS-Poll */
                if (data->ps_poll_pending &&
-                   memcmp(data->hw->wiphy->perm_addr, skb->data + 4,
-                          ETH_ALEN) == 0) {
+                   mac80211_hwsim_addr_match(data, skb->data + 4)) {
                        data->ps_poll_pending = false;
                        return true;
                }
@@ -809,39 +843,6 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
        return true;
 }
 
-
-struct mac80211_hwsim_addr_match_data {
-       bool ret;
-       const u8 *addr;
-};
-
-static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
-                                    struct ieee80211_vif *vif)
-{
-       struct mac80211_hwsim_addr_match_data *md = data;
-       if (memcmp(mac, md->addr, ETH_ALEN) == 0)
-               md->ret = true;
-}
-
-
-static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
-                                     const u8 *addr)
-{
-       struct mac80211_hwsim_addr_match_data md;
-
-       if (memcmp(addr, data->hw->wiphy->perm_addr, ETH_ALEN) == 0)
-               return true;
-
-       md.ret = false;
-       md.addr = addr;
-       ieee80211_iterate_active_interfaces_atomic(data->hw,
-                                                  IEEE80211_IFACE_ITER_NORMAL,
-                                                  mac80211_hwsim_addr_iter,
-                                                  &md);
-
-       return md.ret;
-}
-
 static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
                                       struct sk_buff *my_skb,
                                       int dst_portid)
@@ -1740,9 +1741,10 @@ static void hw_scan_work(struct work_struct *work)
 
 static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
-                                 struct cfg80211_scan_request *req)
+                                 struct ieee80211_scan_request *hw_req)
 {
        struct mac80211_hwsim_data *hwsim = hw->priv;
+       struct cfg80211_scan_request *req = &hw_req->req;
 
        mutex_lock(&hwsim->mutex);
        if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
index 706831df1fa2a4183cb3c5ad849f1aa8df8dbb14..59d23fb2365f202112ac9e2a1a322885f0d791b6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11ac
  *
- * Copyright (C) 2013, Marvell International Ltd.
+ * Copyright (C) 2013-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 0b02cb6cfcb4d25ea496b21c2f737d94a0017e0c..1ca92c7a8a4a864ec3a2d370d1eda7bed7f163f3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11ac
  *
- * Copyright (C) 2013, Marvell International Ltd.
+ * Copyright (C) 2013-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index e76b0db4e3e6392236489affbfcba48b98468348..2668e83afbb65c52c58dddd2cea1a8261390d0a7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11h
  *
- * Copyright (C) 2013, Marvell International Ltd.
+ * Copyright (C) 2013-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index e1c2f67ae85e694d52b1f9e4ad69f2d50ab6ba91..62f5dbe602d3de46f8daf6408f776670e024fe05 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11n
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -541,7 +541,6 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid,
 int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
 {
        struct host_cmd_ds_11n_addba_req add_ba_req;
-       struct mwifiex_sta_node *sta_ptr;
        u32 tx_win_size = priv->add_ba_param.tx_win_size;
        static u8 dialog_tok;
        int ret;
@@ -553,6 +552,8 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
            ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
            priv->adapter->is_hw_11ac_capable &&
            memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
+               struct mwifiex_sta_node *sta_ptr;
+
                sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
                if (!sta_ptr) {
                        dev_warn(priv->adapter->dev,
index 0b73fa08f5d466b98d5292d0a5b16e1011c30be3..2ee268b632be56ca1aa994ed9b1de7baece31d3d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11n
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index fe0f66f735076d68aa7cef6e19ab791d34411b4a..8720a3d3c755c6065dd9db413ee708680647b531 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11n Aggregation
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 892098d6a69687dd2d8c1fc61612a6fb9999d754..0cd2a3eb6c178ad3314415658df9f36f1a6866f7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11n Aggregation
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 0c3571f830b0d70cc609e64d9b0b6fde3cf17209..06a2c215ef5e13129a53e72add714c05cbb77489 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11n RX Re-ordering
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -249,13 +249,22 @@ void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
  * buffered in Rx reordering table.
  */
 static int
-mwifiex_11n_find_last_seq_num(struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr)
+mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
 {
+       struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
+       struct mwifiex_private *priv = ctx->priv;
+       unsigned long flags;
        int i;
 
-       for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i)
-               if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
+               if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              flags);
                        return i;
+               }
+       }
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
        return -1;
 }
@@ -274,7 +283,7 @@ mwifiex_flush_data(unsigned long context)
                (struct reorder_tmr_cnxt *) context;
        int start_win, seq_num;
 
-       seq_num = mwifiex_11n_find_last_seq_num(ctx->ptr);
+       seq_num = mwifiex_11n_find_last_seq_num(ctx);
 
        if (seq_num < 0)
                return;
@@ -729,9 +738,9 @@ void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv)
                mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr);
                spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
        }
+       INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
        spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
 
-       INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
        mwifiex_reset_11n_rx_seq_num(priv);
 }
 
@@ -749,10 +758,14 @@ void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags)
                priv = adapter->priv[i];
                if (!priv)
                        continue;
-               if (list_empty(&priv->rx_reorder_tbl_ptr))
-                       continue;
 
                spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags);
+               if (list_empty(&priv->rx_reorder_tbl_ptr)) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              lock_flags);
+                       continue;
+               }
+
                list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list)
                        tbl->flags = flags;
                spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags);
index 0fc76e4a60f886c32d3e46cf885cbcc809ee9893..3a87bb0e3a62adb477784a94feba13cefd32626b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: 802.11n RX Re-ordering
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 2aa208ffbe233eefc06f9cdb24c64db9c56f3188..9487d728ac20acb46521089111ebd46ff5ab8a83 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (C) 2011, Marvell International Ltd.
+# Copyright (C) 2011-2014, Marvell International Ltd.
 #
 # This software file (the "File") is distributed by Marvell International
 # Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 3b55ce5690a54e226c5482f523a3c80d1e95d7bf..31928caeeed225edbae57f38bd7488edee7b726d 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2011, Marvell International Ltd.
+# Copyright (C) 2011-2014, Marvell International Ltd.
 #
 # This software file (the "File") is distributed by Marvell International
 # Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -194,6 +194,36 @@ rdeeprom
        Example:
                echo "0 20" > rdeeprom      : Read 20 bytes of EEPROM data from offset 0
 
+hscfg
+       This command is used to debug/simulate host sleep feature using
+       different configuration parameters.
+
+       Usage:
+               echo "<condition> [GPIO# [gap]]]" > hscfg
+               cat hscfg
+
+       where the parameters are,
+               <condition>: bit 0 = 1   -- broadcast data
+                            bit 1 = 1   -- unicast data
+                            bit 2 = 1   -- mac event
+                            bit 3 = 1   -- multicast data
+               [GPIO#]: pin number of GPIO used to wakeup the host.
+                        GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO
+                        will be used instead).
+               [gap]:   the gap in milliseconds between wakeup signal and
+                        wakeup event or 0xff for special setting (host
+                        acknowledge required) when GPIO is used to wakeup host.
+
+       Examples:
+               echo "-1" > hscfg        : Cancel host sleep mode
+               echo "3" > hscfg         : Broadcast and unicast data;
+                                          Use GPIO and gap set previously
+               echo "2 3" > hscfg       : Unicast data and GPIO 3;
+                                          Use gap set previously
+               echo "2 1 160" > hscfg   : Unicast data, GPIO 1 and gap 160 ms
+               echo "2 1 0xff" > hscfg  : Unicast data, GPIO 1; Wait for host
+                                          to ack before sending wakeup event
+
 getlog
         This command is used to get the statistics available in the station.
        Usage:
index b511613bba2d8608f057fd15223c2af33c3be962..a738cc959a478cdf4f2cab7a061c9f3688202156 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: CFG80211
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -42,36 +42,6 @@ static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = {
        .beacon_int_infra_match = true,
 };
 
-static const struct ieee80211_regdomain mwifiex_world_regdom_custom = {
-       .n_reg_rules = 7,
-       .alpha2 =  "99",
-       .reg_rules = {
-               /* Channel 1 - 11 */
-               REG_RULE(2412-10, 2462+10, 40, 3, 20, 0),
-               /* Channel 12 - 13 */
-               REG_RULE(2467-10, 2472+10, 20, 3, 20,
-                        NL80211_RRF_NO_IR),
-               /* Channel 14 */
-               REG_RULE(2484-10, 2484+10, 20, 3, 20,
-                        NL80211_RRF_NO_IR |
-                        NL80211_RRF_NO_OFDM),
-               /* Channel 36 - 48 */
-               REG_RULE(5180-10, 5240+10, 40, 3, 20,
-                        NL80211_RRF_NO_IR),
-               /* Channel 149 - 165 */
-               REG_RULE(5745-10, 5825+10, 40, 3, 20,
-                        NL80211_RRF_NO_IR),
-               /* Channel 52 - 64 */
-               REG_RULE(5260-10, 5320+10, 40, 3, 30,
-                        NL80211_RRF_NO_IR |
-                        NL80211_RRF_DFS),
-               /* Channel 100 - 140 */
-               REG_RULE(5500-10, 5700+10, 40, 3, 30,
-                        NL80211_RRF_NO_IR |
-                        NL80211_RRF_DFS),
-       }
-};
-
 /*
  * This function maps the nl802.11 channel type into driver channel type.
  *
@@ -151,7 +121,6 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
        u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
        u16 pkt_len;
        u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT;
-       struct timeval tv;
 
        pkt_len = len + ETH_ALEN;
 
@@ -173,8 +142,7 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
               len - sizeof(struct ieee80211_hdr_3addr));
 
        skb->priority = LOW_PRIO_TID;
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
 
        return 0;
 }
@@ -1636,9 +1604,6 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len,
                return -EINVAL;
        }
 
-       /* disconnect before try to associate */
-       mwifiex_deauthenticate(priv, NULL);
-
        /* As this is new association, clear locally stored
         * keys and security related flags */
        priv->sec_info.wpa_enabled = false;
@@ -1776,6 +1741,11 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
                return -EINVAL;
        }
 
+       if (priv->wdev && priv->wdev->current_bss) {
+               wiphy_warn(wiphy, "%s: already connected\n", dev->name);
+               return -EALREADY;
+       }
+
        wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n",
                  (char *) sme->ssid, sme->bssid);
 
@@ -2484,6 +2454,16 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
                mef_entry->filter[filt_num].filt_type = TYPE_EQ;
                if (filt_num)
                        mef_entry->filter[filt_num].filt_action = TYPE_OR;
+
+               filt_num++;
+               mef_entry->filter[filt_num].repeat = 16;
+               memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
+                      ETH_ALEN);
+               mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+                                                               ETH_ALEN;
+               mef_entry->filter[filt_num].offset = 56;
+               mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+               mef_entry->filter[filt_num].filt_action = TYPE_OR;
        }
 
        if (!mef_cfg.criteria)
@@ -2632,7 +2612,8 @@ static int
 mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
                           const u8 *peer, u8 action_code, u8 dialog_token,
                           u16 status_code, u32 peer_capability,
-                          const u8 *extra_ies, size_t extra_ies_len)
+                          bool initiator, const u8 *extra_ies,
+                          size_t extra_ies_len)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        int ret;
@@ -2917,12 +2898,6 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
                wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
                                WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
 
-       wiphy->regulatory_flags |=
-                       REGULATORY_CUSTOM_REG |
-                       REGULATORY_STRICT_REG;
-
-       wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
-
 #ifdef CONFIG_PM
        wiphy->wowlan = &mwifiex_wowlan_support;
 #endif
index c5848934f1117d15e343c9e925f407a7d03a34de..908367857d58918f6e00f78d1b2b996acda06dc4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: CFG80211
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 0ddec3d4b059cbd7d03221578035d5b1721db9b2..b8242eb2be6fd272906c605532f47b986ae61c08 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: Channel, Frequence and Power
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index c161141f6c39ec8c2bcf5d8e9a2a2951c9f94a71..baf0aab63c04d17bb1d01501e0c29f524b4a5f57 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: commands and events
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -137,7 +137,6 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
        struct host_cmd_ds_command *host_cmd;
        uint16_t cmd_code;
        uint16_t cmd_size;
-       struct timeval tstamp;
        unsigned long flags;
        __le32 tmp;
 
@@ -198,10 +197,8 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
                 */
                skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
 
-       do_gettimeofday(&tstamp);
-       dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d,"
-               " seqno %#x\n",
-               tstamp.tv_sec, tstamp.tv_usec, cmd_code,
+       dev_dbg(adapter->dev,
+               "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", cmd_code,
                le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)), cmd_size,
                le16_to_cpu(host_cmd->seq_num));
 
@@ -283,6 +280,13 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
                                        (adapter->seq_num, priv->bss_num,
                                         priv->bss_type)));
 
+       dev_dbg(adapter->dev,
+               "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
+               le16_to_cpu(sleep_cfm_buf->command),
+               le16_to_cpu(sleep_cfm_buf->action),
+               le16_to_cpu(sleep_cfm_buf->size),
+               le16_to_cpu(sleep_cfm_buf->seq_num));
+
        if (adapter->iface_type == MWIFIEX_USB) {
                sleep_cfm_tmp =
                        dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)
@@ -433,7 +437,6 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
                mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
        struct sk_buff *skb = adapter->event_skb;
        u32 eventcause = adapter->event_cause;
-       struct timeval tstamp;
        struct mwifiex_rxinfo *rx_info;
 
        /* Save the last event to debug log */
@@ -458,11 +461,8 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
                rx_info->bss_type = priv->bss_type;
        }
 
-       if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) {
-               do_gettimeofday(&tstamp);
-               dev_dbg(adapter->dev, "event: %lu.%lu: cause: %#x\n",
-                       tstamp.tv_sec, tstamp.tv_usec, eventcause);
-       } else {
+       dev_dbg(adapter->dev, "EVENT: cause: %#x\n", eventcause);
+       if (eventcause == EVENT_PS_SLEEP || eventcause == EVENT_PS_AWAKE) {
                /* Handle PS_SLEEP/AWAKE events on STA */
                priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
                if (!priv)
@@ -773,7 +773,6 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
        uint16_t orig_cmdresp_no;
        uint16_t cmdresp_no;
        uint16_t cmdresp_result;
-       struct timeval tstamp;
        unsigned long flags;
 
        /* Now we got response from FW, cancel the command timer */
@@ -831,11 +830,10 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
        adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] =
                                                                orig_cmdresp_no;
 
-       do_gettimeofday(&tstamp);
-       dev_dbg(adapter->dev, "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d,"
-               " len %d, seqno 0x%x\n",
-              tstamp.tv_sec, tstamp.tv_usec, orig_cmdresp_no, cmdresp_result,
-              le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
+       dev_dbg(adapter->dev,
+               "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
+               orig_cmdresp_no, cmdresp_result,
+               le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
 
        if (!(orig_cmdresp_no & HostCmd_RET_BIT)) {
                dev_err(adapter->dev, "CMD_RESP: invalid cmd resp\n");
@@ -895,7 +893,6 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
        struct mwifiex_adapter *adapter =
                (struct mwifiex_adapter *) function_context;
        struct cmd_ctrl_node *cmd_node;
-       struct timeval tstamp;
 
        adapter->is_cmd_timedout = 1;
        if (!adapter->curr_cmd) {
@@ -908,10 +905,8 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
                        adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index];
                adapter->dbg.timeout_cmd_act =
                        adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index];
-               do_gettimeofday(&tstamp);
                dev_err(adapter->dev,
-                       "%s: Timeout cmd id (%lu.%lu) = %#x, act = %#x\n",
-                       __func__, tstamp.tv_sec, tstamp.tv_usec,
+                       "%s: Timeout cmd id = %#x, act = %#x\n", __func__,
                        adapter->dbg.timeout_cmd_id,
                        adapter->dbg.timeout_cmd_act);
 
@@ -961,6 +956,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
        if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
                mwifiex_init_fw_complete(adapter);
 
+       if (adapter->if_ops.fw_dump)
+               adapter->if_ops.fw_dump(adapter);
+
        if (adapter->if_ops.card_reset)
                adapter->if_ops.card_reset(adapter);
 }
@@ -1232,6 +1230,10 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
                return;
        }
 
+       dev_dbg(adapter->dev,
+               "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
+               command, result, le16_to_cpu(cmd->size), seq_num);
+
        /* Get BSS number and corresponding priv */
        priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num),
                                      HostCmd_GET_BSS_TYPE(seq_num));
index 7b419bbcd5444f5c5abdf40ffb2368087b77e89a..2713f7acd35e6d45081482c36b983a8a92af89a3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: debugfs
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -692,6 +692,97 @@ done:
        return ret;
 }
 
+/* Proc hscfg file write handler
+ * This function can be used to configure the host sleep parameters.
+ */
+static ssize_t
+mwifiex_hscfg_write(struct file *file, const char __user *ubuf,
+                   size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv = (void *)file->private_data;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+       size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+       int ret, arg_num;
+       struct mwifiex_ds_hs_cfg hscfg;
+       int conditions = HS_CFG_COND_DEF;
+       u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;
+
+       if (!buf)
+               return -ENOMEM;
+
+       if (copy_from_user(buf, ubuf, buf_size)) {
+               ret = -EFAULT;
+               goto done;
+       }
+
+       arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);
+
+       memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
+
+       if (arg_num > 3) {
+               dev_err(priv->adapter->dev, "Too many arguments\n");
+               ret = -EINVAL;
+               goto done;
+       }
+
+       if (arg_num >= 1 && arg_num < 3)
+               mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
+                                     MWIFIEX_SYNC_CMD, &hscfg);
+
+       if (arg_num) {
+               if (conditions == HS_CFG_CANCEL) {
+                       mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD);
+                       ret = count;
+                       goto done;
+               }
+               hscfg.conditions = conditions;
+       }
+       if (arg_num >= 2)
+               hscfg.gpio = gpio;
+       if (arg_num == 3)
+               hscfg.gap = gap;
+
+       hscfg.is_invoke_hostcmd = false;
+       mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
+                             MWIFIEX_SYNC_CMD, &hscfg);
+
+       mwifiex_enable_hs(priv->adapter);
+       priv->adapter->hs_enabling = false;
+       ret = count;
+done:
+       free_page(addr);
+       return ret;
+}
+
+/* Proc hscfg file read handler
+ * This function can be used to read host sleep configuration
+ * parameters from driver.
+ */
+static ssize_t
+mwifiex_hscfg_read(struct file *file, char __user *ubuf,
+                  size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv = (void *)file->private_data;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+       int pos, ret;
+       struct mwifiex_ds_hs_cfg hscfg;
+
+       if (!buf)
+               return -ENOMEM;
+
+       mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
+                             MWIFIEX_SYNC_CMD, &hscfg);
+
+       pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions,
+                      hscfg.gpio, hscfg.gap);
+
+       ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+       free_page(addr);
+       return ret;
+}
 
 #define MWIFIEX_DFS_ADD_FILE(name) do {                                 \
        if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir,        \
@@ -725,6 +816,7 @@ MWIFIEX_DFS_FILE_READ_OPS(getlog);
 MWIFIEX_DFS_FILE_READ_OPS(fw_dump);
 MWIFIEX_DFS_FILE_OPS(regrdwr);
 MWIFIEX_DFS_FILE_OPS(rdeeprom);
+MWIFIEX_DFS_FILE_OPS(hscfg);
 
 /*
  * This function creates the debug FS directory structure and the files.
@@ -747,6 +839,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
        MWIFIEX_DFS_ADD_FILE(regrdwr);
        MWIFIEX_DFS_ADD_FILE(rdeeprom);
        MWIFIEX_DFS_ADD_FILE(fw_dump);
+       MWIFIEX_DFS_ADD_FILE(hscfg);
 }
 
 /*
index 38da6ff6f41623618efa22add335ffef1fa46828..0e03fe39fc35ea64fca0933a20b46966686a5208 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: generic data structures and APIs
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index bfb39908b2c694b0c58b2ee2695b22032bb746ad..04e56b5fc5354eea46d6cf44b88707bed3f19367 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: ethtool
  *
- * Copyright (C) 2013, Marvell International Ltd.
+ * Copyright (C) 2013-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -64,7 +64,90 @@ static int mwifiex_ethtool_set_wol(struct net_device *dev,
        return 0;
 }
 
+static int
+mwifiex_get_dump_flag(struct net_device *dev, struct ethtool_dump *dump)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct memory_type_mapping *entry;
+
+       if (!adapter->if_ops.fw_dump)
+               return -ENOTSUPP;
+
+       dump->flag = adapter->curr_mem_idx;
+       dump->version = 1;
+       if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) {
+               entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];
+               dump->len = entry->mem_size;
+       } else {
+               dump->len = 0;
+       }
+
+       return 0;
+}
+
+static int
+mwifiex_get_dump_data(struct net_device *dev, struct ethtool_dump *dump,
+                     void *buffer)
+{
+       u8 *p = buffer;
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct memory_type_mapping *entry;
+
+       if (!adapter->if_ops.fw_dump)
+               return -ENOTSUPP;
+
+       if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
+               dev_err(adapter->dev, "firmware dump in progress!!\n");
+               return -EBUSY;
+       }
+
+       entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];
+
+       if (!entry->mem_ptr)
+               return -EFAULT;
+
+       memcpy(p, entry->mem_ptr, entry->mem_size);
+
+       entry->mem_size = 0;
+       vfree(entry->mem_ptr);
+       entry->mem_ptr = NULL;
+
+       return 0;
+}
+
+static int mwifiex_set_dump(struct net_device *dev, struct ethtool_dump *val)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       if (!adapter->if_ops.fw_dump)
+               return -ENOTSUPP;
+
+       if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
+               dev_err(adapter->dev, "firmware dump in progress!!\n");
+               return -EBUSY;
+       }
+
+       if (val->flag == MWIFIEX_FW_DUMP_IDX) {
+               adapter->curr_mem_idx = val->flag;
+               adapter->if_ops.fw_dump(adapter);
+               return 0;
+       }
+
+       if (val->flag < 0 || val->flag >= adapter->num_mem_types)
+               return -EINVAL;
+
+       adapter->curr_mem_idx = val->flag;
+
+       return 0;
+}
+
 const struct ethtool_ops mwifiex_ethtool_ops = {
        .get_wol = mwifiex_ethtool_get_wol,
        .set_wol = mwifiex_ethtool_set_wol,
+       .get_dump_flag = mwifiex_get_dump_flag,
+       .get_dump_data = mwifiex_get_dump_data,
+       .set_dump = mwifiex_set_dump,
 };
index 3175dd04834b9960698c67e750fc3d8928075088..49da2d53d29455830fb9958bc4ab7dbbcd5d5666 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: Firmware specific macros & structures
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -713,7 +713,7 @@ struct mwifiex_ie_types_vendor_param_set {
        u8 ie[MWIFIEX_MAX_VSIE_LEN];
 };
 
-#define MWIFIEX_TDLS_IDLE_TIMEOUT      60
+#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC       60
 
 struct mwifiex_ie_types_tdls_idle_timeout {
        struct mwifiex_ie_types_header header;
index 3bf3d58bbc029b0a48a937a0c65d15a83552b295..b933794758b7171e01f8104d4b0f6b63320103be 100644 (file)
@@ -2,7 +2,7 @@
  * Marvell Wireless LAN device driver: management IE handling- setting and
  * deleting IE.
  *
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 4ecd0b208ac64f5be6ffa75b9f15a1be965250a2..269a277d0a2e6072c092f066c9b23943d8075419 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: HW/FW Initialization
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -382,6 +382,8 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
 static void
 mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
 {
+       int idx;
+
        if (!adapter) {
                pr_err("%s: adapter is NULL\n", __func__);
                return;
@@ -396,7 +398,16 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
        dev_dbg(adapter->dev, "info: free cmd buffer\n");
        mwifiex_free_cmd_buffer(adapter);
 
-       dev_dbg(adapter->dev, "info: free scan table\n");
+       for (idx = 0; idx < adapter->num_mem_types; idx++) {
+               struct memory_type_mapping *entry =
+                               &adapter->mem_type_mapping_tbl[idx];
+
+               if (entry->mem_ptr) {
+                       vfree(entry->mem_ptr);
+                       entry->mem_ptr = NULL;
+               }
+               entry->mem_size = 0;
+       }
 
        if (adapter->sleep_cfm)
                dev_kfree_skb_any(adapter->sleep_cfm);
index 1b576722671d5e6f228363c36c94c4ef47867980..0847f3e07ab7888e2f79eada0e07fd263620edcb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: ioctl data structures & APIs
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 89dc62a467f4d2ba8b7cc2fcf6b7d1d63b058f68..8d6c25908b6d012fc9f2ef3d9f9211fd5f09b862 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: association and ad-hoc start/join
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -949,7 +949,7 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
                                chan_tlv->chan_scan_param[0].radio_type |=
                                        (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4);
                        else if (adapter->sec_chan_offset ==
-                                           IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
+                                           IEEE80211_HT_PARAM_CHA_SEC_BELOW)
                                chan_tlv->chan_scan_param[0].radio_type |=
                                        (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4);
                }
@@ -1288,8 +1288,6 @@ done:
 int mwifiex_associate(struct mwifiex_private *priv,
                      struct mwifiex_bssdescriptor *bss_desc)
 {
-       u8 current_bssid[ETH_ALEN];
-
        /* Return error if the adapter is not STA role or table entry
         * is not marked as infra.
         */
@@ -1304,10 +1302,6 @@ int mwifiex_associate(struct mwifiex_private *priv,
        else
                mwifiex_set_ba_params(priv);
 
-       memcpy(&current_bssid,
-              &priv->curr_bss_params.bss_descriptor.mac_address,
-              sizeof(current_bssid));
-
        /* Clear any past association response stored for application
           retrieval */
        priv->assoc_rsp_size = 0;
index e91cd0fa5ca81e3585e8173a0fb6a1789cfdaca7..dfa37eadc4db566ecc75db890c838d8d04600917 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: major functions
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -33,6 +33,7 @@ static void scan_delay_timer_fn(unsigned long data)
        struct mwifiex_private *priv = (struct mwifiex_private *)data;
        struct mwifiex_adapter *adapter = priv->adapter;
        struct cmd_ctrl_node *cmd_node, *tmp_node;
+       spinlock_t *scan_q_lock = &adapter->scan_pending_q_lock;
        unsigned long flags;
 
        if (adapter->surprise_removed)
@@ -44,13 +45,13 @@ static void scan_delay_timer_fn(unsigned long data)
                 * Abort scan operation by cancelling all pending scan
                 * commands
                 */
-               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+               spin_lock_irqsave(scan_q_lock, flags);
                list_for_each_entry_safe(cmd_node, tmp_node,
                                         &adapter->scan_pending_q, list) {
                        list_del(&cmd_node->list);
                        mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
                }
-               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+               spin_unlock_irqrestore(scan_q_lock, flags);
 
                spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
                adapter->scan_processing = false;
@@ -79,12 +80,17 @@ static void scan_delay_timer_fn(unsigned long data)
                         */
                        adapter->scan_delay_cnt = 0;
                        adapter->empty_tx_q_cnt = 0;
-                       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+                       spin_lock_irqsave(scan_q_lock, flags);
+
+                       if (list_empty(&adapter->scan_pending_q)) {
+                               spin_unlock_irqrestore(scan_q_lock, flags);
+                               goto done;
+                       }
+
                        cmd_node = list_first_entry(&adapter->scan_pending_q,
                                                    struct cmd_ctrl_node, list);
                        list_del(&cmd_node->list);
-                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
-                                              flags);
+                       spin_unlock_irqrestore(scan_q_lock, flags);
 
                        mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
                                                        true);
@@ -609,7 +615,6 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        struct sk_buff *new_skb;
        struct mwifiex_txinfo *tx_info;
-       struct timeval tv;
 
        dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
                jiffies, priv->bss_type, priv->bss_num);
@@ -657,8 +662,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
         * firmware for aggregate delay calculation for stats and
         * MSDU lifetime expiry.
         */
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
 
        mwifiex_queue_tx_pkt(priv, skb);
 
@@ -882,6 +886,8 @@ mwifiex_add_card(void *card, struct semaphore *sem,
                goto err_kmalloc;
 
        INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
+       if (adapter->if_ops.iface_work)
+               INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work);
 
        /* Register the device. Fill up the private data structure with relevant
           information from the card. */
index 1398afa8406401c9fd3898716b0849a22946cd42..a2733b1e63f9ec0374f38ba5908b18e52345dfd6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: major data structures and prototypes
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -30,6 +30,7 @@
 #include <linux/etherdevice.h>
 #include <net/sock.h>
 #include <net/lib80211.h>
+#include <linux/vmalloc.h>
 #include <linux/firmware.h>
 #include <linux/ctype.h>
 #include <linux/of.h>
@@ -410,6 +411,29 @@ struct mwifiex_roc_cfg {
        struct ieee80211_channel chan;
 };
 
+#define MWIFIEX_FW_DUMP_IDX            0xff
+#define FW_DUMP_MAX_NAME_LEN           8
+#define FW_DUMP_HOST_READY             0xEE
+#define FW_DUMP_DONE                   0xFF
+
+struct memory_type_mapping {
+       u8 mem_name[FW_DUMP_MAX_NAME_LEN];
+       u8 *mem_ptr;
+       u32 mem_size;
+       u8 done_flag;
+};
+
+enum rdwr_status {
+       RDWR_STATUS_SUCCESS = 0,
+       RDWR_STATUS_FAILURE = 1,
+       RDWR_STATUS_DONE = 2
+};
+
+enum mwifiex_iface_work_flags {
+       MWIFIEX_IFACE_WORK_FW_DUMP,
+       MWIFIEX_IFACE_WORK_CARD_RESET,
+};
+
 struct mwifiex_adapter;
 struct mwifiex_private;
 
@@ -674,6 +698,7 @@ struct mwifiex_if_ops {
        void (*card_reset) (struct mwifiex_adapter *);
        void (*fw_dump)(struct mwifiex_adapter *);
        int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
+       void (*iface_work)(struct work_struct *work);
 };
 
 struct mwifiex_adapter {
@@ -809,6 +834,11 @@ struct mwifiex_adapter {
        bool ext_scan;
        u8 fw_api_ver;
        u8 fw_key_api_major_ver, fw_key_api_minor_ver;
+       struct work_struct iface_work;
+       unsigned long iface_work_flags;
+       struct memory_type_mapping *mem_type_mapping_tbl;
+       u8 num_mem_types;
+       u8 curr_mem_idx;
 };
 
 int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@ -890,6 +920,8 @@ int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
 void mwifiex_process_hs_config(struct mwifiex_adapter *adapter);
 void mwifiex_hs_activated_event(struct mwifiex_private *priv,
                                        u8 activated);
+int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
+                         int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg);
 int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
                              struct host_cmd_ds_command *resp);
 int mwifiex_process_rx_packet(struct mwifiex_private *priv,
index 2cc9b6fca490cd4b57854a8002e61f0064c28397..c16dd2cc81987791d760cf9bdff9aeadcda36579 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: PCIE specific handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -37,6 +37,13 @@ static struct mwifiex_if_ops pcie_ops;
 
 static struct semaphore add_remove_card_sem;
 
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+       {"ITCM", NULL, 0, 0xF0},
+       {"DTCM", NULL, 0, 0xF1},
+       {"SQRAM", NULL, 0, 0xF2},
+       {"IRAM", NULL, 0, 0xF3},
+};
+
 static int
 mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
                       size_t size, int flags)
@@ -192,6 +199,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
                card->pcie.reg = data->reg;
                card->pcie.blksz_fw_dl = data->blksz_fw_dl;
                card->pcie.tx_buf_size = data->tx_buf_size;
+               card->pcie.supports_fw_dump = data->supports_fw_dump;
        }
 
        if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops,
@@ -221,6 +229,8 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
        if (!adapter || !adapter->priv_num)
                return;
 
+       cancel_work_sync(&adapter->iface_work);
+
        if (user_rmmod) {
 #ifdef CONFIG_PM_SLEEP
                if (adapter->is_suspended)
@@ -307,6 +317,17 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
        return 0;
 }
 
+/* This function reads u8 data from PCIE card register. */
+static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter,
+                                int reg, u8 *data)
+{
+       struct pcie_service_card *card = adapter->card;
+
+       *data = ioread8(card->pci_mmap1 + reg);
+
+       return 0;
+}
+
 /*
  * This function adds delay loop to ensure FW is awake before proceeding.
  */
@@ -2173,6 +2194,168 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type,
        return 0;
 }
 
+/* This function read/write firmware */
+static enum rdwr_status
+mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag)
+{
+       int ret, tries;
+       u8 ctrl_data;
+       struct pcie_service_card *card = adapter->card;
+       const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+       ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY);
+       if (ret) {
+               dev_err(adapter->dev, "PCIE write err\n");
+               return RDWR_STATUS_FAILURE;
+       }
+
+       for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+               mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data);
+               if (ctrl_data == FW_DUMP_DONE)
+                       return RDWR_STATUS_SUCCESS;
+               if (doneflag && ctrl_data == doneflag)
+                       return RDWR_STATUS_DONE;
+               if (ctrl_data != FW_DUMP_HOST_READY) {
+                       dev_info(adapter->dev,
+                                "The ctrl reg was changed, re-try again!\n");
+                       mwifiex_write_reg(adapter, reg->fw_dump_ctrl,
+                                         FW_DUMP_HOST_READY);
+                       if (ret) {
+                               dev_err(adapter->dev, "PCIE write err\n");
+                               return RDWR_STATUS_FAILURE;
+                       }
+               }
+               usleep_range(100, 200);
+       }
+
+       dev_err(adapter->dev, "Fail to pull ctrl_data\n");
+       return RDWR_STATUS_FAILURE;
+}
+
+/* This function dump firmware memory to file */
+static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
+{
+       struct pcie_service_card *card = adapter->card;
+       const struct mwifiex_pcie_card_reg *creg = card->pcie.reg;
+       unsigned int reg, reg_start, reg_end;
+       u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
+       enum rdwr_status stat;
+       u32 memory_size;
+       static char *env[] = { "DRIVER=mwifiex_pcie", "EVENT=fw_dump", NULL };
+
+       if (!card->pcie.supports_fw_dump)
+               return;
+
+       for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+               struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+               if (entry->mem_ptr) {
+                       vfree(entry->mem_ptr);
+                       entry->mem_ptr = NULL;
+               }
+               entry->mem_size = 0;
+       }
+
+       dev_info(adapter->dev, "== mwifiex firmware dump start ==\n");
+
+       /* Read the number of the memories which will dump */
+       stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+       if (stat == RDWR_STATUS_FAILURE)
+               goto done;
+
+       reg = creg->fw_dump_start;
+       mwifiex_read_reg_byte(adapter, reg, &dump_num);
+
+       /* Read the length of every memory which will dump */
+       for (idx = 0; idx < dump_num; idx++) {
+               struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+               stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+               if (stat == RDWR_STATUS_FAILURE)
+                       goto done;
+
+               memory_size = 0;
+               reg = creg->fw_dump_start;
+               for (i = 0; i < 4; i++) {
+                       mwifiex_read_reg_byte(adapter, reg, &read_reg);
+                       memory_size |= (read_reg << (i * 8));
+                       reg++;
+               }
+
+               if (memory_size == 0) {
+                       dev_info(adapter->dev, "Firmware dump Finished!\n");
+                       break;
+               }
+
+               dev_info(adapter->dev,
+                        "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
+               entry->mem_ptr = vmalloc(memory_size + 1);
+               entry->mem_size = memory_size;
+               if (!entry->mem_ptr) {
+                       dev_err(adapter->dev,
+                               "Vmalloc %s failed\n", entry->mem_name);
+                       goto done;
+               }
+               dbg_ptr = entry->mem_ptr;
+               end_ptr = dbg_ptr + memory_size;
+
+               doneflag = entry->done_flag;
+               dev_info(adapter->dev, "Start %s output, please wait...\n",
+                        entry->mem_name);
+
+               do {
+                       stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+                       if (RDWR_STATUS_FAILURE == stat)
+                               goto done;
+
+                       reg_start = creg->fw_dump_start;
+                       reg_end = creg->fw_dump_end;
+                       for (reg = reg_start; reg <= reg_end; reg++) {
+                               mwifiex_read_reg_byte(adapter, reg, dbg_ptr);
+                               if (dbg_ptr < end_ptr)
+                                       dbg_ptr++;
+                               else
+                                       dev_err(adapter->dev,
+                                               "Allocated buf not enough\n");
+                       }
+
+                       if (stat != RDWR_STATUS_DONE)
+                               continue;
+
+                       dev_info(adapter->dev, "%s done: size=0x%tx\n",
+                                entry->mem_name, dbg_ptr - entry->mem_ptr);
+                       break;
+               } while (true);
+       }
+       dev_info(adapter->dev, "== mwifiex firmware dump end ==\n");
+
+       kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
+
+done:
+       adapter->curr_mem_idx = 0;
+}
+
+static void mwifiex_pcie_work(struct work_struct *work)
+{
+       struct mwifiex_adapter *adapter =
+                       container_of(work, struct mwifiex_adapter, iface_work);
+
+       if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
+                              &adapter->iface_work_flags))
+               mwifiex_pcie_fw_dump_work(adapter);
+}
+
+/* This function dumps FW information */
+static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter)
+{
+       if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
+               return;
+
+       set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
+
+       schedule_work(&adapter->iface_work);
+}
+
 /*
  * This function initializes the PCI-E host memory space, WCB rings, etc.
  *
@@ -2342,6 +2525,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
 
        adapter->dev = &pdev->dev;
        adapter->tx_buf_size = card->pcie.tx_buf_size;
+       adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
+       adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
        strcpy(adapter->fw_name, card->pcie.firmware);
 
        return 0;
@@ -2394,6 +2579,8 @@ static struct mwifiex_if_ops pcie_ops = {
        .cleanup_mpa_buf =              NULL,
        .init_fw_port =                 mwifiex_pcie_init_fw_port,
        .clean_pcie_ring =              mwifiex_clean_pcie_ring_buf,
+       .fw_dump =                      mwifiex_pcie_fw_dump,
+       .iface_work =                   mwifiex_pcie_work,
 };
 
 /*
index e8ec561f8a642495e410793539a0f2b808435cdf..a1a8fd3bc1be5355289b34457cb4ff1cafe894ba 100644 (file)
@@ -3,7 +3,7 @@
  * @brief This file contains definitions for PCI-E interface.
  * driver.
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -129,6 +129,9 @@ struct mwifiex_pcie_card_reg {
        u32 ring_tx_start_ptr;
        u8 pfu_enabled;
        u8 sleep_cookie;
+       u16 fw_dump_ctrl;
+       u16 fw_dump_start;
+       u16 fw_dump_end;
 };
 
 static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = {
@@ -191,6 +194,9 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = {
        .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR,
        .pfu_enabled = 1,
        .sleep_cookie = 0,
+       .fw_dump_ctrl = 0xcf4,
+       .fw_dump_start = 0xcf8,
+       .fw_dump_end = 0xcff
 };
 
 struct mwifiex_pcie_device {
@@ -198,6 +204,7 @@ struct mwifiex_pcie_device {
        const struct mwifiex_pcie_card_reg *reg;
        u16 blksz_fw_dl;
        u16 tx_buf_size;
+       bool supports_fw_dump;
 };
 
 static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
@@ -205,6 +212,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
        .reg            = &mwifiex_reg_8766,
        .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+       .supports_fw_dump = false,
 };
 
 static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
@@ -212,6 +220,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
        .reg            = &mwifiex_reg_8897,
        .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+       .supports_fw_dump = true,
 };
 
 struct mwifiex_evt_buf_desc {
@@ -322,4 +331,5 @@ mwifiex_pcie_txbd_not_full(struct pcie_service_card *card)
 
        return 0;
 }
+
 #endif /* _MWIFIEX_PCIE_H */
index 45c5b3450cf5c719886483c9e0853c327fd62811..dee717a19ddb560956175c43789b2c802f029554 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: scan ioctl and command handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 4ce3d7b33991ace2cdd3ba0bcfe50c728e59220d..1770fa3fc1e621fc2965cde14581c337c1691d98 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: SDIO specific handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -50,6 +50,24 @@ static struct mwifiex_if_ops sdio_ops;
 
 static struct semaphore add_remove_card_sem;
 
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+       {"ITCM", NULL, 0, 0xF0},
+       {"DTCM", NULL, 0, 0xF1},
+       {"SQRAM", NULL, 0, 0xF2},
+       {"APU", NULL, 0, 0xF3},
+       {"CIU", NULL, 0, 0xF4},
+       {"ICU", NULL, 0, 0xF5},
+       {"MAC", NULL, 0, 0xF6},
+       {"EXT7", NULL, 0, 0xF7},
+       {"EXT8", NULL, 0, 0xF8},
+       {"EXT9", NULL, 0, 0xF9},
+       {"EXT10", NULL, 0, 0xFA},
+       {"EXT11", NULL, 0, 0xFB},
+       {"EXT12", NULL, 0, 0xFC},
+       {"EXT13", NULL, 0, 0xFD},
+       {"EXTLAST", NULL, 0, 0xFE},
+};
+
 /*
  * SDIO probe.
  *
@@ -87,6 +105,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
                card->tx_buf_size = data->tx_buf_size;
                card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
                card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
+               card->supports_fw_dump = data->supports_fw_dump;
        }
 
        sdio_claim_host(func);
@@ -179,6 +198,8 @@ mwifiex_sdio_remove(struct sdio_func *func)
        if (!adapter || !adapter->priv_num)
                return;
 
+       cancel_work_sync(&adapter->iface_work);
+
        if (user_rmmod) {
                if (adapter->is_suspended)
                        mwifiex_sdio_resume(adapter->dev);
@@ -1777,6 +1798,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
        adapter->dev = &func->dev;
 
        strcpy(adapter->fw_name, card->firmware);
+       adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
+       adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
 
        return 0;
 }
@@ -1914,10 +1937,10 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
                port, card->mp_data_port_mask);
 }
 
-static struct mmc_host *reset_host;
-static void sdio_card_reset_worker(struct work_struct *work)
+static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
 {
-       struct mmc_host *target = reset_host;
+       struct sdio_mmc_card *card = adapter->card;
+       struct mmc_host *target = card->func->card->host;
 
        /* The actual reset operation must be run outside of driver thread.
         * This is because mmc_remove_host() will cause the device to be
@@ -1931,17 +1954,210 @@ static void sdio_card_reset_worker(struct work_struct *work)
        mmc_remove_host(target);
        /* 20ms delay is based on experiment with sdhci controller */
        mdelay(20);
+       target->rescan_entered = 0; /* rescan non-removable cards */
        mmc_add_host(target);
 }
-static DECLARE_WORK(card_reset_work, sdio_card_reset_worker);
+
+/* This function read/write firmware */
+static enum
+rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter,
+                                      u8 doneflag)
+{
+       struct sdio_mmc_card *card = adapter->card;
+       int ret, tries;
+       u8 ctrl_data = 0;
+
+       sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl,
+                   &ret);
+       if (ret) {
+               dev_err(adapter->dev, "SDIO Write ERR\n");
+               return RDWR_STATUS_FAILURE;
+       }
+       for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+               ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
+                                      &ret);
+               if (ret) {
+                       dev_err(adapter->dev, "SDIO read err\n");
+                       return RDWR_STATUS_FAILURE;
+               }
+               if (ctrl_data == FW_DUMP_DONE)
+                       break;
+               if (doneflag && ctrl_data == doneflag)
+                       return RDWR_STATUS_DONE;
+               if (ctrl_data != FW_DUMP_HOST_READY) {
+                       dev_info(adapter->dev,
+                                "The ctrl reg was changed, re-try again!\n");
+                       sdio_writeb(card->func, FW_DUMP_HOST_READY,
+                                   card->reg->fw_dump_ctrl, &ret);
+                       if (ret) {
+                               dev_err(adapter->dev, "SDIO write err\n");
+                               return RDWR_STATUS_FAILURE;
+                       }
+               }
+               usleep_range(100, 200);
+       }
+       if (ctrl_data == FW_DUMP_HOST_READY) {
+               dev_err(adapter->dev, "Fail to pull ctrl_data\n");
+               return RDWR_STATUS_FAILURE;
+       }
+
+       return RDWR_STATUS_SUCCESS;
+}
+
+/* This function dump firmware memory to file */
+static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
+{
+       struct mwifiex_adapter *adapter =
+                       container_of(work, struct mwifiex_adapter, iface_work);
+       struct sdio_mmc_card *card = adapter->card;
+       int ret = 0;
+       unsigned int reg, reg_start, reg_end;
+       u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
+       enum rdwr_status stat;
+       u32 memory_size;
+       static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL };
+
+       if (!card->supports_fw_dump)
+               return;
+
+       for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+               struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+               if (entry->mem_ptr) {
+                       vfree(entry->mem_ptr);
+                       entry->mem_ptr = NULL;
+               }
+               entry->mem_size = 0;
+       }
+
+       mwifiex_pm_wakeup_card(adapter);
+       sdio_claim_host(card->func);
+
+       dev_info(adapter->dev, "== mwifiex firmware dump start ==\n");
+
+       stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+       if (stat == RDWR_STATUS_FAILURE)
+               goto done;
+
+       reg = card->reg->fw_dump_start;
+       /* Read the number of the memories which will dump */
+       dump_num = sdio_readb(card->func, reg, &ret);
+       if (ret) {
+               dev_err(adapter->dev, "SDIO read memory length err\n");
+               goto done;
+       }
+
+       /* Read the length of every memory which will dump */
+       for (idx = 0; idx < dump_num; idx++) {
+               struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+               stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+               if (stat == RDWR_STATUS_FAILURE)
+                       goto done;
+
+               memory_size = 0;
+               reg = card->reg->fw_dump_start;
+               for (i = 0; i < 4; i++) {
+                       read_reg = sdio_readb(card->func, reg, &ret);
+                       if (ret) {
+                               dev_err(adapter->dev, "SDIO read err\n");
+                               goto done;
+                       }
+                       memory_size |= (read_reg << i*8);
+                       reg++;
+               }
+
+               if (memory_size == 0) {
+                       dev_info(adapter->dev, "Firmware dump Finished!\n");
+                       break;
+               }
+
+               dev_info(adapter->dev,
+                        "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
+               entry->mem_ptr = vmalloc(memory_size + 1);
+               entry->mem_size = memory_size;
+               if (!entry->mem_ptr) {
+                       dev_err(adapter->dev, "Vmalloc %s failed\n",
+                               entry->mem_name);
+                       goto done;
+               }
+               dbg_ptr = entry->mem_ptr;
+               end_ptr = dbg_ptr + memory_size;
+
+               doneflag = entry->done_flag;
+               dev_info(adapter->dev, "Start %s output, please wait...\n",
+                        entry->mem_name);
+
+               do {
+                       stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+                       if (stat == RDWR_STATUS_FAILURE)
+                               goto done;
+
+                       reg_start = card->reg->fw_dump_start;
+                       reg_end = card->reg->fw_dump_end;
+                       for (reg = reg_start; reg <= reg_end; reg++) {
+                               *dbg_ptr = sdio_readb(card->func, reg, &ret);
+                               if (ret) {
+                                       dev_err(adapter->dev,
+                                               "SDIO read err\n");
+                                       goto done;
+                               }
+                               if (dbg_ptr < end_ptr)
+                                       dbg_ptr++;
+                               else
+                                       dev_err(adapter->dev,
+                                               "Allocated buf not enough\n");
+                       }
+
+                       if (stat != RDWR_STATUS_DONE)
+                               continue;
+
+                       dev_info(adapter->dev, "%s done: size=0x%tx\n",
+                                entry->mem_name, dbg_ptr - entry->mem_ptr);
+                       break;
+               } while (1);
+       }
+       dev_info(adapter->dev, "== mwifiex firmware dump end ==\n");
+
+       kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
+
+done:
+       sdio_release_host(card->func);
+       adapter->curr_mem_idx = 0;
+}
+
+static void mwifiex_sdio_work(struct work_struct *work)
+{
+       struct mwifiex_adapter *adapter =
+                       container_of(work, struct mwifiex_adapter, iface_work);
+
+       if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET,
+                              &adapter->iface_work_flags))
+               mwifiex_sdio_card_reset_work(adapter);
+       if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
+                              &adapter->iface_work_flags))
+               mwifiex_sdio_fw_dump_work(work);
+}
 
 /* This function resets the card */
 static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter)
 {
-       struct sdio_mmc_card *card = adapter->card;
+       if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags))
+               return;
+
+       set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags);
+
+       schedule_work(&adapter->iface_work);
+}
+
+/* This function dumps FW information */
+static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter)
+{
+       if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
+               return;
 
-       reset_host = card->func->card->host;
-       schedule_work(&card_reset_work);
+       set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
+       schedule_work(&adapter->iface_work);
 }
 
 static struct mwifiex_if_ops sdio_ops = {
@@ -1964,6 +2180,8 @@ static struct mwifiex_if_ops sdio_ops = {
        .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
        .event_complete = mwifiex_sdio_event_complete,
        .card_reset = mwifiex_sdio_card_reset,
+       .iface_work = mwifiex_sdio_work,
+       .fw_dump = mwifiex_sdio_fw_dump,
 };
 
 /*
@@ -2001,7 +2219,6 @@ mwifiex_sdio_cleanup_module(void)
        /* Set the flag as user is removing this module. */
        user_rmmod = 1;
 
-       cancel_work_sync(&card_reset_work);
        sdio_unregister_driver(&mwifiex_sdio);
 }
 
index 6eea30b43ed714f3bd81f9453a5008001a11b33d..6b8835ec88f1a802dc9cbabf4efdb38f23b7973c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: SDIO specific definitions
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -219,6 +219,9 @@ struct mwifiex_sdio_card_reg {
        u8 rd_len_p0_l;
        u8 rd_len_p0_u;
        u8 card_misc_cfg_reg;
+       u8 fw_dump_ctrl;
+       u8 fw_dump_start;
+       u8 fw_dump_end;
 };
 
 struct sdio_mmc_card {
@@ -231,6 +234,7 @@ struct sdio_mmc_card {
        u8 mp_agg_pkt_limit;
        bool supports_sdio_new_mode;
        bool has_control_mask;
+       bool supports_fw_dump;
        u16 tx_buf_size;
        u32 mp_tx_agg_buf_size;
        u32 mp_rx_agg_buf_size;
@@ -257,6 +261,7 @@ struct mwifiex_sdio_device {
        u8 mp_agg_pkt_limit;
        bool supports_sdio_new_mode;
        bool has_control_mask;
+       bool supports_fw_dump;
        u16 tx_buf_size;
        u32 mp_tx_agg_buf_size;
        u32 mp_rx_agg_buf_size;
@@ -307,6 +312,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
        .rd_len_p0_l = 0x0c,
        .rd_len_p0_u = 0x0d,
        .card_misc_cfg_reg = 0xcc,
+       .fw_dump_ctrl = 0xe2,
+       .fw_dump_start = 0xe3,
+       .fw_dump_end = 0xea,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
@@ -319,6 +327,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
        .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
        .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .supports_fw_dump = false,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
@@ -331,6 +340,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
        .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
        .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .supports_fw_dump = false,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
@@ -343,6 +353,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
        .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
        .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+       .supports_fw_dump = false,
 };
 
 static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
@@ -355,6 +366,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
        .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
        .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
        .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+       .supports_fw_dump = true,
 };
 
 /*
index 88202ce0c13965fdff679c506a3772ef287e6d54..733de92a4c611e39da93fb580a272203d269e54f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: station command handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -1647,7 +1647,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
                timeout = (void *)(pos + config_len);
                timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT);
                timeout->header.len = cpu_to_le16(sizeof(timeout->value));
-               timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT);
+               timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC);
                config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout);
 
                break;
index 577f2979ed8f2bcacbacc6af6ce9af03d3f86137..08b78baeb846d65ef18680e462991c226c94d5e3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: station command response handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -908,7 +908,7 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv,
                break;
        default:
                dev_err(priv->adapter->dev,
-                       "Unknown TDLS command action respnse %d", action);
+                       "Unknown TDLS command action response %d", action);
                return -1;
        }
 
index f6395ef11a721b8fc6d8ee797fb34a72b7c2f43d..f1c240eca0cdae2e6364f67e0aab150497afed2b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: station event handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 536c14aa71f39cb0e4f73417429fca55258af5ed..caae9738100aa732087e2d03477b063bc1534fe2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: functions for station ioctl
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -26,7 +26,7 @@
 #include "11n.h"
 #include "cfg80211.h"
 
-static int disconnect_on_suspend = 1;
+static int disconnect_on_suspend;
 module_param(disconnect_on_suspend, int, 0644);
 
 /*
@@ -283,10 +283,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
            priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
                u8 config_bands;
 
-               ret = mwifiex_deauthenticate(priv, NULL);
-               if (ret)
-                       goto done;
-
                if (!bss_desc)
                        return -1;
 
@@ -345,12 +341,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
                        goto done;
                }
 
-               /* Exit Adhoc mode first */
-               dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n");
-               ret = mwifiex_deauthenticate(priv, NULL);
-               if (ret)
-                       goto done;
-
                priv->adhoc_is_link_sensed = false;
 
                ret = mwifiex_check_network_compatibility(priv, bss_desc);
@@ -389,8 +379,8 @@ done:
  * This function prepares the correct firmware command and
  * issues it.
  */
-static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
-                                int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
+int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
+                         int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
 
 {
        struct mwifiex_adapter *adapter = priv->adapter;
index 8b639d7fe6df263814901554a2c43363be6791e9..9ceb1dbe34c532fdae40952a167675d76bdea9aa 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: station RX data handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 70eb863c724974f94f16f4ea09b1b11b568f7803..dab7b33c54bed0d2849ba2b275b103bf0925ba1e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: station TX data handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 0e88364e0c670a5fe59fccdec3d711d679bf7be3..4c5fd953893dcb22712f60c618f68499351c1dff 100644 (file)
@@ -530,7 +530,6 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
 {
        struct sk_buff *skb;
        struct mwifiex_txinfo *tx_info;
-       struct timeval tv;
        int ret;
        u16 skb_len;
 
@@ -609,8 +608,7 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
        tx_info->bss_num = priv->bss_num;
        tx_info->bss_type = priv->bss_type;
 
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
        mwifiex_queue_tx_pkt(priv, skb);
 
        return 0;
@@ -703,7 +701,6 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
 {
        struct sk_buff *skb;
        struct mwifiex_txinfo *tx_info;
-       struct timeval tv;
        u8 *pos;
        u32 pkt_type, tx_control;
        u16 pkt_len, skb_len;
@@ -769,8 +766,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
        pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len);
        memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len,
               sizeof(pkt_len));
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
        mwifiex_queue_tx_pkt(priv, skb);
 
        return 0;
@@ -785,6 +781,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
        struct mwifiex_sta_node *sta_ptr;
        u8 *peer, *pos, *end;
        u8 i, action, basic;
+       __le16 cap = 0;
        int ie_len = 0;
 
        if (len < (sizeof(struct ethhdr) + 3))
@@ -796,18 +793,9 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
 
        peer = buf + ETH_ALEN;
        action = *(buf + sizeof(struct ethhdr) + 2);
-
-       /* just handle TDLS setup request/response/confirm */
-       if (action > WLAN_TDLS_SETUP_CONFIRM)
-               return;
-
        dev_dbg(priv->adapter->dev,
                "rx:tdls action: peer=%pM, action=%d\n", peer, action);
 
-       sta_ptr = mwifiex_add_sta_entry(priv, peer);
-       if (!sta_ptr)
-               return;
-
        switch (action) {
        case WLAN_TDLS_SETUP_REQUEST:
                if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN))
@@ -815,7 +803,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
 
                pos = buf + sizeof(struct ethhdr) + 4;
                /* payload 1+ category 1 + action 1 + dialog 1 */
-               sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos);
+               cap = cpu_to_le16(*(u16 *)pos);
                ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN;
                pos += 2;
                break;
@@ -825,7 +813,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
                        return;
                /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/
                pos = buf + sizeof(struct ethhdr) + 6;
-               sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos);
+               cap = cpu_to_le16(*(u16 *)pos);
                ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN;
                pos += 2;
                break;
@@ -837,10 +825,16 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
                ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN;
                break;
        default:
-               dev_warn(priv->adapter->dev, "Unknown TDLS frame type.\n");
+               dev_dbg(priv->adapter->dev, "Unknown TDLS frame type.\n");
                return;
        }
 
+       sta_ptr = mwifiex_add_sta_entry(priv, peer);
+       if (!sta_ptr)
+               return;
+
+       sta_ptr->tdls_cap.capab = cap;
+
        for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) {
                if (pos + 2 + pos[1] > end)
                        break;
index fd7e5b9b4581fa5d44ea60a3476e45aa3054e045..96a2126cc44bf81da15f8c17d6dc7895cad5abce 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: generic TX/RX data handling
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 32643555dd2a32a302d1301427e463d877c8260a..300bab4380117076dadaf51b4eaa516b7f799044 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: AP specific command handling
  *
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 92e77a398ecfcaa0f7bf3493864f4823eb30394d..7c2b97660a032ccdb424b5783e38c0680f5599c3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: AP event handling
  *
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index b0601b91cc4f1310b76f519e5ae3651ebb8fe1c5..ec7309d096abaf5a11845eea72659374e318fce1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: AP TX and RX data handling
  *
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -96,7 +96,6 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
        struct sk_buff *new_skb;
        struct mwifiex_txinfo *tx_info;
        int hdr_chop;
-       struct timeval tv;
        struct ethhdr *p_ethhdr;
 
        uap_rx_pd = (struct uap_rxpd *)(skb->data);
@@ -193,8 +192,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
                tx_info->pkt_len = skb->len;
        }
 
-       do_gettimeofday(&tv);
-       skb->tstamp = timeval_to_ktime(tv);
+       __net_timestamp(skb);
        mwifiex_wmm_add_buf_txqueue(priv, skb);
        atomic_inc(&adapter->tx_pending);
        atomic_inc(&adapter->pending_bridged_pkts);
index a8ce8130cfaeeda08a2a08f7b693540fe79d9f85..7118a18b91ba9f2a98a4333a2a5070f00d17a433 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: USB specific handling
  *
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 15b73d12e9983dc98019dd739e80f38ffa66aedd..4c41c2a193c553106f61010574a0e656c49cfc2f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file contains definitions for mwifiex USB interface driver.
  *
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 6da5abf52e61a4360b236411b09310205e8316ea..cee028321a9ab73eac65ced1048e21fed2ce8e7e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: utility functions
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index caadb3737b9ebb6a877d707d9be44533c8643063..40296cb4a3f126c78a57369c03841942e3d4bb9c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: utility functions
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index d3671d009f6c3c89c4f6e91e06220edd2c481da9..94c98a86ebbec84bc83fe8c6c9f3e879c09df627 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: WMM
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -878,15 +878,8 @@ u8
 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv,
                                  const struct sk_buff *skb)
 {
+       u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp));
        u8 ret_val;
-       struct timeval out_tstamp, in_tstamp;
-       u32 queue_delay;
-
-       do_gettimeofday(&out_tstamp);
-       in_tstamp = ktime_to_timeval(skb->tstamp);
-
-       queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000;
-       queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000;
 
        /*
         * Queue delay is passed as a uint8 in units of 2ms (ms shifted
index eca56e371a57bb5afb2df6165c24fbbc4d34e561..569bd73f33c5f001f93241fe1c81b40e172230e0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Marvell Wireless LAN device driver: WMM
  *
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
  *
  * This software file (the "File") is distributed by Marvell International
  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
index 60819bcf437735fd43a7f03d14ba988623e8e9e6..60698b02085190a1147f8bb96e1a21060be2295b 100644 (file)
@@ -107,7 +107,7 @@ config PCI_HERMES
 
 config PCMCIA_HERMES
        tristate "Hermes PCMCIA card support"
-       depends on PCMCIA && HERMES
+       depends on PCMCIA && HERMES && HAS_IOPORT_MAP
        ---help---
          A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
          as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -122,7 +122,7 @@ config PCMCIA_HERMES
 
 config PCMCIA_SPECTRUM
        tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
-       depends on PCMCIA && HERMES
+       depends on PCMCIA && HERMES && HAS_IOPORT_MAP
        ---help---
 
          This is a driver for 802.11b cards using RAM-loadable Symbol
index de15171e2cd896f8dfd78c6d5e00966e2161ec12..63de5eed25cf9309f6f86a869481632b59ec892d 100644 (file)
@@ -193,7 +193,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev)
        /* allow users to customize their eeprom.
         */
 
-       ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev);
+       ret = request_firmware_direct(&eeprom, "3826.eeprom", &priv->spi->dev);
        if (ret < 0) {
 #ifdef CONFIG_P54_SPI_DEFAULT_EEPROM
                dev_info(&priv->spi->dev, "loading default eeprom...\n");
index cf61d6e3eaa7cd746a7bec20853277b5f1c69001..f3d3995d8f6b4c8ada23a5f25c6ad8035b108970 100644 (file)
@@ -76,6 +76,52 @@ static bool rsi_recalculate_weights(struct rsi_common *common)
        return recontend_queue;
 }
 
+/**
+ * rsi_get_num_pkts_dequeue() - This function determines the number of
+ *                             packets to be dequeued based on the number
+ *                             of bytes calculated using txop.
+ *
+ * @common: Pointer to the driver private structure.
+ * @q_num: the queue from which pkts have to be dequeued
+ *
+ * Return: pkt_num: Number of pkts to be dequeued.
+ */
+static u32 rsi_get_num_pkts_dequeue(struct rsi_common *common, u8 q_num)
+{
+       struct rsi_hw *adapter = common->priv;
+       struct sk_buff *skb;
+       u32 pkt_cnt = 0;
+       s16 txop = common->tx_qinfo[q_num].txop * 32;
+       __le16 r_txop;
+       struct ieee80211_rate rate;
+
+       rate.bitrate = RSI_RATE_MCS0 * 5 * 10; /* Convert to Kbps */
+       if (q_num == VI_Q)
+               txop = ((txop << 5) / 80);
+
+       if (skb_queue_len(&common->tx_queue[q_num]))
+               skb = skb_peek(&common->tx_queue[q_num]);
+       else
+               return 0;
+
+       do {
+               r_txop = ieee80211_generic_frame_duration(adapter->hw,
+                                                         adapter->vifs[0],
+                                                         common->band,
+                                                         skb->len, &rate);
+               txop -= le16_to_cpu(r_txop);
+               pkt_cnt += 1;
+               /*checking if pkts are still there*/
+               if (skb_queue_len(&common->tx_queue[q_num]) - pkt_cnt)
+                       skb = skb->next;
+               else
+                       break;
+
+       } while (txop > 0);
+
+       return pkt_cnt;
+}
+
 /**
  * rsi_core_determine_hal_queue() - This function determines the queue from
  *                                 which packet has to be dequeued.
@@ -88,7 +134,7 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common)
        bool recontend_queue = false;
        u32 q_len = 0;
        u8 q_num = INVALID_QUEUE;
-       u8 ii = 0, min = 0;
+       u8 ii = 0;
 
        if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) {
                if (!common->mgmt_q_block)
@@ -96,6 +142,9 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common)
                return q_num;
        }
 
+       if (common->hw_data_qs_blocked)
+               return q_num;
+
        if (common->pkt_cnt != 0) {
                --common->pkt_cnt;
                return common->selected_qnum;
@@ -106,14 +155,15 @@ get_queue_num:
 
        q_num = rsi_determine_min_weight_queue(common);
 
-       q_len = skb_queue_len(&common->tx_queue[ii]);
        ii = q_num;
 
        /* Selecting the queue with least back off */
        for (; ii < NUM_EDCA_QUEUES; ii++) {
+               q_len = skb_queue_len(&common->tx_queue[ii]);
                if (((common->tx_qinfo[ii].pkt_contended) &&
-                    (common->tx_qinfo[ii].weight < min)) && q_len) {
-                       min = common->tx_qinfo[ii].weight;
+                    (common->tx_qinfo[ii].weight < common->min_weight)) &&
+                     q_len) {
+                       common->min_weight = common->tx_qinfo[ii].weight;
                        q_num = ii;
                }
        }
@@ -140,25 +190,9 @@ get_queue_num:
        common->selected_qnum = q_num;
        q_len = skb_queue_len(&common->tx_queue[q_num]);
 
-       switch (common->selected_qnum) {
-       case VO_Q:
-               if (q_len > MAX_CONTINUOUS_VO_PKTS)
-                       common->pkt_cnt = (MAX_CONTINUOUS_VO_PKTS - 1);
-               else
-                       common->pkt_cnt = --q_len;
-               break;
-
-       case VI_Q:
-               if (q_len > MAX_CONTINUOUS_VI_PKTS)
-                       common->pkt_cnt = (MAX_CONTINUOUS_VI_PKTS - 1);
-               else
-                       common->pkt_cnt = --q_len;
-
-               break;
-
-       default:
-               common->pkt_cnt = 0;
-               break;
+       if (q_num == VO_Q || q_num == VI_Q) {
+               common->pkt_cnt = rsi_get_num_pkts_dequeue(common, q_num);
+               common->pkt_cnt -= 1;
        }
 
        return q_num;
@@ -252,6 +286,7 @@ void rsi_core_qos_processor(struct rsi_common *common)
 
                skb = rsi_core_dequeue_pkt(common, q_num);
                if (skb == NULL) {
+                       rsi_dbg(ERR_ZONE, "skb null\n");
                        mutex_unlock(&common->tx_rxlock);
                        break;
                }
@@ -306,7 +341,8 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
        }
 
        if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) ||
-           (ieee80211_is_ctl(tmp_hdr->frame_control))) {
+           (ieee80211_is_ctl(tmp_hdr->frame_control)) ||
+           (ieee80211_is_qos_nullfunc(tmp_hdr->frame_control))) {
                q_num = MGMT_SOFT_Q;
                skb->priority = q_num;
        } else {
@@ -325,6 +361,7 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
        if ((q_num != MGMT_SOFT_Q) &&
            ((skb_queue_len(&common->tx_queue[q_num]) + 1) >=
             DATA_QUEUE_WATER_MARK)) {
+               rsi_dbg(ERR_ZONE, "%s: sw queue full\n", __func__);
                if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num)))
                        ieee80211_stop_queue(adapter->hw, WME_AC(q_num));
                rsi_set_event(&common->tx_thread.event);
index c466246a323f3118d158b22df5c389bdb46e759b..828a042f903f19fef82eb09425f3e865e534f551 100644 (file)
@@ -145,7 +145,7 @@ static int rsi_stats_read(struct seq_file *seq, void *data)
        seq_printf(seq, "total_mgmt_pkt_send : %d\n",
                   common->tx_stats.total_tx_pkt_send[MGMT_SOFT_Q]);
        seq_printf(seq, "total_mgmt_pkt_queued : %d\n",
-                  skb_queue_len(&common->tx_queue[4]));
+                  skb_queue_len(&common->tx_queue[MGMT_SOFT_Q]));
        seq_printf(seq, "total_mgmt_pkt_freed  : %d\n",
                   common->tx_stats.total_tx_pkt_freed[MGMT_SOFT_Q]);
 
@@ -153,25 +153,25 @@ static int rsi_stats_read(struct seq_file *seq, void *data)
        seq_printf(seq, "total_data_vo_pkt_send: %8d\t",
                   common->tx_stats.total_tx_pkt_send[VO_Q]);
        seq_printf(seq, "total_data_vo_pkt_queued:  %8d\t",
-                  skb_queue_len(&common->tx_queue[0]));
+                  skb_queue_len(&common->tx_queue[VO_Q]));
        seq_printf(seq, "total_vo_pkt_freed: %8d\n",
                   common->tx_stats.total_tx_pkt_freed[VO_Q]);
        seq_printf(seq, "total_data_vi_pkt_send: %8d\t",
                   common->tx_stats.total_tx_pkt_send[VI_Q]);
        seq_printf(seq, "total_data_vi_pkt_queued:  %8d\t",
-                  skb_queue_len(&common->tx_queue[1]));
+                  skb_queue_len(&common->tx_queue[VI_Q]));
        seq_printf(seq, "total_vi_pkt_freed: %8d\n",
                   common->tx_stats.total_tx_pkt_freed[VI_Q]);
        seq_printf(seq,  "total_data_be_pkt_send: %8d\t",
                   common->tx_stats.total_tx_pkt_send[BE_Q]);
        seq_printf(seq, "total_data_be_pkt_queued:  %8d\t",
-                  skb_queue_len(&common->tx_queue[2]));
+                  skb_queue_len(&common->tx_queue[BE_Q]));
        seq_printf(seq, "total_be_pkt_freed: %8d\n",
                   common->tx_stats.total_tx_pkt_freed[BE_Q]);
        seq_printf(seq, "total_data_bk_pkt_send: %8d\t",
                   common->tx_stats.total_tx_pkt_send[BK_Q]);
        seq_printf(seq, "total_data_bk_pkt_queued:  %8d\t",
-                  skb_queue_len(&common->tx_queue[3]));
+                  skb_queue_len(&common->tx_queue[BK_Q]));
        seq_printf(seq, "total_bk_pkt_freed: %8d\n",
                   common->tx_stats.total_tx_pkt_freed[BK_Q]);
 
index 54aaeb09debf568c9dcad47ae3892153c2d827d6..aeaf87bb551841a607b9884aeed992513e0001a0 100644 (file)
@@ -177,7 +177,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
        sbands->ht_cap.cap = (IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
                              IEEE80211_HT_CAP_SGI_20 |
                              IEEE80211_HT_CAP_SGI_40);
-       sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
+       sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
        sbands->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
        sbands->ht_cap.mcs.rx_mask[0] = 0xff;
        sbands->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
@@ -185,7 +185,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
 }
 
 /**
- * rsi_mac80211_attach() - This function is used to de-initialize the
+ * rsi_mac80211_detach() - This function is used to de-initialize the
  *                        Mac80211 stack.
  * @adapter: Pointer to the adapter structure.
  *
@@ -340,6 +340,59 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw,
        mutex_unlock(&common->mutex);
 }
 
+/**
+ * rsi_channel_change() - This function is a performs the checks
+ *                       required for changing a channel and sets
+ *                       the channel accordingly.
+ * @hw: Pointer to the ieee80211_hw structure.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rsi_channel_change(struct ieee80211_hw *hw)
+{
+       struct rsi_hw *adapter = hw->priv;
+       struct rsi_common *common = adapter->priv;
+       int status = -EOPNOTSUPP;
+       struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+       u16 channel = curchan->hw_value;
+       struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf;
+
+       rsi_dbg(INFO_ZONE,
+               "%s: Set channel: %d MHz type: %d channel_no %d\n",
+               __func__, curchan->center_freq,
+               curchan->flags, channel);
+
+       if (bss->assoc) {
+               if (!common->hw_data_qs_blocked &&
+                   (rsi_get_connected_channel(adapter) != channel)) {
+                       rsi_dbg(INFO_ZONE, "blk data q %d\n", channel);
+                       if (!rsi_send_block_unblock_frame(common, true))
+                               common->hw_data_qs_blocked = true;
+               }
+       }
+
+       status = rsi_band_check(common);
+       if (!status)
+               status = rsi_set_channel(adapter->priv, channel);
+
+       if (bss->assoc) {
+               if (common->hw_data_qs_blocked &&
+                   (rsi_get_connected_channel(adapter) == channel)) {
+                       rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel);
+                       if (!rsi_send_block_unblock_frame(common, false))
+                               common->hw_data_qs_blocked = false;
+               }
+       } else {
+               if (common->hw_data_qs_blocked) {
+                       rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel);
+                       if (!rsi_send_block_unblock_frame(common, false))
+                               common->hw_data_qs_blocked = false;
+               }
+       }
+
+       return status;
+}
+
 /**
  * rsi_mac80211_config() - This function is a handler for configuration
  *                        requests. The stack calls this function to
@@ -357,17 +410,10 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw,
        int status = -EOPNOTSUPP;
 
        mutex_lock(&common->mutex);
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               struct ieee80211_channel *curchan = hw->conf.chandef.chan;
-               u16 channel = curchan->hw_value;
-
-               rsi_dbg(INFO_ZONE,
-                       "%s: Set channel: %d MHz type: %d channel_no %d\n",
-                       __func__, curchan->center_freq,
-                       curchan->flags, channel);
-               common->band = curchan->band;
-               status = rsi_set_channel(adapter->priv, channel);
-       }
+
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
+               status = rsi_channel_change(hw);
+
        mutex_unlock(&common->mutex);
 
        return status;
@@ -421,6 +467,15 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
                                      bss_conf->qos,
                                      bss_conf->aid);
        }
+
+       if (changed & BSS_CHANGED_CQM) {
+               common->cqm_info.last_cqm_event_rssi = 0;
+               common->cqm_info.rssi_thold = bss_conf->cqm_rssi_thold;
+               common->cqm_info.rssi_hyst = bss_conf->cqm_rssi_hyst;
+               rsi_dbg(INFO_ZONE, "RSSI throld & hysteresis are: %d %d\n",
+                       common->cqm_info.rssi_thold,
+                       common->cqm_info.rssi_hyst);
+       }
        mutex_unlock(&common->mutex);
 }
 
@@ -723,23 +778,54 @@ static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw,
 {
        struct rsi_hw *adapter = hw->priv;
        struct rsi_common *common = adapter->priv;
+       enum ieee80211_band band = hw->conf.chandef.chan->band;
 
        mutex_lock(&common->mutex);
+       common->fixedrate_mask[band] = 0;
 
-       common->fixedrate_mask[IEEE80211_BAND_2GHZ] = 0;
-
-       if (mask->control[IEEE80211_BAND_2GHZ].legacy == 0xfff) {
-               common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
-                       (mask->control[IEEE80211_BAND_2GHZ].ht_mcs[0] << 12);
+       if (mask->control[band].legacy == 0xfff) {
+               common->fixedrate_mask[band] =
+                       (mask->control[band].ht_mcs[0] << 12);
        } else {
-               common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
-                       mask->control[IEEE80211_BAND_2GHZ].legacy;
+               common->fixedrate_mask[band] =
+                       mask->control[band].legacy;
        }
        mutex_unlock(&common->mutex);
 
        return 0;
 }
 
+/**
+ * rsi_perform_cqm() - This function performs cqm.
+ * @common: Pointer to the driver private structure.
+ * @bssid: pointer to the bssid.
+ * @rssi: RSSI value.
+ */
+static void rsi_perform_cqm(struct rsi_common *common,
+                           u8 *bssid,
+                           s8 rssi)
+{
+       struct rsi_hw *adapter = common->priv;
+       s8 last_event = common->cqm_info.last_cqm_event_rssi;
+       int thold = common->cqm_info.rssi_thold;
+       u32 hyst = common->cqm_info.rssi_hyst;
+       enum nl80211_cqm_rssi_threshold_event event;
+
+       if (rssi < thold && (last_event == 0 || rssi < (last_event - hyst)))
+               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+       else if (rssi > thold &&
+                (last_event == 0 || rssi > (last_event + hyst)))
+               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+       else
+               return;
+
+       common->cqm_info.last_cqm_event_rssi = rssi;
+       rsi_dbg(INFO_ZONE, "CQM: Notifying event: %d\n", event);
+       ieee80211_cqm_rssi_notify(adapter->vifs[0], event, GFP_KERNEL);
+
+       return;
+}
+
 /**
  * rsi_fill_rx_status() - This function fills rx status in
  *                       ieee80211_rx_status structure.
@@ -755,6 +841,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
                               struct rsi_common *common,
                               struct ieee80211_rx_status *rxs)
 {
+       struct ieee80211_bss_conf *bss = &common->priv->vifs[0]->bss_conf;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct skb_info *rx_params = (struct skb_info *)info->driver_data;
        struct ieee80211_hdr *hdr;
@@ -770,10 +857,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
 
        rxs->signal = -(rssi);
 
-       if (channel <= 14)
-               rxs->band = IEEE80211_BAND_2GHZ;
-       else
-               rxs->band = IEEE80211_BAND_5GHZ;
+       rxs->band = common->band;
 
        freq = ieee80211_channel_to_frequency(channel, rxs->band);
 
@@ -792,6 +876,14 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
                rxs->flag |= RX_FLAG_DECRYPTED;
                rxs->flag |= RX_FLAG_IV_STRIPPED;
        }
+
+       /* CQM only for connected AP beacons, the RSSI is a weighted avg */
+       if (bss->assoc && !(memcmp(bss->bssid, hdr->addr2, ETH_ALEN))) {
+               if (ieee80211_is_beacon(hdr->frame_control))
+                       rsi_perform_cqm(common, hdr->addr2, rxs->signal);
+       }
+
+       return;
 }
 
 /**
@@ -983,6 +1075,7 @@ int rsi_mac80211_attach(struct rsi_common *common)
 
        hw->max_tx_aggregation_subframes = 6;
        rsi_register_rates_channels(adapter, IEEE80211_BAND_2GHZ);
+       rsi_register_rates_channels(adapter, IEEE80211_BAND_5GHZ);
        hw->rate_control_algorithm = "AARF";
 
        SET_IEEE80211_PERM_ADDR(hw, common->mac_addr);
@@ -1000,6 +1093,8 @@ int rsi_mac80211_attach(struct rsi_common *common)
        wiphy->available_antennas_tx = 1;
        wiphy->bands[IEEE80211_BAND_2GHZ] =
                &adapter->sbands[IEEE80211_BAND_2GHZ];
+       wiphy->bands[IEEE80211_BAND_5GHZ] =
+               &adapter->sbands[IEEE80211_BAND_5GHZ];
 
        status = ieee80211_register_hw(hw);
        if (status)
index 2eefbf159bc0d0abdcead9ff1caf6b867d95793f..8d110fd9eba1c95d4b1537c26dff1853c7407a42 100644 (file)
@@ -217,6 +217,7 @@ static void rsi_set_default_parameters(struct rsi_common *common)
        common->min_rate = 0xffff;
        common->fsm_state = FSM_CARD_NOT_READY;
        common->iface_down = true;
+       common->endpoint = EP_2GHZ_20MHZ;
 }
 
 /**
@@ -276,7 +277,6 @@ static int rsi_load_radio_caps(struct rsi_common *common)
 {
        struct rsi_radio_caps *radio_caps;
        struct rsi_hw *adapter = common->priv;
-       struct ieee80211_hw *hw = adapter->hw;
        u16 inx = 0;
        u8 ii;
        u8 radio_id = 0;
@@ -285,7 +285,6 @@ static int rsi_load_radio_caps(struct rsi_common *common)
                      0xf0, 0xf0, 0xf0, 0xf0,
                      0xf0, 0xf0, 0xf0, 0xf0,
                      0xf0, 0xf0, 0xf0, 0xf0};
-       struct ieee80211_conf *conf = &hw->conf;
        struct sk_buff *skb;
 
        rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__);
@@ -307,29 +306,36 @@ static int rsi_load_radio_caps(struct rsi_common *common)
        if (common->channel_width == BW_40MHZ) {
                radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ);
                radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ);
-               if (common->channel_width) {
-                       radio_caps->desc_word[5] =
-                               cpu_to_le16(common->channel_width << 12);
-                       radio_caps->desc_word[5] |= cpu_to_le16(FULL40M_ENABLE);
-               }
 
-               if (conf_is_ht40_minus(conf)) {
-                       radio_caps->desc_word[5] = 0;
-                       radio_caps->desc_word[5] |=
-                               cpu_to_le16(LOWER_20_ENABLE);
-                       radio_caps->desc_word[5] |=
-                               cpu_to_le16(LOWER_20_ENABLE >> 12);
-               }
-
-               if (conf_is_ht40_plus(conf)) {
-                       radio_caps->desc_word[5] = 0;
-                       radio_caps->desc_word[5] |=
-                               cpu_to_le16(UPPER_20_ENABLE);
-                       radio_caps->desc_word[5] |=
-                               cpu_to_le16(UPPER_20_ENABLE >> 12);
+               if (common->fsm_state == FSM_MAC_INIT_DONE) {
+                       struct ieee80211_hw *hw = adapter->hw;
+                       struct ieee80211_conf *conf = &hw->conf;
+                       if (conf_is_ht40_plus(conf)) {
+                               radio_caps->desc_word[5] =
+                                       cpu_to_le16(LOWER_20_ENABLE);
+                               radio_caps->desc_word[5] |=
+                                       cpu_to_le16(LOWER_20_ENABLE >> 12);
+                       } else if (conf_is_ht40_minus(conf)) {
+                               radio_caps->desc_word[5] =
+                                       cpu_to_le16(UPPER_20_ENABLE);
+                               radio_caps->desc_word[5] |=
+                                       cpu_to_le16(UPPER_20_ENABLE >> 12);
+                       } else {
+                               radio_caps->desc_word[5] =
+                                       cpu_to_le16(BW_40MHZ << 12);
+                               radio_caps->desc_word[5] |=
+                                       cpu_to_le16(FULL40M_ENABLE);
+                       }
                }
        }
 
+       radio_caps->sifs_tx_11n = cpu_to_le16(SIFS_TX_11N_VALUE);
+       radio_caps->sifs_tx_11b = cpu_to_le16(SIFS_TX_11B_VALUE);
+       radio_caps->slot_rx_11n = cpu_to_le16(SHORT_SLOT_VALUE);
+       radio_caps->ofdm_ack_tout = cpu_to_le16(OFDM_ACK_TOUT_VALUE);
+       radio_caps->cck_ack_tout = cpu_to_le16(CCK_ACK_TOUT_VALUE);
+       radio_caps->preamble_type = cpu_to_le16(LONG_PREAMBLE);
+
        radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8);
 
        for (ii = 0; ii < MAX_HW_QUEUES; ii++) {
@@ -588,7 +594,7 @@ static int rsi_program_bb_rf(struct rsi_common *common)
 
        mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
        mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA);
-       mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint << 8);
+       mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint);
 
        if (common->rf_reset) {
                mgmt_frame->desc_word[7] =  cpu_to_le16(RF_RESET_ENABLE);
@@ -615,6 +621,9 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode)
 {
        struct sk_buff *skb = NULL;
        struct rsi_vap_caps *vap_caps;
+       struct rsi_hw *adapter = common->priv;
+       struct ieee80211_hw *hw = adapter->hw;
+       struct ieee80211_conf *conf = &hw->conf;
        u16 vap_id = 0;
 
        rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__);
@@ -644,13 +653,24 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode)
        vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD);
 
        vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold);
-       vap_caps->default_mgmt_rate = 0;
-       if (conf_is_ht40(&common->priv->hw->conf)) {
-               vap_caps->default_ctrl_rate =
-                               cpu_to_le32(RSI_RATE_6 | FULL40M_ENABLE << 16);
-       } else {
+       vap_caps->default_mgmt_rate = cpu_to_le32(RSI_RATE_6);
+
+       if (common->band == IEEE80211_BAND_5GHZ) {
                vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6);
+               if (conf_is_ht40(&common->priv->hw->conf)) {
+                       vap_caps->default_ctrl_rate |=
+                               cpu_to_le32(FULL40M_ENABLE << 16);
+               }
+       } else {
+               vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_1);
+               if (conf_is_ht40_minus(conf))
+                       vap_caps->default_ctrl_rate |=
+                               cpu_to_le32(UPPER_20_ENABLE << 16);
+               else if (conf_is_ht40_plus(conf))
+                       vap_caps->default_ctrl_rate |=
+                               cpu_to_le32(LOWER_20_ENABLE << 16);
        }
+
        vap_caps->default_data_rate = 0;
        vap_caps->beacon_interval = cpu_to_le16(200);
        vap_caps->dtim_period = cpu_to_le16(4);
@@ -826,6 +846,63 @@ static int rsi_send_reset_mac(struct rsi_common *common)
        return rsi_send_internal_mgmt_frame(common, skb);
 }
 
+/**
+ * rsi_band_check() - This function programs the band
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+int rsi_band_check(struct rsi_common *common)
+{
+       struct rsi_hw *adapter = common->priv;
+       struct ieee80211_hw *hw = adapter->hw;
+       u8 prev_bw = common->channel_width;
+       u8 prev_ep = common->endpoint;
+       struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+       int status = 0;
+
+       if (common->band != curchan->band) {
+               common->rf_reset = 1;
+               common->band = curchan->band;
+       }
+
+       if ((hw->conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) ||
+           (hw->conf.chandef.width == NL80211_CHAN_WIDTH_20))
+               common->channel_width = BW_20MHZ;
+       else
+               common->channel_width = BW_40MHZ;
+
+       if (common->band == IEEE80211_BAND_2GHZ) {
+               if (common->channel_width)
+                       common->endpoint = EP_2GHZ_40MHZ;
+               else
+                       common->endpoint = EP_2GHZ_20MHZ;
+       } else {
+               if (common->channel_width)
+                       common->endpoint = EP_5GHZ_40MHZ;
+               else
+                       common->endpoint = EP_5GHZ_20MHZ;
+       }
+
+       if (common->endpoint != prev_ep) {
+               status = rsi_program_bb_rf(common);
+               if (status)
+                       return status;
+       }
+
+       if (common->channel_width != prev_bw) {
+               status = rsi_load_bootup_params(common);
+               if (status)
+                       return status;
+
+               status = rsi_load_radio_caps(common);
+               if (status)
+                       return status;
+       }
+
+       return status;
+}
+
 /**
  * rsi_set_channel() - This function programs the channel.
  * @common: Pointer to the driver private structure.
@@ -841,23 +918,6 @@ int rsi_set_channel(struct rsi_common *common, u16 channel)
        rsi_dbg(MGMT_TX_ZONE,
                "%s: Sending scan req frame\n", __func__);
 
-       if (common->band == IEEE80211_BAND_5GHZ) {
-               if ((channel >= 36) && (channel <= 64))
-                       channel = ((channel - 32) / 4);
-               else if ((channel > 64) && (channel <= 140))
-                       channel = ((channel - 102) / 4) + 8;
-               else if (channel >= 149)
-                       channel = ((channel - 151) / 4) + 18;
-               else
-                       return -EINVAL;
-       } else {
-               if (channel > 14) {
-                       rsi_dbg(ERR_ZONE, "%s: Invalid chno %d, band = %d\n",
-                               __func__, channel, common->band);
-                       return -EINVAL;
-               }
-       }
-
        skb = dev_alloc_skb(FRAME_DESC_SZ);
        if (!skb) {
                rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
@@ -877,6 +937,7 @@ int rsi_set_channel(struct rsi_common *common, u16 channel)
                                               (RSI_RF_TYPE << 4));
 
        mgmt_frame->desc_word[5] = cpu_to_le16(0x01);
+       mgmt_frame->desc_word[6] = cpu_to_le16(0x12);
 
        if (common->channel_width == BW_40MHZ)
                mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8);
@@ -950,7 +1011,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
        struct ieee80211_hw *hw = common->priv->hw;
        u8 band = hw->conf.chandef.chan->band;
        u8 num_supported_rates = 0;
-       u8 rate_offset = 0;
+       u8 rate_table_offset, rate_offset = 0;
        u32 rate_bitmap = common->bitrate_mask[band];
 
        u16 *selected_rates, min_rate;
@@ -986,14 +1047,19 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
        if (common->channel_width == BW_40MHZ)
                auto_rate->desc_word[7] |= cpu_to_le16(1);
 
-       if (band == IEEE80211_BAND_2GHZ)
-               min_rate = STD_RATE_01;
-       else
-               min_rate = STD_RATE_06;
+       if (band == IEEE80211_BAND_2GHZ) {
+               min_rate = RSI_RATE_1;
+               rate_table_offset = 0;
+       } else {
+               min_rate = RSI_RATE_6;
+               rate_table_offset = 4;
+       }
 
-       for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) {
+       for (ii = 0, jj = 0;
+            ii < (ARRAY_SIZE(rsi_rates) - rate_table_offset); ii++) {
                if (rate_bitmap & BIT(ii)) {
-                       selected_rates[jj++] = (rsi_rates[ii].bitrate / 5);
+                       selected_rates[jj++] =
+                       (rsi_rates[ii + rate_table_offset].bitrate / 5);
                        rate_offset++;
                }
        }
@@ -1006,13 +1072,6 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
                rate_offset += ARRAY_SIZE(mcs);
        }
 
-       if (rate_offset < (RSI_TBL_SZ / 2) - 1) {
-               for (ii = jj; ii < (RSI_TBL_SZ / 2); ii++) {
-                       selected_rates[jj++] = min_rate;
-                       rate_offset++;
-               }
-       }
-
        sort(selected_rates, jj, sizeof(u16), &rsi_compare, NULL);
 
        /* mapping the rates to RSI rates */
@@ -1028,25 +1087,25 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
 
        /* loading HT rates in the bottom half of the auto rate table */
        if (common->vif_info[0].is_ht) {
-               if (common->vif_info[0].sgi)
-                       auto_rate->supported_rates[rate_offset++] =
-                               cpu_to_le16(RSI_RATE_MCS7_SG);
-
                for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1;
                     ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) {
-                       if (common->vif_info[0].sgi)
+                       if (common->vif_info[0].sgi ||
+                           conf_is_ht40(&common->priv->hw->conf))
                                auto_rate->supported_rates[ii++] =
                                        cpu_to_le16(rsi_mcsrates[kk] | BIT(9));
                        auto_rate->supported_rates[ii] =
                                cpu_to_le16(rsi_mcsrates[kk--]);
                }
 
-               for (; ii < RSI_TBL_SZ; ii++) {
+               for (; ii < (RSI_TBL_SZ - 1); ii++) {
                        auto_rate->supported_rates[ii] =
                                cpu_to_le16(rsi_mcsrates[0]);
                }
        }
 
+       for (; ii < RSI_TBL_SZ; ii++)
+               auto_rate->supported_rates[ii] = cpu_to_le16(min_rate);
+
        auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2);
        auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2);
        auto_rate->desc_word[7] |= cpu_to_le16(0 << 8);
@@ -1140,6 +1199,49 @@ static int rsi_eeprom_read(struct rsi_common *common)
        return rsi_send_internal_mgmt_frame(common, skb);
 }
 
+/**
+ * This function sends a frame to block/unblock
+ * data queues in the firmware
+ *
+ * @param common Pointer to the driver private structure.
+ * @param block event - block if true, unblock if false
+ * @return 0 on success, -1 on failure.
+ */
+int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event)
+{
+       struct rsi_mac_frame *mgmt_frame;
+       struct sk_buff *skb;
+
+       rsi_dbg(MGMT_TX_ZONE, "%s: Sending block/unblock frame\n", __func__);
+
+       skb = dev_alloc_skb(FRAME_DESC_SZ);
+       if (!skb) {
+               rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+                       __func__);
+               return -ENOMEM;
+       }
+
+       memset(skb->data, 0, FRAME_DESC_SZ);
+       mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+       mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+       mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE);
+
+       if (block_event == true) {
+               rsi_dbg(INFO_ZONE, "blocking the data qs\n");
+               mgmt_frame->desc_word[4] = cpu_to_le16(0xf);
+       } else {
+               rsi_dbg(INFO_ZONE, "unblocking the data qs\n");
+               mgmt_frame->desc_word[5] = cpu_to_le16(0xf);
+       }
+
+       skb_put(skb, FRAME_DESC_SZ);
+
+       return rsi_send_internal_mgmt_frame(common, skb);
+
+}
+
+
 /**
  * rsi_handle_ta_confirm_type() - This function handles the confirm frames.
  * @common: Pointer to the driver private structure.
@@ -1164,7 +1266,7 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
                                common->fsm_state = FSM_EEPROM_READ_MAC_ADDR;
                        }
                } else {
-                       rsi_dbg(ERR_ZONE,
+                       rsi_dbg(INFO_ZONE,
                                "%s: Received bootup params cfm in %d state\n",
                                 __func__, common->fsm_state);
                        return 0;
@@ -1227,7 +1329,7 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
                                        __func__);
                        }
                } else {
-                       rsi_dbg(ERR_ZONE,
+                       rsi_dbg(INFO_ZONE,
                                "%s: Received radio caps cfm in %d state\n",
                                 __func__, common->fsm_state);
                        return 0;
@@ -1245,7 +1347,10 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
                                return rsi_mac80211_attach(common);
                        }
                } else {
-                       goto out;
+                       rsi_dbg(INFO_ZONE,
+                               "%s: Received bbb_rf cfm in %d state\n",
+                                __func__, common->fsm_state);
+                       return 0;
                }
                break;
 
index 8e48e72bae204ae0410d40743fe6e00fe918fe1f..702593f199971a89c227b3015b243efb203920c4 100644 (file)
@@ -81,6 +81,16 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb)
                /* Send fixed rate */
                frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE);
                frame_desc[4] = cpu_to_le16(common->min_rate);
+
+               if (conf_is_ht40(&common->priv->hw->conf))
+                       frame_desc[5] = cpu_to_le16(FULL40M_ENABLE);
+
+               if (common->vif_info[0].sgi) {
+                       if (common->min_rate & 0x100) /* Only MCS rates */
+                               frame_desc[4] |=
+                                       cpu_to_le16(ENABLE_SHORTGI_RATE);
+               }
+
        }
 
        frame_desc[6] |= cpu_to_le16(seq_num & 0xfff);
@@ -116,6 +126,8 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
        struct ieee80211_hdr *wh = NULL;
        struct ieee80211_tx_info *info;
        struct ieee80211_bss_conf *bss = NULL;
+       struct ieee80211_hw *hw = adapter->hw;
+       struct ieee80211_conf *conf = &hw->conf;
        struct skb_info *tx_params;
        int status = -E2BIG;
        __le16 *msg = NULL;
@@ -175,6 +187,11 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
        else
                msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE);
 
+       if (conf_is_ht40(conf)) {
+               msg[4] = cpu_to_le16(0xB | RSI_11G_MODE);
+               msg[5] = cpu_to_le16(0x6);
+       }
+
        /* Indicate to firmware to give cfm */
        if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) {
                msg[1] |= cpu_to_le16(BIT(10));
index 46e7af446f01028ad15eec09e461037bc9f9211c..8428858204a6763a6d4ccfbeb6c2c81a12e7a33f 100644 (file)
@@ -820,9 +820,11 @@ static struct sdio_driver rsi_driver = {
  */
 static int rsi_module_init(void)
 {
-       sdio_register_driver(&rsi_driver);
+       int ret;
+
+       ret = sdio_register_driver(&rsi_driver);
        rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
-       return 0;
+       return ret;
 }
 
 /**
index 20d11ccfffe3b757c06a232ed3c057228339a76b..4834a9abc17177a5cd769418475a433765ed075a 100644 (file)
@@ -401,14 +401,16 @@ void rsi_interrupt_handler(struct rsi_hw *adapter)
                        case BUFFER_AVAILABLE:
                                dev->rx_info.watch_bufferfull_count = 0;
                                dev->rx_info.buffer_full = false;
+                               dev->rx_info.semi_buffer_full = false;
                                dev->rx_info.mgmt_buffer_full = false;
                                rsi_sdio_ack_intr(common->priv,
                                                  (1 << PKT_BUFF_AVAILABLE));
-                               rsi_set_event((&common->tx_thread.event));
+                               rsi_set_event(&common->tx_thread.event);
+
                                rsi_dbg(ISR_ZONE,
-                                       "%s: ==> BUFFER_AVILABLE <==\n",
+                                       "%s: ==> BUFFER_AVAILABLE <==\n",
                                        __func__);
-                               dev->rx_info.buf_avilable_counter++;
+                               dev->rx_info.buf_available_counter++;
                                break;
 
                        case FIRMWARE_ASSERT_IND:
index 4c46e5631e2f0bf81196dd8b89e3a1dea8b00ec7..ef5d394f185bfb46cc24c9170eecf4f6cee4dab8 100644 (file)
@@ -25,7 +25,7 @@
  * @len: Length to be written.
  * @endpoint: Type of endpoint.
  *
- * Return: status: 0 on success, -1 on failure.
+ * Return: status: 0 on success, a negative error code on failure.
  */
 static int rsi_usb_card_write(struct rsi_hw *adapter,
                              void *buf,
@@ -60,7 +60,7 @@ static int rsi_usb_card_write(struct rsi_hw *adapter,
  * @data: Pointer to the data that has to be written.
  * @count: Number of multiple bytes to be written.
  *
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
  */
 static int rsi_write_multiple(struct rsi_hw *adapter,
                              u8 endpoint,
@@ -147,7 +147,7 @@ static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface,
  * @value: Value to be read.
  * @len: length of data to be read.
  *
- * Return: status: 0 on success, -1 on failure.
+ * Return: status: 0 on success, a negative error code on failure.
  */
 static int rsi_usb_reg_read(struct usb_device *usbdev,
                            u32 reg,
@@ -189,7 +189,7 @@ static int rsi_usb_reg_read(struct usb_device *usbdev,
  * @value: Value to write.
  * @len: Length of data to be written.
  *
- * Return: status: 0 on success, -1 on failure.
+ * Return: status: 0 on success, a negative error code on failure.
  */
 static int rsi_usb_reg_write(struct usb_device *usbdev,
                             u32 reg,
@@ -249,7 +249,7 @@ static void rsi_rx_done_handler(struct urb *urb)
  * rsi_rx_urb_submit() - This function submits the given URB to the USB stack.
  * @adapter: Pointer to the adapter structure.
  *
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
  */
 static int rsi_rx_urb_submit(struct rsi_hw *adapter)
 {
@@ -281,7 +281,7 @@ static int rsi_rx_urb_submit(struct rsi_hw *adapter)
  * @data: Pointer to the data that has to be written.
  * @count: Number of multiple bytes to be written on to the registers.
  *
- * Return: status: 0 on success, -1 on failure.
+ * Return: status: 0 on success, a negative error code on failure.
  */
 int rsi_usb_write_register_multiple(struct rsi_hw *adapter,
                                    u32 addr,
@@ -331,7 +331,7 @@ int rsi_usb_write_register_multiple(struct rsi_hw *adapter,
  * @pkt: Pointer to the data to be written on to the card.
  * @len: Length of the data to be written on to the card.
  *
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
  */
 static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter,
                                       u8 *pkt,
@@ -359,6 +359,7 @@ static void rsi_deinit_usb_interface(struct rsi_hw *adapter)
        struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
 
        rsi_kill_thread(&dev->rx_thread);
+       usb_free_urb(dev->rx_usb_urb[0]);
        kfree(adapter->priv->rx_data_pkt);
        kfree(dev->tx_buffer);
 }
@@ -368,7 +369,7 @@ static void rsi_deinit_usb_interface(struct rsi_hw *adapter)
  * @adapter: Pointer to the adapter structure.
  * @pfunction: Pointer to USB interface structure.
  *
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
  */
 static int rsi_init_usb_interface(struct rsi_hw *adapter,
                                  struct usb_interface *pfunction)
@@ -397,8 +398,16 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
                return -ENOMEM;
        }
 
-       rsi_dev->tx_buffer = kmalloc(2048, GFP_ATOMIC);
+       rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL);
+       if (!rsi_dev->tx_buffer) {
+               status = -ENOMEM;
+               goto fail_tx;
+       }
        rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL);
+       if (!rsi_dev->rx_usb_urb[0]) {
+               status = -ENOMEM;
+               goto fail_rx;
+       }
        rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt;
        rsi_dev->tx_blk_size = 252;
 
@@ -413,7 +422,7 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
                                    rsi_usb_rx_thread, "RX-Thread");
        if (status) {
                rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
-               goto fail;
+               goto fail_thread;
        }
 
 #ifdef CONFIG_RSI_DEBUGFS
@@ -424,8 +433,11 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
        rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__);
        return 0;
 
-fail:
+fail_thread:
+       usb_free_urb(rsi_dev->rx_usb_urb[0]);
+fail_rx:
        kfree(rsi_dev->tx_buffer);
+fail_tx:
        kfree(common->rx_data_pkt);
        return status;
 }
@@ -437,7 +449,7 @@ fail:
  * @pfunction: Pointer to the USB interface structure.
  * @id: Pointer to the usb_device_id structure.
  *
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
  */
 static int rsi_probe(struct usb_interface *pfunction,
                     const struct usb_device_id *id)
@@ -445,6 +457,7 @@ static int rsi_probe(struct usb_interface *pfunction,
        struct rsi_hw *adapter;
        struct rsi_91x_usbdev *dev;
        u16 fw_status;
+       int status;
 
        rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
 
@@ -452,10 +465,11 @@ static int rsi_probe(struct usb_interface *pfunction,
        if (!adapter) {
                rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
                        __func__);
-               return 1;
+               return -ENOMEM;
        }
 
-       if (rsi_init_usb_interface(adapter, pfunction)) {
+       status = rsi_init_usb_interface(adapter, pfunction);
+       if (status) {
                rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n",
                        __func__);
                goto err;
@@ -465,26 +479,30 @@ static int rsi_probe(struct usb_interface *pfunction,
 
        dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
 
-       if (rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2) < 0)
+       status = rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2);
+       if (status)
                goto err1;
        else
                fw_status &= 1;
 
        if (!fw_status) {
-               if (rsi_usb_device_init(adapter->priv)) {
+               status = rsi_usb_device_init(adapter->priv);
+               if (status) {
                        rsi_dbg(ERR_ZONE, "%s: Failed in device init\n",
                                __func__);
                        goto err1;
                }
 
-               if (rsi_usb_reg_write(dev->usbdev,
-                                     USB_INTERNAL_REG_1,
-                                     RSI_USB_READY_MAGIC_NUM, 1) < 0)
+               status = rsi_usb_reg_write(dev->usbdev,
+                                          USB_INTERNAL_REG_1,
+                                          RSI_USB_READY_MAGIC_NUM, 1);
+               if (status)
                        goto err1;
                rsi_dbg(INIT_ZONE, "%s: Performed device init\n", __func__);
        }
 
-       if (rsi_rx_urb_submit(adapter))
+       status = rsi_rx_urb_submit(adapter);
+       if (status)
                goto err1;
 
        return 0;
@@ -493,7 +511,7 @@ err1:
 err:
        rsi_91x_deinit(adapter);
        rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
-       return 1;
+       return status;
 }
 
 /**
@@ -550,33 +568,7 @@ static struct usb_driver rsi_driver = {
 #endif
 };
 
-/**
- * rsi_module_init() - This function registers the client driver.
- * @void: Void.
- *
- * Return: 0 on success.
- */
-static int rsi_module_init(void)
-{
-       usb_register(&rsi_driver);
-       rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
-       return 0;
-}
-
-/**
- * rsi_module_exit() - This function unregisters the client driver.
- * @void: Void.
- *
- * Return: None.
- */
-static void rsi_module_exit(void)
-{
-       usb_deregister(&rsi_driver);
-       rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__);
-}
-
-module_init(rsi_module_init);
-module_exit(rsi_module_exit);
+module_usb_driver(rsi_driver);
 
 MODULE_AUTHOR("Redpine Signals Inc");
 MODULE_DESCRIPTION("Common USB layer for RSI drivers");
index 2cb73e7edb98d9e11f0d2e02902517a81b4051aa..5baed945f60e2cb276eeeaa7a520156132af4a55 100644 (file)
@@ -115,6 +115,7 @@ struct wmm_qinfo {
        s32 weight;
        s32 wme_params;
        s32 pkt_contended;
+       s32 txop;
 };
 
 struct transmit_q_stats {
@@ -141,6 +142,12 @@ struct rsi_thread {
        atomic_t thread_done;
 };
 
+struct cqm_info {
+       s8 last_cqm_event_rssi;
+       int rssi_thold;
+       u32 rssi_hyst;
+};
+
 struct rsi_hw;
 
 struct rsi_common {
@@ -192,6 +199,11 @@ struct rsi_common {
        u8 selected_qnum;
        u32 pkt_cnt;
        u8 min_weight;
+
+       /* bgscan related */
+       struct cqm_info cqm_info;
+
+       bool hw_data_qs_blocked;
 };
 
 struct rsi_hw {
index 225215a3b8bb484d76b47ed853afb3aeb6eb2130..3741173fd3acea1132224caaeab02aba47d84b36 100644 (file)
@@ -69,6 +69,7 @@
 
 #define RSI_LMAC_CLOCK_80MHZ            0x1
 #define RSI_ENABLE_40MHZ                (0x1 << 3)
+#define ENABLE_SHORTGI_RATE            BIT(9)
 
 #define RX_BA_INDICATION                1
 #define RSI_TBL_SZ                      40
 #define BW_20MHZ                        0
 #define BW_40MHZ                        1
 
+#define EP_2GHZ_20MHZ                  0
+#define EP_2GHZ_40MHZ                  1
+#define EP_5GHZ_20MHZ                  2
+#define EP_5GHZ_40MHZ                  3
+
+#define SIFS_TX_11N_VALUE              580
+#define SIFS_TX_11B_VALUE              346
+#define SHORT_SLOT_VALUE               360
+#define LONG_SLOT_VALUE                        640
+#define OFDM_ACK_TOUT_VALUE            2720
+#define CCK_ACK_TOUT_VALUE             9440
+#define LONG_PREAMBLE                  0x0000
+#define SHORT_PREAMBLE                 0x0001
+
 #define RSI_SUPP_FILTERS       (FIF_ALLMULTI | FIF_PROBE_REQ |\
                                 FIF_BCN_PRBRESP_PROMISC)
 enum opmode {
@@ -153,7 +168,7 @@ enum cmd_frame_type {
        SCAN_REQUEST,
        TSF_UPDATE,
        PEER_NOTIFY,
-       BLOCK_UNBLOCK,
+       BLOCK_HW_QUEUE,
        SET_KEY_REQ,
        AUTO_RATE_IND,
        BOOTUP_PARAMS_REQUEST,
@@ -238,6 +253,12 @@ struct rsi_radio_caps {
        u8 num_11n_rates;
        u8 num_11ac_rates;
        __le16 gcpd_per_rate[20];
+       __le16 sifs_tx_11n;
+       __le16 sifs_tx_11b;
+       __le16 slot_rx_11n;
+       __le16 ofdm_ack_tout;
+       __le16 cck_ack_tout;
+       __le16 preamble_type;
 } __packed;
 
 static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
@@ -272,6 +293,7 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid,
 int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len,
                     u8 key_type, u8 key_id, u32 cipher);
 int rsi_set_channel(struct rsi_common *common, u16 chno);
+int rsi_send_block_unblock_frame(struct rsi_common *common, bool event);
 void rsi_inform_bss_status(struct rsi_common *common, u8 status,
                           const u8 *bssid, u8 qos_enable, u16 aid);
 void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb);
@@ -283,4 +305,5 @@ void rsi_core_qos_processor(struct rsi_common *common);
 void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb);
 int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb);
 int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb);
+int rsi_band_check(struct rsi_common *common);
 #endif
index df4b5e20e05f056d8e6dad66628c73dd32a2d3f4..c7e8f2be790184399f6aa3f102d054f58852d423 100644 (file)
@@ -30,7 +30,7 @@
 
 enum sdio_interrupt_type {
        BUFFER_FULL         = 0x0,
-       BUFFER_AVAILABLE    = 0x1,
+       BUFFER_AVAILABLE    = 0x2,
        FIRMWARE_ASSERT_IND = 0x3,
        MSDU_PACKET_PENDING = 0x4,
        UNKNOWN_INT         = 0XE
@@ -42,7 +42,7 @@ enum sdio_interrupt_type {
 #define PKT_MGMT_BUFF_FULL                      2
 #define MSDU_PKT_PENDING                        3
 /* Interrupt Bit Related Macros */
-#define PKT_BUFF_AVAILABLE                      0
+#define PKT_BUFF_AVAILABLE                      1
 #define FW_ASSERT_IND                           2
 
 #define RSI_DEVICE_BUFFER_STATUS_REGISTER       0xf3
@@ -84,7 +84,7 @@ enum sdio_interrupt_type {
 #define TA_HOLD_THREAD_VALUE         cpu_to_le32(0xF)
 #define TA_RELEASE_THREAD_VALUE      cpu_to_le32(0xF)
 #define TA_BASE_ADDR                 0x2200
-#define MISC_CFG_BASE_ADDR           0x4150
+#define MISC_CFG_BASE_ADDR           0x4105
 
 struct receive_info {
        bool buffer_full;
@@ -98,7 +98,7 @@ struct receive_info {
        u32 total_sdio_msdu_pending_intr;
        u32 total_sdio_unknown_intr;
        u32 buf_full_counter;
-       u32 buf_avilable_counter;
+       u32 buf_available_counter;
 };
 
 struct rsi_91x_sdiodev {
index c17fcf272728cb06ae25e95787003f6f59f52dba..893c9d5f3d6f09c659710a855fd8425a5475d28a 100644 (file)
@@ -947,6 +947,40 @@ static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
        return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
 }
 
+static void rt2800_update_beacons_setup(struct rt2x00_dev *rt2x00dev)
+{
+       struct data_queue *queue = rt2x00dev->bcn;
+       struct queue_entry *entry;
+       int i, bcn_num = 0;
+       u64 off, reg = 0;
+       u32 bssid_dw1;
+
+       /*
+        * Setup offsets of all active beacons in BCN_OFFSET{0,1} registers.
+        */
+       for (i = 0; i < queue->limit; i++) {
+               entry = &queue->entries[i];
+               if (!test_bit(ENTRY_BCN_ENABLED, &entry->flags))
+                       continue;
+               off = rt2800_get_beacon_offset(rt2x00dev, entry->entry_idx);
+               reg |= off << (8 * bcn_num);
+               bcn_num++;
+       }
+
+       WARN_ON_ONCE(bcn_num != rt2x00dev->intf_beaconing);
+
+       rt2800_register_write(rt2x00dev, BCN_OFFSET0, (u32) reg);
+       rt2800_register_write(rt2x00dev, BCN_OFFSET1, (u32) (reg >> 32));
+
+       /*
+        * H/W sends up to MAC_BSSID_DW1_BSS_BCN_NUM + 1 consecutive beacons.
+        */
+       rt2800_register_read(rt2x00dev, MAC_BSSID_DW1, &bssid_dw1);
+       rt2x00_set_field32(&bssid_dw1, MAC_BSSID_DW1_BSS_BCN_NUM,
+                          bcn_num > 0 ? bcn_num - 1 : 0);
+       rt2800_register_write(rt2x00dev, MAC_BSSID_DW1, bssid_dw1);
+}
+
 void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -1003,6 +1037,12 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
 
        rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
                                   entry->skb->len + padding_len);
+       __set_bit(ENTRY_BCN_ENABLED, &entry->flags);
+
+       /*
+        * Change global beacons settings.
+        */
+       rt2800_update_beacons_setup(rt2x00dev);
 
        /*
         * Restore beaconing state.
@@ -1053,7 +1093,12 @@ void rt2800_clear_beacon(struct queue_entry *entry)
         * Clear beacon.
         */
        rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
+       __clear_bit(ENTRY_BCN_ENABLED, &entry->flags);
 
+       /*
+        * Change global beacons settings.
+        */
+       rt2800_update_beacons_setup(rt2x00dev);
        /*
         * Restore beaconing state.
         */
@@ -1556,7 +1601,7 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
                if (!is_zero_ether_addr((const u8 *)conf->bssid)) {
                        reg = le32_to_cpu(conf->bssid[1]);
                        rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
-                       rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
+                       rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 0);
                        conf->bssid[1] = cpu_to_le32(reg);
                }
 
@@ -4517,28 +4562,6 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
        if (ret)
                return ret;
 
-       rt2800_register_read(rt2x00dev, BCN_OFFSET0, &reg);
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0,
-                          rt2800_get_beacon_offset(rt2x00dev, 0));
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1,
-                          rt2800_get_beacon_offset(rt2x00dev, 1));
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2,
-                          rt2800_get_beacon_offset(rt2x00dev, 2));
-       rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3,
-                          rt2800_get_beacon_offset(rt2x00dev, 3));
-       rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
-
-       rt2800_register_read(rt2x00dev, BCN_OFFSET1, &reg);
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4,
-                          rt2800_get_beacon_offset(rt2x00dev, 4));
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5,
-                          rt2800_get_beacon_offset(rt2x00dev, 5));
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6,
-                          rt2800_get_beacon_offset(rt2x00dev, 6));
-       rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7,
-                          rt2800_get_beacon_offset(rt2x00dev, 7));
-       rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
-
        rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
        rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
 
index 832006b5aab158e4e14356001ef8f40a952753a2..573897b8e878a2e6bfc3aa61a9df72d9b99e0372 100644 (file)
@@ -1284,6 +1284,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
        /* Arcadyan */
        { USB_DEVICE(0x043e, 0x7a12) },
        { USB_DEVICE(0x043e, 0x7a32) },
+       /* ASUS */
+       { USB_DEVICE(0x0b05, 0x17e8) },
        /* Azurewave */
        { USB_DEVICE(0x13d3, 0x3329) },
        { USB_DEVICE(0x13d3, 0x3365) },
@@ -1320,6 +1322,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x057c, 0x8501) },
        /* Buffalo */
        { USB_DEVICE(0x0411, 0x0241) },
+       { USB_DEVICE(0x0411, 0x0253) },
        /* D-Link */
        { USB_DEVICE(0x2001, 0x3c1a) },
        { USB_DEVICE(0x2001, 0x3c21) },
@@ -1410,6 +1413,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
        { USB_DEVICE(0x0df6, 0x0053) },
        { USB_DEVICE(0x0df6, 0x0069) },
        { USB_DEVICE(0x0df6, 0x006f) },
+       { USB_DEVICE(0x0df6, 0x0078) },
        /* SMC */
        { USB_DEVICE(0x083a, 0xa512) },
        { USB_DEVICE(0x083a, 0xc522) },
index 4fa43a2eeb732bc1e5c5fe8308ae3d570bbc7dcb..9967a1d9f0eceee3a68cfc89534a6df1f39b1763 100644 (file)
@@ -141,8 +141,11 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,
        if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
                return;
 
-       if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags))
+       if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) {
+               mutex_lock(&intf->beacon_skb_mutex);
                rt2x00queue_update_beacon(rt2x00dev, vif);
+               mutex_unlock(&intf->beacon_skb_mutex);
+       }
 }
 
 static void rt2x00lib_intf_scheduled(struct work_struct *work)
@@ -216,7 +219,7 @@ static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac,
         * never be called for USB devices.
         */
        WARN_ON(rt2x00_is_usb(rt2x00dev));
-       rt2x00queue_update_beacon_locked(rt2x00dev, vif);
+       rt2x00queue_update_beacon(rt2x00dev, vif);
 }
 
 void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
@@ -1470,8 +1473,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
        /*
         * Free the driver data.
         */
-       if (rt2x00dev->drv_data)
-               kfree(rt2x00dev->drv_data);
+       kfree(rt2x00dev->drv_data);
 }
 EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev);
 
index 004dff9b962d9753a0a7b43a52983acb0032f748..ad6e5a8d1e10fbc06dac3895288168f1d7d2fb65 100644 (file)
@@ -626,25 +626,24 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
         * Start/stop beaconing.
         */
        if (changes & BSS_CHANGED_BEACON_ENABLED) {
+               mutex_lock(&intf->beacon_skb_mutex);
                if (!bss_conf->enable_beacon && intf->enable_beacon) {
                        rt2x00dev->intf_beaconing--;
                        intf->enable_beacon = false;
-                       /*
-                        * Clear beacon in the H/W for this vif. This is needed
-                        * to disable beaconing on this particular interface
-                        * and keep it running on other interfaces.
-                        */
-                       rt2x00queue_clear_beacon(rt2x00dev, vif);
 
                        if (rt2x00dev->intf_beaconing == 0) {
                                /*
                                 * Last beaconing interface disabled
                                 * -> stop beacon queue.
                                 */
-                               mutex_lock(&intf->beacon_skb_mutex);
                                rt2x00queue_stop_queue(rt2x00dev->bcn);
-                               mutex_unlock(&intf->beacon_skb_mutex);
                        }
+                       /*
+                        * Clear beacon in the H/W for this vif. This is needed
+                        * to disable beaconing on this particular interface
+                        * and keep it running on other interfaces.
+                        */
+                       rt2x00queue_clear_beacon(rt2x00dev, vif);
                } else if (bss_conf->enable_beacon && !intf->enable_beacon) {
                        rt2x00dev->intf_beaconing++;
                        intf->enable_beacon = true;
@@ -660,11 +659,10 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
                                 * First beaconing interface enabled
                                 * -> start beacon queue.
                                 */
-                               mutex_lock(&intf->beacon_skb_mutex);
                                rt2x00queue_start_queue(rt2x00dev->bcn);
-                               mutex_unlock(&intf->beacon_skb_mutex);
                        }
                }
+               mutex_unlock(&intf->beacon_skb_mutex);
        }
 
        /*
@@ -801,6 +799,8 @@ int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
 
        setup.tx = tx_ant;
        setup.rx = rx_ant;
+       setup.rx_chain_num = 0;
+       setup.tx_chain_num = 0;
 
        rt2x00lib_config_antenna(rt2x00dev, setup);
 
index 6f236ea180aa3df23dbf8be831f367c020f4a1a2..f0178fd4fe5ff8c078749e6df8b82d1ea7a26a0c 100644 (file)
@@ -119,14 +119,12 @@ static int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev,
        /*
         * Allocate DMA memory for descriptor and buffer.
         */
-       addr = dma_alloc_coherent(rt2x00dev->dev,
-                                 queue->limit * queue->desc_size,
-                                 &dma, GFP_KERNEL);
+       addr = dma_zalloc_coherent(rt2x00dev->dev,
+                                  queue->limit * queue->desc_size, &dma,
+                                  GFP_KERNEL);
        if (!addr)
                return -ENOMEM;
 
-       memset(addr, 0, queue->limit * queue->desc_size);
-
        /*
         * Initialize all queue entries to contain valid addresses.
         */
index 5642ccceca7c5544ba6e2aad62b0f00b0870a2c1..8e68f87ab13c3081f062acc69fb71f59601e836f 100644 (file)
@@ -754,8 +754,6 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
        if (unlikely(!intf->beacon))
                return -ENOBUFS;
 
-       mutex_lock(&intf->beacon_skb_mutex);
-
        /*
         * Clean up the beacon skb.
         */
@@ -768,13 +766,11 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
        if (rt2x00dev->ops->lib->clear_beacon)
                rt2x00dev->ops->lib->clear_beacon(intf->beacon);
 
-       mutex_unlock(&intf->beacon_skb_mutex);
-
        return 0;
 }
 
-int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
-                                    struct ieee80211_vif *vif)
+int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
+                             struct ieee80211_vif *vif)
 {
        struct rt2x00_intf *intf = vif_to_intf(vif);
        struct skb_frame_desc *skbdesc;
@@ -815,19 +811,6 @@ int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
 
 }
 
-int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
-                             struct ieee80211_vif *vif)
-{
-       struct rt2x00_intf *intf = vif_to_intf(vif);
-       int ret;
-
-       mutex_lock(&intf->beacon_skb_mutex);
-       ret = rt2x00queue_update_beacon_locked(rt2x00dev, vif);
-       mutex_unlock(&intf->beacon_skb_mutex);
-
-       return ret;
-}
-
 bool rt2x00queue_for_each_entry(struct data_queue *queue,
                                enum queue_index start,
                                enum queue_index end,
index c48125be0e34247cfe30ba51be437bb03ef66ed3..2233b911a1d7d7c5992db5681dcc1907d9719769 100644 (file)
@@ -353,6 +353,7 @@ struct txentry_desc {
  */
 enum queue_entry_flags {
        ENTRY_BCN_ASSIGNED,
+       ENTRY_BCN_ENABLED,
        ENTRY_OWNER_DEVICE_DATA,
        ENTRY_DATA_PENDING,
        ENTRY_DATA_IO_FAILED,
index 2c1c02bafa10bbfe1f198279400a84ad08256bb4..4b904f70818487fde779ffd0c524284f23972e84 100644 (file)
@@ -16,6 +16,7 @@
  *
  * based also on:
  *  - portions of rtl8187se Linux staging driver, Copyright Realtek corp.
+ *    (available in drivers/staging/rtl8187se directory of Linux 3.14)
  *  - other GPL, unpublished (until now), Linux driver code,
  *    Copyright Larry Finger <Larry.Finger@lwfinger.net>
  *
@@ -209,7 +210,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
        struct rtl8180_priv *priv = dev->priv;
        struct rtl818x_rx_cmd_desc *cmd_desc;
        unsigned int count = 32;
-       u8 signal, agc, sq;
+       u8 agc, sq, signal = 1;
        dma_addr_t mapping;
 
        while (count--) {
@@ -222,12 +223,20 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
                        struct rtl8187se_rx_desc *desc = entry;
 
                        flags = le32_to_cpu(desc->flags);
+                       /* if ownership flag is set, then we can trust the
+                        * HW has written other fields. We must not trust
+                        * other descriptor data read before we checked (read)
+                        * the ownership flag
+                        */
+                       rmb();
                        flags2 = le32_to_cpu(desc->flags2);
                        tsft = le64_to_cpu(desc->tsft);
                } else {
                        struct rtl8180_rx_desc *desc = entry;
 
                        flags = le32_to_cpu(desc->flags);
+                       /* same as above */
+                       rmb();
                        flags2 = le32_to_cpu(desc->flags2);
                        tsft = le64_to_cpu(desc->tsft);
                }
@@ -266,18 +275,21 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
                        rx_status.rate_idx = (flags >> 20) & 0xF;
                        agc = (flags2 >> 17) & 0x7F;
 
-                       if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) {
+                       switch (priv->chip_family) {
+                       case RTL818X_CHIP_FAMILY_RTL8185:
                                if (rx_status.rate_idx > 3)
-                                       signal = 90 - clamp_t(u8, agc, 25, 90);
+                                       signal = -clamp_t(u8, agc, 25, 90) - 9;
                                else
-                                       signal = 95 - clamp_t(u8, agc, 30, 95);
-                       } else if (priv->chip_family ==
-                                  RTL818X_CHIP_FAMILY_RTL8180) {
+                                       signal = -clamp_t(u8, agc, 30, 95);
+                               break;
+                       case RTL818X_CHIP_FAMILY_RTL8180:
                                sq = flags2 & 0xff;
                                signal = priv->rf->calc_rssi(agc, sq);
-                       } else {
+                               break;
+                       case RTL818X_CHIP_FAMILY_RTL8187SE:
                                /* TODO: rtl8187se rssi */
                                signal = 10;
+                               break;
                        }
                        rx_status.signal = signal;
                        rx_status.freq = dev->conf.chandef.chan->center_freq;
@@ -336,7 +348,6 @@ static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio)
                        info->flags |= IEEE80211_TX_STAT_ACK;
 
                info->status.rates[0].count = (flags & 0xFF) + 1;
-               info->status.rates[1].idx = -1;
 
                ieee80211_tx_status_irqsafe(dev, skb);
                if (ring->entries - skb_queue_len(&ring->queue) == 2)
@@ -528,9 +539,7 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
        entry->plcp_len = cpu_to_le16(plcp_len);
        entry->tx_buf = cpu_to_le32(mapping);
 
-       entry->flags2 = info->control.rates[1].idx >= 0 ?
-               ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0;
-       entry->retry_limit = info->control.rates[0].count;
+       entry->retry_limit = info->control.rates[0].count - 1;
 
        /* We must be sure that tx_flags is written last because the HW
         * looks at it to check if the rest of data is valid or not
@@ -852,7 +861,7 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
 
        if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) {
                rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
-               rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
+               rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0);
        } else {
                rtl818x_iowrite8(priv, &priv->map->SECURITY, 0);
 
@@ -868,6 +877,16 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
                reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
                rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2));
                rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+               /* fix eccessive IFS after CTS-to-self */
+               if (priv->map_pio) {
+                       u8 reg;
+
+                       reg = rtl818x_ioread8(priv, &priv->map->PGSELECT);
+                       rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1);
+                       rtl818x_iowrite8(priv, REG_ADDR1(0xff), 0x35);
+                       rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
+               } else
+                       rtl818x_iowrite8(priv, REG_ADDR1(0x1ff), 0x35);
        }
 
        if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
@@ -1450,9 +1469,10 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev,
        vif_priv = (struct rtl8180_vif *)&vif->drv_priv;
 
        if (changed & BSS_CHANGED_BSSID) {
-               for (i = 0; i < ETH_ALEN; i++)
-                       rtl818x_iowrite8(priv, &priv->map->BSSID[i],
-                                        info->bssid[i]);
+               rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->BSSID[0],
+                                 le16_to_cpu(*(__le16 *)info->bssid));
+               rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->BSSID[2],
+                                 le32_to_cpu(*(__le32 *)(info->bssid + 2)));
 
                if (is_valid_ether_addr(info->bssid)) {
                        if (vif->type == NL80211_IFTYPE_ADHOC)
@@ -1723,17 +1743,20 @@ static int rtl8180_probe(struct pci_dev *pdev,
        priv = dev->priv;
        priv->pdev = pdev;
 
-       dev->max_rates = 2;
+       dev->max_rates = 1;
        SET_IEEE80211_DEV(dev, &pdev->dev);
        pci_set_drvdata(pdev, dev);
 
+       priv->map_pio = false;
        priv->map = pci_iomap(pdev, 1, mem_len);
-       if (!priv->map)
+       if (!priv->map) {
                priv->map = pci_iomap(pdev, 0, io_len);
+               priv->map_pio = true;
+       }
 
        if (!priv->map) {
-               printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n",
-                      pci_name(pdev));
+               dev_err(&pdev->dev, "Cannot map device memory/PIO\n");
+               err = -ENOMEM;
                goto err_free_dev;
        }
 
@@ -1751,8 +1774,7 @@ static int rtl8180_probe(struct pci_dev *pdev,
        dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
 
        dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
-                    IEEE80211_HW_RX_INCLUDES_FCS |
-                    IEEE80211_HW_SIGNAL_UNSPEC;
+               IEEE80211_HW_RX_INCLUDES_FCS;
        dev->vif_data_size = sizeof(struct rtl8180_vif);
        dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                                        BIT(NL80211_IFTYPE_ADHOC);
@@ -1783,12 +1805,19 @@ static int rtl8180_probe(struct pci_dev *pdev,
 
        case RTL818X_TX_CONF_RTL8187SE:
                chip_name = "RTL8187SE";
+               if (priv->map_pio) {
+                       dev_err(&pdev->dev,
+                               "MMIO failed. PIO not supported on RTL8187SE\n");
+                       err = -ENOMEM;
+                       goto err_iounmap;
+               }
                priv->chip_family = RTL818X_CHIP_FAMILY_RTL8187SE;
                break;
 
        default:
                printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n",
                       pci_name(pdev), reg >> 25);
+               err = -ENODEV;
                goto err_iounmap;
        }
 
@@ -1809,6 +1838,11 @@ static int rtl8180_probe(struct pci_dev *pdev,
                pci_try_set_mwi(pdev);
        }
 
+       if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185)
+               dev->flags |= IEEE80211_HW_SIGNAL_DBM;
+       else
+               dev->flags |= IEEE80211_HW_SIGNAL_UNSPEC;
+
        rtl8180_eeprom_read(priv);
 
        switch (priv->rf_type) {
@@ -1834,12 +1868,14 @@ static int rtl8180_probe(struct pci_dev *pdev,
        default:
                printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n",
                       pci_name(pdev), priv->rf_type);
+               err = -ENODEV;
                goto err_iounmap;
        }
 
        if (!priv->rf) {
                printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n",
                       pci_name(pdev), rf_name);
+               err = -ENODEV;
                goto err_iounmap;
        }
 
index 291a55970d1ab0ebdaabcc6463a4a458afab307d..e8243a44d6b6074136921154d498bbf63facc0c9 100644 (file)
@@ -107,6 +107,7 @@ struct rtl8180_priv {
        struct ieee80211_vif *vif;
 
        /* rtl8180 driver specific */
+       bool map_pio;
        spinlock_t lock;
        void *rx_ring;
        u8 rx_ring_sz;
index 871fc3c6d559f63b65f1322417a1e2d61a8fc955..049f4c8d98a8675baa69bd993a1bfe268bd08f2f 100644 (file)
@@ -114,7 +114,7 @@ extern u32 btc_dbg_type[];
 
 
 #define        CL_SPRINTF      snprintf
-#define        CL_PRINTF       printk
+#define        CL_PRINTF(buf)  printk("%s", buf)
 
 #define        BTC_PRINT(dbgtype, dbgflag, printstr, ...)              \
        do {                                                    \
index b14cf5a10f4421127e8f6ce414eee52054129163..d840ad7bdf65e82248a97190e43fce3c97d4abaf 100644 (file)
@@ -1231,7 +1231,7 @@ static int _rtl88ee_set_media_status(struct ieee80211_hw *hw,
 
        rtl_write_byte(rtlpriv, (MSR), bt_msr);
        rtlpriv->cfg->ops->led_control(hw, ledaction);
-       if ((bt_msr & 0xfc) == MSR_AP)
+       if ((bt_msr & MSR_MASK) == MSR_AP)
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
        else
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
index 7af85cfa8f8706f0c67c0bf2082d11889fa662d9..cd7e7a52713380966bc5128548ab8bf6c266233a 100644 (file)
 #define        MSR_ADHOC                               0x01
 #define        MSR_INFRA                               0x02
 #define        MSR_AP                                  0x03
+#define        MSR_MASK                                0x03
 
 #define        RRSR_RSC_OFFSET                         21
 #define        RRSR_SHORT_OFFSET                       23
index cdecb0fd4d8edb531c34cb929b8a13f568f40eed..e2736929b5d0dccbe6a7aaaa7ae3b92cc3fef24d 100644 (file)
@@ -1206,7 +1206,7 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw,
 
        rtl_write_byte(rtlpriv, (MSR), bt_msr);
        rtlpriv->cfg->ops->led_control(hw, ledaction);
-       if ((bt_msr & 0xfc) == MSR_AP)
+       if ((bt_msr & MSR_MASK) == MSR_AP)
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
        else
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
index ed703a1b3b7c1c54490efb803e804000a2d43342..dc8460c0b32f44f60b8644cb04409c7d4235926a 100644 (file)
 #define        MSR_ADHOC                               0x01
 #define        MSR_INFRA                               0x02
 #define        MSR_AP                                  0x03
+#define        MSR_MASK                                0x03
 
 #define        RRSR_RSC_OFFSET                         21
 #define        RRSR_SHORT_OFFSET                       23
index a903c2671b4d1701c0c71416748b5d4fa8ec6bf9..270cbffcac70cf02329fc1093f82e00244b78cae 100644 (file)
@@ -1360,7 +1360,7 @@ static int _rtl92cu_set_media_status(struct ieee80211_hw *hw,
        }
        rtl_write_byte(rtlpriv, (MSR), bt_msr);
        rtlpriv->cfg->ops->led_control(hw, ledaction);
-       if ((bt_msr & 0xfc) == MSR_AP)
+       if ((bt_msr & MSR_MASK) == MSR_AP)
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
        else
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
index 2b08671004a0aa88b4c6ea270ebc4e7c8368aee6..280c3da42993dbd6154ba8c5ab9a6abbb5784d2d 100644 (file)
@@ -1128,7 +1128,7 @@ static int _rtl92de_set_media_status(struct ieee80211_hw *hw,
        }
        rtl_write_byte(rtlpriv, REG_CR + 2, bt_msr);
        rtlpriv->cfg->ops->led_control(hw, ledaction);
-       if ((bt_msr & 0xfc) == MSR_AP)
+       if ((bt_msr & MSR_MASK) == MSR_AP)
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
        else
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
index 3d1f0dd4e52d89825710544078895752895b2c76..592125a5f19cbd3b43501d26345abe68c3b11fc8 100644 (file)
@@ -203,11 +203,12 @@ u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask)
        struct rtl_priv *rtlpriv = rtl_priv(hw);
        struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
        u32 returnvalue, originalvalue, bitshift;
-       u8 dbi_direct;
 
        RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x)\n",
                 regaddr, bitmask);
        if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) {
+               u8 dbi_direct = 0;
+
                /* mac1 use phy0 read radio_b. */
                /* mac0 use phy1 read radio_b. */
                if (rtlhal->during_mac1init_radioa)
index 7f29b8d765b37cf22e1a8cf362144d4229a2f583..315a298bab06a756525ef81ea14f66105e426f03 100644 (file)
 #define        MSR_ADHOC                       0x01
 #define        MSR_INFRA                       0x02
 #define        MSR_AP                          0x03
+#define        MSR_MASK                        0x03
 
 /* 6. Adaptive Control Registers  (Offset: 0x0160 - 0x01CF) */
 /* ----------------------------------------------------- */
index 87f69166a7eda86b2517feb6237da073c0ebe9da..539e53987372e0f7063291d766266e6eeee49f5c 100644 (file)
@@ -1109,7 +1109,7 @@ static int _rtl8723ae_set_media_status(struct ieee80211_hw *hw,
 
        rtl_write_byte(rtlpriv, (MSR), bt_msr);
        rtlpriv->cfg->ops->led_control(hw, ledaction);
-       if ((bt_msr & 0x03) == MSR_AP)
+       if ((bt_msr & MSR_MASK) == MSR_AP)
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
        else
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
index 64376b38708bd2e216f4b23a18da87247b4059b9..ce2c66fd9eeeaec1f943130a8190805d848d631b 100644 (file)
 #define        MSR_ADHOC                               0x01
 #define        MSR_INFRA                               0x02
 #define        MSR_AP                                  0x03
+#define        MSR_MASK                                0x03
 
 #define        RRSR_RSC_OFFSET                         21
 #define        RRSR_SHORT_OFFSET                       23
index 3d555495b45319b8d287d9edd5e1bc1c6162e625..3cd286930fe0088d73b2046a970f3349a4531a3b 100644 (file)
@@ -1197,7 +1197,7 @@ static int _rtl8723be_set_media_status(struct ieee80211_hw *hw,
        }
        rtl_write_byte(rtlpriv, (MSR), bt_msr);
        rtlpriv->cfg->ops->led_control(hw, ledaction);
-       if ((bt_msr & 0x03) == MSR_AP)
+       if ((bt_msr & MSR_MASK) == MSR_AP)
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
        else
                rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
index 4c653fab8795fd90de2381e4c48d4716be03c95d..3006849ed439bde2b40e8858c847761e67433ed0 100644 (file)
 #define        MSR_ADHOC                               0x01
 #define        MSR_INFRA                               0x02
 #define        MSR_AP                                  0x03
+#define        MSR_MASK                                0x03
 
 #define        RRSR_RSC_OFFSET                         21
 #define        RRSR_SHORT_OFFSET                       23
index 4e782f18ae3431600a66923216faa536d42c46b6..38234851457e51cd22829a1ea25c7e26cc584c21 100644 (file)
@@ -991,8 +991,9 @@ out:
 
 static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
-                            struct cfg80211_scan_request *req)
+                            struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wl1251 *wl = hw->priv;
        struct sk_buff *skb;
        size_t ssid_len = 0;
index d50dfac91631ebcbd4f685497ca237d3eb22893f..0bccf123831ec0ab80d8a8c62e99b01d7cd23c12 100644 (file)
@@ -1668,7 +1668,7 @@ static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
 {
        u8 thold;
 
-       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
+       if (test_bit(hlid, &wl->fw_fast_lnk_map))
                thold = wl->conf.tx.fast_link_thold;
        else
                thold = wl->conf.tx.slow_link_thold;
index 7541bd1a4a4b40de9b6be249dea94ca8f95e96ae..0c0d5cd98514207c25b3faf804573f13af78976e 100644 (file)
@@ -156,7 +156,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                                         cmd->params.role_id, band,
                                         wl->scan.ssid, wl->scan.ssid_len,
                                         wl->scan.req->ie,
-                                        wl->scan.req->ie_len, false);
+                                        wl->scan.req->ie_len, NULL, 0, false);
        if (ret < 0) {
                wl1271_error("PROBE request template failed");
                goto out;
@@ -317,7 +317,7 @@ static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
 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 ieee80211_scan_ies *ies)
 {
        struct wl1271_cmd_sched_scan_config *cfg = NULL;
        struct wlcore_scan_channels *cfg_channels = NULL;
@@ -378,8 +378,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                                 wlvif->role_id, band,
                                                 req->ssids[0].ssid,
                                                 req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
+                                                ies->ies[band],
+                                                ies->len[band],
+                                                ies->common_ies,
+                                                ies->common_ie_len,
+                                                true);
                if (ret < 0) {
                        wl1271_error("2.4GHz PROBE request template failed");
                        goto out;
@@ -392,8 +395,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                                 wlvif->role_id, band,
                                                 req->ssids[0].ssid,
                                                 req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
+                                                ies->ies[band],
+                                                ies->len[band],
+                                                ies->common_ies,
+                                                ies->common_ie_len,
+                                                true);
                if (ret < 0) {
                        wl1271_error("5GHz PROBE request template failed");
                        goto out;
@@ -449,7 +455,7 @@ out_free:
 
 int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
                            struct cfg80211_sched_scan_request *req,
-                           struct ieee80211_sched_scan_ies *ies)
+                           struct ieee80211_scan_ies *ies)
 {
        int ret;
 
index 264af7ac27854721c372379c4b59cd2248facac6..427f9af85a00d5f6b8d2676d102f12734ad2db13 100644 (file)
@@ -135,6 +135,6 @@ 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);
+                           struct ieee80211_scan_ies *ies);
 void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
 #endif
index 7649c75cd68dfd9d189fc724a57522fbad2b9bce..44f0b205b065efa06233387e6545c8021fcf9931 100644 (file)
@@ -78,3 +78,92 @@ out_free:
 out:
        return ret;
 }
+
+int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap)
+{
+       struct wl18xx_cmd_smart_config_start *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd smart config start group_bitmap=0x%x",
+                    group_bitmap);
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->group_id_bitmask = cpu_to_le32(group_bitmap);
+
+       ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_START, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send smart config start command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
+
+int wl18xx_cmd_smart_config_stop(struct wl1271 *wl)
+{
+       struct wl1271_cmd_header *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd smart config stop");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_STOP, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send smart config stop command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
+
+int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
+                                         u8 key_len, u8 *key)
+{
+       struct wl18xx_cmd_smart_config_set_group_key *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd smart config set group key id=0x%x",
+                    group_id);
+
+       if (key_len != sizeof(cmd->key)) {
+               wl1271_error("invalid group key size: %d", key_len);
+               return -E2BIG;
+       }
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->group_id = cpu_to_le32(group_id);
+       memcpy(cmd->key, key, key_len);
+
+       ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_SET_GROUP_KEY, cmd,
+                             sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send smart config set group key cmd");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
index 6687d10899acd4e9a130461ecdddbce07157a783..92499e2dfa83206a83c7992e24fa21157cf776aa 100644 (file)
@@ -45,8 +45,25 @@ struct wl18xx_cmd_channel_switch {
        u8 padding[2];
 } __packed;
 
+struct wl18xx_cmd_smart_config_start {
+       struct wl1271_cmd_header header;
+
+       __le32 group_id_bitmask;
+} __packed;
+
+struct wl18xx_cmd_smart_config_set_group_key {
+       struct wl1271_cmd_header header;
+
+       __le32 group_id;
+
+       u8 key[16];
+} __packed;
+
 int wl18xx_cmd_channel_switch(struct wl1271 *wl,
                              struct wl12xx_vif *wlvif,
                              struct ieee80211_channel_switch *ch_switch);
-
+int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap);
+int wl18xx_cmd_smart_config_stop(struct wl1271 *wl);
+int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
+                                         u8 key_len, u8 *key);
 #endif
index c9199d7804c634380c25bcb5c126f1ebb22f40fd..eb1848e084242f24e220a3d721b2da51df322b02 100644 (file)
  *
  */
 
+#include <net/genetlink.h>
 #include "event.h"
 #include "scan.h"
 #include "../wlcore/cmd.h"
 #include "../wlcore/debug.h"
+#include "../wlcore/vendor_cmd.h"
 
 int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
                          bool *timeout)
@@ -45,6 +47,58 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
        return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
 }
 
+static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel,
+                                         u8 sync_band)
+{
+       struct sk_buff *skb;
+       enum ieee80211_band band;
+       int freq;
+
+       if (sync_band == WLCORE_BAND_5GHZ)
+               band = IEEE80211_BAND_5GHZ;
+       else
+               band = IEEE80211_BAND_2GHZ;
+
+       freq = ieee80211_channel_to_frequency(sync_channel, band);
+
+       wl1271_debug(DEBUG_EVENT,
+                    "SMART_CONFIG_SYNC_EVENT_ID, freq: %d (chan: %d band %d)",
+                    freq, sync_channel, sync_band);
+       skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, 20,
+                                         WLCORE_VENDOR_EVENT_SC_SYNC,
+                                         GFP_KERNEL);
+
+       if (nla_put_u32(skb, WLCORE_VENDOR_ATTR_FREQ, freq)) {
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
+       cfg80211_vendor_event(skb, GFP_KERNEL);
+       return 0;
+}
+
+static int wlcore_smart_config_decode_event(struct wl1271 *wl,
+                                           u8 ssid_len, u8 *ssid,
+                                           u8 pwd_len, u8 *pwd)
+{
+       struct sk_buff *skb;
+
+       wl1271_debug(DEBUG_EVENT, "SMART_CONFIG_DECODE_EVENT_ID");
+       wl1271_dump_ascii(DEBUG_EVENT, "SSID:", ssid, ssid_len);
+
+       skb = cfg80211_vendor_event_alloc(wl->hw->wiphy,
+                                         ssid_len + pwd_len + 20,
+                                         WLCORE_VENDOR_EVENT_SC_DECODE,
+                                         GFP_KERNEL);
+
+       if (nla_put(skb, WLCORE_VENDOR_ATTR_SSID, ssid_len, ssid) ||
+           nla_put(skb, WLCORE_VENDOR_ATTR_PSK, pwd_len, pwd)) {
+               kfree_skb(skb);
+               return -EMSGSIZE;
+       }
+       cfg80211_vendor_event(skb, GFP_KERNEL);
+       return 0;
+}
+
 int wl18xx_process_mailbox_events(struct wl1271 *wl)
 {
        struct wl18xx_event_mailbox *mbox = wl->mbox;
@@ -107,5 +161,16 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl)
        if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
                wlcore_event_roc_complete(wl);
 
+       if (vector & SMART_CONFIG_SYNC_EVENT_ID)
+               wlcore_smart_config_sync_event(wl, mbox->sc_sync_channel,
+                                              mbox->sc_sync_band);
+
+       if (vector & SMART_CONFIG_DECODE_EVENT_ID)
+               wlcore_smart_config_decode_event(wl,
+                                                mbox->sc_ssid_len,
+                                                mbox->sc_ssid,
+                                                mbox->sc_pwd_len,
+                                                mbox->sc_pwd);
+
        return 0;
 }
index a76e98eb8372a9c2f7ff205e87e8a5667a48de38..0680312d49439ca1fe8ea181855c54cb656117be 100644 (file)
@@ -38,6 +38,8 @@ enum {
        REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(18),
        DFS_CHANNELS_CONFIG_COMPLETE_EVENT       = BIT(19),
        PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(20),
+       SMART_CONFIG_SYNC_EVENT_ID               = BIT(22),
+       SMART_CONFIG_DECODE_EVENT_ID             = BIT(23),
 };
 
 struct wl18xx_event_mailbox {
index de5b4fa5d1666b9a5af57b8312da487a037ebdf4..7af1936719eb879d0915fd9dda2db28ded54f39b 100644 (file)
@@ -992,7 +992,10 @@ static int wl18xx_boot(struct wl1271 *wl)
                REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
                INACTIVE_STA_EVENT_ID |
                CHANNEL_SWITCH_COMPLETE_EVENT_ID |
-               DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+               DFS_CHANNELS_CONFIG_COMPLETE_EVENT |
+               SMART_CONFIG_SYNC_EVENT_ID |
+               SMART_CONFIG_DECODE_EVENT_ID;
+;
 
        wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID;
 
@@ -1606,15 +1609,20 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
        u8 thold;
        struct wl18xx_fw_status_priv *status_priv =
                (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
-       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+       unsigned long suspend_bitmap;
+
+       /* if we don't have the link map yet, assume they all low prio */
+       if (!status_priv)
+               return false;
 
        /* suspended links are never high priority */
-       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+       suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+       if (test_bit(hlid, &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))
+       if (test_bit(hlid, &wl->fw_fast_lnk_map) &&
+           !test_bit(hlid, &wl->ap_fw_ps_map))
                thold = status_priv->tx_fast_link_prio_threshold;
        else
                thold = status_priv->tx_slow_link_prio_threshold;
@@ -1628,12 +1636,17 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        u8 thold;
        struct wl18xx_fw_status_priv *status_priv =
                (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
-       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+       unsigned long suspend_bitmap;
+
+       /* if we don't have the link map yet, assume they all low prio */
+       if (!status_priv)
+               return true;
 
-       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+       suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+       if (test_bit(hlid, &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))
+       else if (test_bit(hlid, &wl->fw_fast_lnk_map) &&
+                !test_bit(hlid, &wl->ap_fw_ps_map))
                thold = status_priv->tx_fast_stop_threshold;
        else
                thold = status_priv->tx_slow_stop_threshold;
@@ -1687,6 +1700,9 @@ static struct wlcore_ops wl18xx_ops = {
        .convert_hwaddr = wl18xx_convert_hwaddr,
        .lnk_high_prio  = wl18xx_lnk_high_prio,
        .lnk_low_prio   = wl18xx_lnk_low_prio,
+       .smart_config_start = wl18xx_cmd_smart_config_start,
+       .smart_config_stop  = wl18xx_cmd_smart_config_stop,
+       .smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
index 2b642f8c9266ef21321f8276e6ccbd9a719170af..98666f235a12d9a70873e18eaa3245298007284f 100644 (file)
@@ -113,6 +113,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                                 req->ssids ? req->ssids[0].ssid_len : 0,
                                 req->ie,
                                 req->ie_len,
+                                NULL,
+                                0,
                                 false);
                if (ret < 0) {
                        wl1271_error("2.4GHz PROBE request template failed");
@@ -128,6 +130,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                                 req->ssids ? req->ssids[0].ssid_len : 0,
                                 req->ie,
                                 req->ie_len,
+                                NULL,
+                                0,
                                 false);
                if (ret < 0) {
                        wl1271_error("5GHz PROBE request template failed");
@@ -161,7 +165,7 @@ 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 ieee80211_scan_ies *ies)
 {
        struct wl18xx_cmd_scan_params *cmd;
        struct wlcore_scan_channels *cmd_channels = NULL;
@@ -237,8 +241,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
                                 cmd->role_id, band,
                                 req->ssids ? req->ssids[0].ssid : NULL,
                                 req->ssids ? req->ssids[0].ssid_len : 0,
-                                ies->ie[band],
+                                ies->ies[band],
                                 ies->len[band],
+                                ies->common_ies,
+                                ies->common_ie_len,
                                 true);
                if (ret < 0) {
                        wl1271_error("2.4GHz PROBE request template failed");
@@ -252,8 +258,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
                                 cmd->role_id, band,
                                 req->ssids ? req->ssids[0].ssid : NULL,
                                 req->ssids ? req->ssids[0].ssid_len : 0,
-                                ies->ie[band],
+                                ies->ies[band],
                                 ies->len[band],
+                                ies->common_ies,
+                                ies->common_ie_len,
                                 true);
                if (ret < 0) {
                        wl1271_error("5GHz PROBE request template failed");
@@ -277,7 +285,7 @@ out:
 
 int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                            struct cfg80211_sched_scan_request *req,
-                           struct ieee80211_sched_scan_ies *ies)
+                           struct ieee80211_scan_ies *ies)
 {
        return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
 }
index eadee42689d1802ba6eba714ca798703f2abe843..2e636aa5dba9bac8d11ff63d5fe02b7efc19af9c 100644 (file)
@@ -122,6 +122,6 @@ 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);
+                           struct ieee80211_scan_ies *ies);
 void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 #endif
index be1ebd55ac88e8f7f04be16e6e7bb02436468cf6..3406ffb53325c16ed47271feb470da95f0f0965e 100644 (file)
@@ -30,7 +30,7 @@
 
 static
 void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
-                            struct ieee80211_tx_rate *rate)
+                            u8 band, struct ieee80211_tx_rate *rate)
 {
        u8 fw_rate = wl->fw_status->counters.tx_last_rate;
 
@@ -43,6 +43,8 @@ void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
 
        if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) {
                rate->idx = fw_rate;
+               if (band == IEEE80211_BAND_5GHZ)
+                       rate->idx -= CONF_HW_RATE_INDEX_6MBPS;
                rate->flags = 0;
        } else {
                rate->flags = IEEE80211_TX_RC_MCS;
@@ -102,7 +104,8 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
         * 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]);
+       wl18xx_get_last_tx_rate(wl, info->control.vif,
+                               info->band, &info->status.rates[0]);
 
        info->status.rates[0].count = 1; /* no data about retries */
        info->status.ack_signal = -1;
index eb7cfe8170104ab5eb273c84f2c49209a0d9e1a3..6a2b88030c1d37bdd0c6aa768e273a6fabff99b5 100644 (file)
@@ -38,7 +38,7 @@
 #define WL18XX_NUM_TX_DESCRIPTORS 32
 #define WL18XX_NUM_RX_DESCRIPTORS 32
 
-#define WL18XX_NUM_MAC_ADDRESSES 3
+#define WL18XX_NUM_MAC_ADDRESSES 2
 
 #define WL18XX_RX_BA_MAX_SESSIONS 13
 
index 4f23931d7bd56237f9e1fc623e784fa059b2340e..0a69c1373643b07fffd482f6a011724d91d9e760 100644 (file)
@@ -1,5 +1,5 @@
 wlcore-objs            = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \
-                         boot.o init.o debugfs.o scan.o sysfs.o
+                         boot.o init.o debugfs.o scan.o sysfs.o vendor_cmd.o
 
 wlcore_spi-objs        = spi.o
 wlcore_sdio-objs       = sdio.o
index 40dc30f4faaab2b2be20c724abe54c7807f0871a..05604ee312249cb9f493b270106be2d7ba38913b 100644 (file)
@@ -372,9 +372,9 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        wl1271_tx_reset_link_queues(wl, *hlid);
        wl->links[*hlid].wlvif = NULL;
 
-       if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
-           (wlvif->bss_type == BSS_TYPE_AP_BSS &&
-            *hlid == wlvif->ap.bcast_hlid)) {
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
+           *hlid == wlvif->ap.bcast_hlid) {
+               u32 sqn_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING;
                /*
                 * save the total freed packets in the wlvif, in case this is
                 * recovery or suspend
@@ -385,9 +385,11 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
                 * increment the initial seq number on recovery to account for
                 * transmitted packets that we haven't yet got in the FW status
                 */
+               if (wlvif->encryption_type == KEY_GEM)
+                       sqn_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM;
+
                if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
-                       wlvif->total_freed_pkts +=
-                                       WL1271_TX_SQN_POST_RECOVERY_PADDING;
+                       wlvif->total_freed_pkts += sqn_padding;
        }
 
        wl->links[*hlid].total_freed_pkts = 0;
@@ -1124,7 +1126,8 @@ out:
 int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               u8 role_id, u8 band,
                               const u8 *ssid, size_t ssid_len,
-                              const u8 *ie, size_t ie_len, bool sched_scan)
+                              const u8 *ie0, size_t ie0_len, const u8 *ie1,
+                              size_t ie1_len, bool sched_scan)
 {
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct sk_buff *skb;
@@ -1136,13 +1139,15 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        wl1271_debug(DEBUG_SCAN, "build probe request band %d", band);
 
        skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
-                                    ie_len);
+                                    ie0_len + ie1_len);
        if (!skb) {
                ret = -ENOMEM;
                goto out;
        }
-       if (ie_len)
-               memcpy(skb_put(skb, ie_len), ie, ie_len);
+       if (ie0_len)
+               memcpy(skb_put(skb, ie0_len), ie0, ie0_len);
+       if (ie1_len)
+               memcpy(skb_put(skb, ie1_len), ie1, ie1_len);
 
        if (sched_scan &&
            (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
index b084830a61cf51adbe40b8461fc28510dd13bd5a..ca6a28b03f8f31b5b47b23b8db982df4dbf78f84 100644 (file)
@@ -64,7 +64,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               u8 role_id, u8 band,
                               const u8 *ssid, size_t ssid_len,
-                              const u8 *ie, size_t ie_len, bool sched_scan);
+                              const u8 *ie, size_t ie_len, const u8 *common_ie,
+                              size_t common_ie_len, bool sched_scan);
 struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
                                              struct wl12xx_vif *wlvif,
                                              struct sk_buff *skb);
@@ -169,6 +170,9 @@ enum wl1271_commands {
 
        /* start of 18xx specific commands */
        CMD_DFS_CHANNEL_CONFIG          = 60,
+       CMD_SMART_CONFIG_START          = 61,
+       CMD_SMART_CONFIG_STOP           = 62,
+       CMD_SMART_CONFIG_SET_GROUP_KEY  = 63,
 
        MAX_COMMAND_ID = 0xFFFF,
 };
index 89893c7170253c8cc5400bf7941cc9ce4a3d3051..0be21f62fcb0eb3a58e7c16886efa8c2bde096cc 100644 (file)
@@ -496,7 +496,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_INT(sg_enabled);
        DRIVER_STATE_PRINT_INT(enable_11a);
        DRIVER_STATE_PRINT_INT(noise);
-       DRIVER_STATE_PRINT_HEX(ap_fw_ps_map);
+       DRIVER_STATE_PRINT_LHEX(ap_fw_ps_map);
        DRIVER_STATE_PRINT_LHEX(ap_ps_map);
        DRIVER_STATE_PRINT_HEX(quirks);
        DRIVER_STATE_PRINT_HEX(irq);
index 1555ff9700509186e43996ffb4a7c5bbe106522f..aa9f82c7229673a6c509a7a9fef38287675f083a 100644 (file)
@@ -260,4 +260,31 @@ wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid,
        return wl->ops->lnk_low_prio(wl, hlid, lnk);
 }
 
+static inline int
+wlcore_smart_config_start(struct wl1271 *wl, u32 group_bitmap)
+{
+       if (!wl->ops->smart_config_start)
+               return -EINVAL;
+
+       return wl->ops->smart_config_start(wl, group_bitmap);
+}
+
+static inline int
+wlcore_smart_config_stop(struct wl1271 *wl)
+{
+       if (!wl->ops->smart_config_stop)
+               return -EINVAL;
+
+       return wl->ops->smart_config_stop(wl);
+}
+
+static inline int
+wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
+                                 u8 key_len, u8 *key)
+{
+       if (!wl->ops->smart_config_set_group_key)
+               return -EINVAL;
+
+       return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key);
+}
 #endif
index 3d6028e62750279299431ce56315bdf67fa1f8cc..575c8f6d4009477dc6430b7eff91ab6d8a60e080 100644 (file)
@@ -37,6 +37,7 @@
 #include "init.h"
 #include "debugfs.h"
 #include "testmode.h"
+#include "vendor_cmd.h"
 #include "scan.h"
 #include "hw_ops.h"
 #include "sysfs.h"
@@ -332,7 +333,7 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
 {
        bool fw_ps;
 
-       fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+       fw_ps = test_bit(hlid, &wl->ap_fw_ps_map);
 
        /*
         * Wake up from high level PS if the STA is asleep with too little
@@ -359,13 +360,13 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
                                           struct wl_fw_status *status)
 {
-       u32 cur_fw_ps_map;
+       unsigned long cur_fw_ps_map;
        u8 hlid;
 
        cur_fw_ps_map = status->link_ps_bitmap;
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
                wl1271_debug(DEBUG_PSM,
-                            "link ps prev 0x%x cur 0x%x changed 0x%x",
+                            "link ps prev 0x%lx cur 0x%lx changed 0x%lx",
                             wl->ap_fw_ps_map, cur_fw_ps_map,
                             wl->ap_fw_ps_map ^ cur_fw_ps_map);
 
@@ -898,6 +899,44 @@ out:
        wlcore_set_partition(wl, &old_part);
 }
 
+static void wlcore_save_freed_pkts(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                  u8 hlid, struct ieee80211_sta *sta)
+{
+       struct wl1271_station *wl_sta;
+       u32 sqn_recovery_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING;
+
+       wl_sta = (void *)sta->drv_priv;
+       wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
+
+       /*
+        * increment the initial seq number on recovery to account for
+        * transmitted packets that we haven't yet got in the FW status
+        */
+       if (wlvif->encryption_type == KEY_GEM)
+               sqn_recovery_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM;
+
+       if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
+               wl_sta->total_freed_pkts += sqn_recovery_padding;
+}
+
+static void wlcore_save_freed_pkts_addr(struct wl1271 *wl,
+                                       struct wl12xx_vif *wlvif,
+                                       u8 hlid, const u8 *addr)
+{
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
+       if (WARN_ON(hlid == WL12XX_INVALID_LINK_ID ||
+                   is_zero_ether_addr(addr)))
+               return;
+
+       rcu_read_lock();
+       sta = ieee80211_find_sta(vif, addr);
+       if (sta)
+               wlcore_save_freed_pkts(wl, wlvif, hlid, sta);
+       rcu_read_unlock();
+}
+
 static void wlcore_print_recovery(struct wl1271 *wl)
 {
        u32 pc = 0;
@@ -961,6 +1000,13 @@ static void wl1271_recovery_work(struct work_struct *work)
                wlvif = list_first_entry(&wl->wlvif_list,
                                       struct wl12xx_vif, list);
                vif = wl12xx_wlvif_to_vif(wlvif);
+
+               if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+                   test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
+                       wlcore_save_freed_pkts_addr(wl, wlvif, wlvif->sta.hlid,
+                                                   vif->bss_conf.bssid);
+               }
+
                __wl1271_op_remove_interface(wl, vif, false);
        }
 
@@ -3540,8 +3586,9 @@ out:
 
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
-                            struct cfg80211_scan_request *req)
+                            struct ieee80211_scan_request *hw_req)
 {
+       struct cfg80211_scan_request *req = &hw_req->req;
        struct wl1271 *wl = hw->priv;
        int ret;
        u8 *ssid = NULL;
@@ -3636,7 +3683,7 @@ out:
 static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
                                      struct ieee80211_vif *vif,
                                      struct cfg80211_sched_scan_request *req,
-                                     struct ieee80211_sched_scan_ies *ies)
+                                     struct ieee80211_scan_ies *ies)
 {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
@@ -4702,36 +4749,18 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
 
 void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
 {
-       struct wl1271_station *wl_sta;
-       struct ieee80211_sta *sta;
-       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
-
        if (!test_bit(hlid, wlvif->ap.sta_hlid_map))
                return;
 
        clear_bit(hlid, wlvif->ap.sta_hlid_map);
        __clear_bit(hlid, &wl->ap_ps_map);
-       __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+       __clear_bit(hlid, &wl->ap_fw_ps_map);
 
        /*
         * save the last used PN in the private part of iee80211_sta,
         * in case of recovery/suspend
         */
-       rcu_read_lock();
-       sta = ieee80211_find_sta(vif, wl->links[hlid].addr);
-       if (sta) {
-               wl_sta = (void *)sta->drv_priv;
-               wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts;
-
-               /*
-                * increment the initial seq number on recovery to account for
-                * transmitted packets that we haven't yet got in the FW status
-                */
-               if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
-                       wl_sta->total_freed_pkts +=
-                                       WL1271_TX_SQN_POST_RECOVERY_PADDING;
-       }
-       rcu_read_unlock();
+       wlcore_save_freed_pkts_addr(wl, wlvif, hlid, wl->links[hlid].addr);
 
        wl12xx_free_link(wl, wlvif, &hlid);
        wl->active_sta_count--;
@@ -4914,6 +4943,21 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
                clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
        }
 
+       /* save seq number on disassoc (suspend) */
+       if (is_sta &&
+           old_state == IEEE80211_STA_ASSOC &&
+           new_state == IEEE80211_STA_AUTH) {
+               wlcore_save_freed_pkts(wl, wlvif, wlvif->sta.hlid, sta);
+               wlvif->total_freed_pkts = 0;
+       }
+
+       /* restore seq number on assoc (resume) */
+       if (is_sta &&
+           old_state == IEEE80211_STA_AUTH &&
+           new_state == IEEE80211_STA_ASSOC) {
+               wlvif->total_freed_pkts = wl_sta->total_freed_pkts;
+       }
+
        /* clear ROCs on failure or authorization */
        if (is_sta &&
            (new_state == IEEE80211_STA_AUTHORIZED ||
@@ -5148,6 +5192,10 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
        if (unlikely(wl->state == WLCORE_STATE_OFF)) {
                wl12xx_for_each_wlvif_sta(wl, wlvif) {
                        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
+                       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                               continue;
+
                        ieee80211_chswitch_done(vif, false);
                }
                goto out;
@@ -5163,6 +5211,9 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
        wl12xx_for_each_wlvif_sta(wl, wlvif) {
                unsigned long delay_usec;
 
+               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+                       continue;
+
                ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
                if (ret)
                        goto out_sleep;
@@ -5618,7 +5669,7 @@ static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
                memcpy(&wl->addresses[idx], &wl->addresses[0],
                       sizeof(wl->addresses[0]));
                /* LAA bit */
-               wl->addresses[idx].addr[2] |= BIT(1);
+               wl->addresses[idx].addr[0] |= BIT(1);
        }
 
        wl->hw->wiphy->n_addresses = WLCORE_NUM_MAC_ADDRESSES;
@@ -5763,7 +5814,7 @@ 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->max_remain_on_channel_duration = 30000;
 
        wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
                                WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
@@ -5832,6 +5883,9 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->iface_combinations = wl->iface_combinations;
        wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations;
 
+       /* register vendor commands */
+       wlcore_set_vendor_commands(wl->hw->wiphy);
+
        SET_IEEE80211_DEV(wl->hw, wl->dev);
 
        wl->hw->sta_data_size = sizeof(struct wl1271_station);
index a6ab24b5c0f96ab86278f71b93aae80a11048433..4dadd0c62cde5251f83d924e20b1c5312e3d2806 100644 (file)
@@ -37,7 +37,7 @@ 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);
+                                    struct ieee80211_scan_ies *ies);
 int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wlcore_scan_sched_scan_results(struct wl1271 *wl);
 
index 40b43115f83590b6a6cb4a38d8283e7b78460187..f0ac36139bcc1419a3963a5048e8c3013fce5cd4 100644 (file)
@@ -126,7 +126,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
        if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
                return;
 
-       fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+       fw_ps = test_bit(hlid, &wl->ap_fw_ps_map);
        tx_pkts = wl->links[hlid].allocated_pkts;
 
        /*
diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
new file mode 100644 (file)
index 0000000..ad86a48
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2014 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.
+ */
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+#include "wlcore.h"
+#include "debug.h"
+#include "ps.h"
+#include "hw_ops.h"
+#include "vendor_cmd.h"
+
+static const
+struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = {
+       [WLCORE_VENDOR_ATTR_FREQ]               = { .type = NLA_U32 },
+       [WLCORE_VENDOR_ATTR_GROUP_ID]           = { .type = NLA_U32 },
+       [WLCORE_VENDOR_ATTR_GROUP_KEY]          = { .type = NLA_U32,
+                                                   .len = WLAN_MAX_KEY_LEN },
+};
+
+static int
+wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
+                                    struct wireless_dev *wdev,
+                                    const void *data, int data_len)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
+       struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "vendor cmd smart config start");
+
+       if (!data)
+               return -EINVAL;
+
+       ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
+                       wlcore_vendor_attr_policy);
+       if (ret)
+               return ret;
+
+       if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID])
+               return -EINVAL;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_smart_config_start(wl,
+                       nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return 0;
+}
+
+static int
+wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
+                                   struct wireless_dev *wdev,
+                                   const void *data, int data_len)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop");
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_smart_config_stop(wl);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
+static int
+wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
+                                            struct wireless_dev *wdev,
+                                            const void *data, int data_len)
+{
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
+       struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "testmode cmd smart config set group key");
+
+       if (!data)
+               return -EINVAL;
+
+       ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
+                       wlcore_vendor_attr_policy);
+       if (ret)
+               return ret;
+
+       if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID] ||
+           !tb[WLCORE_VENDOR_ATTR_GROUP_KEY])
+               return -EINVAL;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_smart_config_set_group_key(wl,
+                       nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]),
+                       nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
+                       nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
+static const struct wiphy_vendor_command wlcore_vendor_commands[] = {
+       {
+               .info = {
+                       .vendor_id = TI_OUI,
+                       .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START,
+               },
+               .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+                        WIPHY_VENDOR_CMD_NEED_RUNNING,
+               .doit = wlcore_vendor_cmd_smart_config_start,
+       },
+       {
+               .info = {
+                       .vendor_id = TI_OUI,
+                       .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
+               },
+               .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+                        WIPHY_VENDOR_CMD_NEED_RUNNING,
+               .doit = wlcore_vendor_cmd_smart_config_stop,
+       },
+       {
+               .info = {
+                       .vendor_id = TI_OUI,
+                       .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
+               },
+               .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
+                        WIPHY_VENDOR_CMD_NEED_RUNNING,
+               .doit = wlcore_vendor_cmd_smart_config_set_group_key,
+       },
+};
+
+static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = {
+       {
+               .vendor_id = TI_OUI,
+               .subcmd = WLCORE_VENDOR_EVENT_SC_SYNC,
+       },
+       {
+               .vendor_id = TI_OUI,
+               .subcmd = WLCORE_VENDOR_EVENT_SC_DECODE,
+       },
+};
+
+void wlcore_set_vendor_commands(struct wiphy *wiphy)
+{
+       wiphy->vendor_commands = wlcore_vendor_commands;
+       wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands);
+       wiphy->vendor_events = wlcore_vendor_events;
+       wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events);
+}
diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.h b/drivers/net/wireless/ti/wlcore/vendor_cmd.h
new file mode 100644 (file)
index 0000000..6e0c15e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __WLCORE_VENDOR_H__
+#define __WLCORE_VENDOR_H__
+
+#ifdef __KERNEL__
+void wlcore_set_vendor_commands(struct wiphy *wiphy);
+#endif
+
+#define TI_OUI 0x080028
+
+enum wlcore_vendor_commands {
+       WLCORE_VENDOR_CMD_SMART_CONFIG_START,
+       WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
+       WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
+
+       NUM_WLCORE_VENDOR_CMD,
+       MAX_WLCORE_VENDOR_CMD = NUM_WLCORE_VENDOR_CMD - 1
+};
+
+enum wlcore_vendor_attributes {
+       WLCORE_VENDOR_ATTR_FREQ,
+       WLCORE_VENDOR_ATTR_PSK,
+       WLCORE_VENDOR_ATTR_SSID,
+       WLCORE_VENDOR_ATTR_GROUP_ID,
+       WLCORE_VENDOR_ATTR_GROUP_KEY,
+
+       NUM_WLCORE_VENDOR_ATTR,
+       MAX_WLCORE_VENDOR_ATTR = NUM_WLCORE_VENDOR_ATTR - 1
+};
+
+enum wlcore_vendor_events {
+       WLCORE_VENDOR_EVENT_SC_SYNC,
+       WLCORE_VENDOR_EVENT_SC_DECODE,
+};
+
+#endif /* __WLCORE_VENDOR_H__ */
index 95a54504f0cc3815831d212906002faf6e0e8904..df78cf12ef1574b475751aca21ab5995c397af95 100644 (file)
@@ -95,7 +95,7 @@ struct wlcore_ops {
        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);
+                               struct ieee80211_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,
@@ -117,6 +117,10 @@ struct wlcore_ops {
                              struct wl1271_link *lnk);
        bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
                             struct wl1271_link *lnk);
+       int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap);
+       int (*smart_config_stop)(struct wl1271 *wl);
+       int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id,
+                                         u8 key_len, u8 *key);
 };
 
 enum wlcore_partitions {
@@ -384,10 +388,10 @@ struct wl1271 {
        int active_link_count;
 
        /* Fast/slow links bitmap according to FW */
-       u32 fw_fast_lnk_map;
+       unsigned long fw_fast_lnk_map;
 
        /* AP-mode - a bitmap of links currently in PS mode according to FW */
-       u32 ap_fw_ps_map;
+       unsigned long ap_fw_ps_map;
 
        /* AP-mode - a bitmap of links currently in PS mode in mac80211 */
        unsigned long ap_ps_map;
index c2c34a84ff3d4bf7231f84933564b0e78dcc415b..0e52556044d9c0621c4c15b44e3fc6a7c709f579 100644 (file)
@@ -45,6 +45,9 @@
 #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
 #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff))
 #define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff
+/* Use smaller padding for GEM, as some  APs have issues when it's too big */
+#define WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM 0x20
+
 
 #define WL1271_CIPHER_SUITE_GEM 0x00147201
 
@@ -324,6 +327,7 @@ struct wl1271_station {
         * total freed FW packets on the link to the STA - used for tracking the
         * AES/TKIP PN across recoveries. Re-initialized each time from the
         * wl1271_station structure.
+        * Used in both AP and STA mode.
         */
        u64 total_freed_pkts;
 };
@@ -459,6 +463,13 @@ struct wl12xx_vif {
        /* work for canceling ROC after pending auth reply */
        struct delayed_work pending_auth_complete_work;
 
+       /*
+        * total freed FW packets on the link.
+        * For STA this holds the PN of the link to the AP.
+        * For AP this holds the PN of the broadcast link.
+        */
+       u64 total_freed_pkts;
+
        /*
         * This struct must be last!
         * data that has to be saved acrossed reconfigs (e.g. recovery)
@@ -466,15 +477,6 @@ struct wl12xx_vif {
         */
        struct {
                u8 persistent[0];
-
-               /*
-                * total freed FW packets on the link - used for
-                * storing the AES/TKIP PN during recovery, as this
-                * structure is not zeroed out.
-                * For STA this holds the PN of the link to the AP.
-                * For AP this holds the PN of the broadcast link.
-                */
-               u64 total_freed_pkts;
        };
 };
 
index 96c8e1de0879cf4f78952d1487c9dac2137e4572..95920581860afc19f38383d3880bd81f5e45f1e7 100644 (file)
@@ -3,11 +3,11 @@ config ZD1211RW
        depends on USB && MAC80211
        select FW_LOADER
        ---help---
-         This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless
+         This is a driver for the ZyDAS ZD1211/ZD1211B wireless
          chip, present in many USB-wireless adapters.
 
          Device firmware is required alongside this driver. You can download
-         the firmware distribution from http://zd1211.ath.cx/get-firmware
+         the firmware distribution from http://sf.net/projects/zd1211/files/
 
 config ZD1211RW_DEBUG
        bool "ZyDAS ZD1211 debugging"
index a8dc95ebf2d605a8a774c6b05b9287dfaa3b7846..0f28c08fcb3c76f4a2deaf3cd084a39f800db142 100644 (file)
@@ -326,13 +326,13 @@ err_ctlreg:
        return err;
 }
 
-static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in,
-                              u16 mask, u16 shift)
+static s8 sprom_extract_antgain(u8 sprom_revision, const u16 *in, u16 offset,
+                               u16 mask, u16 shift)
 {
        u16 v;
        u8 gain;
 
-       v = in[SPOFF(SSB_SPROM1_AGAIN)];
+       v = in[SPOFF(offset)];
        gain = (v & mask) >> shift;
        if (gain == 0xFF)
                gain = 2; /* If unset use 2dBm */
@@ -416,12 +416,14 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
        SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0);
 
        /* Extract the antenna gain values. */
-       out->antenna_gain.a0 = r123_extract_antgain(out->revision, in,
-                                                   SSB_SPROM1_AGAIN_BG,
-                                                   SSB_SPROM1_AGAIN_BG_SHIFT);
-       out->antenna_gain.a1 = r123_extract_antgain(out->revision, in,
-                                                   SSB_SPROM1_AGAIN_A,
-                                                   SSB_SPROM1_AGAIN_A_SHIFT);
+       out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM1_AGAIN,
+                                                    SSB_SPROM1_AGAIN_BG,
+                                                    SSB_SPROM1_AGAIN_BG_SHIFT);
+       out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM1_AGAIN,
+                                                    SSB_SPROM1_AGAIN_A,
+                                                    SSB_SPROM1_AGAIN_A_SHIFT);
        if (out->revision >= 2)
                sprom_extract_r23(out, in);
 }
@@ -468,7 +470,15 @@ static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in)
 
 static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
 {
+       static const u16 pwr_info_offset[] = {
+               SSB_SPROM4_PWR_INFO_CORE0, SSB_SPROM4_PWR_INFO_CORE1,
+               SSB_SPROM4_PWR_INFO_CORE2, SSB_SPROM4_PWR_INFO_CORE3
+       };
        u16 il0mac_offset;
+       int i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
+                    ARRAY_SIZE(out->core_pwr_info));
 
        if (out->revision == 4)
                il0mac_offset = SSB_SPROM4_IL0MAC;
@@ -524,14 +534,59 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
        }
 
        /* Extract the antenna gain values. */
-       SPEX(antenna_gain.a0, SSB_SPROM4_AGAIN01,
-            SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT);
-       SPEX(antenna_gain.a1, SSB_SPROM4_AGAIN01,
-            SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT);
-       SPEX(antenna_gain.a2, SSB_SPROM4_AGAIN23,
-            SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT);
-       SPEX(antenna_gain.a3, SSB_SPROM4_AGAIN23,
-            SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT);
+       out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM4_AGAIN01,
+                                                    SSB_SPROM4_AGAIN0,
+                                                    SSB_SPROM4_AGAIN0_SHIFT);
+       out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM4_AGAIN01,
+                                                    SSB_SPROM4_AGAIN1,
+                                                    SSB_SPROM4_AGAIN1_SHIFT);
+       out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM4_AGAIN23,
+                                                    SSB_SPROM4_AGAIN2,
+                                                    SSB_SPROM4_AGAIN2_SHIFT);
+       out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM4_AGAIN23,
+                                                    SSB_SPROM4_AGAIN3,
+                                                    SSB_SPROM4_AGAIN3_SHIFT);
+
+       /* Extract cores power info info */
+       for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
+               u16 o = pwr_info_offset[i];
+
+               SPEX(core_pwr_info[i].itssi_2g, o + SSB_SPROM4_2G_MAXP_ITSSI,
+                       SSB_SPROM4_2G_ITSSI, SSB_SPROM4_2G_ITSSI_SHIFT);
+               SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SPROM4_2G_MAXP_ITSSI,
+                       SSB_SPROM4_2G_MAXP, 0);
+
+               SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SPROM4_2G_PA_0, ~0, 0);
+               SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SPROM4_2G_PA_1, ~0, 0);
+               SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SPROM4_2G_PA_2, ~0, 0);
+               SPEX(core_pwr_info[i].pa_2g[3], o + SSB_SPROM4_2G_PA_3, ~0, 0);
+
+               SPEX(core_pwr_info[i].itssi_5g, o + SSB_SPROM4_5G_MAXP_ITSSI,
+                       SSB_SPROM4_5G_ITSSI, SSB_SPROM4_5G_ITSSI_SHIFT);
+               SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SPROM4_5G_MAXP_ITSSI,
+                       SSB_SPROM4_5G_MAXP, 0);
+               SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM4_5GHL_MAXP,
+                       SSB_SPROM4_5GH_MAXP, 0);
+               SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM4_5GHL_MAXP,
+                       SSB_SPROM4_5GL_MAXP, SSB_SPROM4_5GL_MAXP_SHIFT);
+
+               SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SPROM4_5GL_PA_0, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SPROM4_5GL_PA_1, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SPROM4_5GL_PA_2, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gl[3], o + SSB_SPROM4_5GL_PA_3, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SPROM4_5G_PA_0, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SPROM4_5G_PA_1, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SPROM4_5G_PA_2, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5g[3], o + SSB_SPROM4_5G_PA_3, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SPROM4_5GH_PA_0, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SPROM4_5GH_PA_1, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SPROM4_5GH_PA_2, ~0, 0);
+               SPEX(core_pwr_info[i].pa_5gh[3], o + SSB_SPROM4_5GH_PA_3, ~0, 0);
+       }
 
        sprom_extract_r458(out, in);
 
@@ -621,14 +676,22 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
        SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
 
        /* Extract the antenna gain values. */
-       SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01,
-            SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
-       SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01,
-            SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
-       SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23,
-            SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
-       SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23,
-            SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
+       out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM8_AGAIN01,
+                                                    SSB_SPROM8_AGAIN0,
+                                                    SSB_SPROM8_AGAIN0_SHIFT);
+       out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM8_AGAIN01,
+                                                    SSB_SPROM8_AGAIN1,
+                                                    SSB_SPROM8_AGAIN1_SHIFT);
+       out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM8_AGAIN23,
+                                                    SSB_SPROM8_AGAIN2,
+                                                    SSB_SPROM8_AGAIN2_SHIFT);
+       out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in,
+                                                    SSB_SPROM8_AGAIN23,
+                                                    SSB_SPROM8_AGAIN3,
+                                                    SSB_SPROM8_AGAIN3_SHIFT);
 
        /* Extract cores power info info */
        for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
index e82289047272d6e636583e20969f59450987da05..afec6450450ff08e6be4c1cd7a05dbc293ef0a76 100644 (file)
@@ -59,7 +59,7 @@
 #include <linux/gfp.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/hci.h>
+#include <net/bluetooth/hci_sock.h>
 #include <net/bluetooth/rfcomm.h>
 
 #include <linux/capi.h>
index 0b3bb16c705a2eadc841d56bf177a900e7a8166e..969af0f2bdf92cdaf14d5e040a6b76f70dd32984 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/bcma/bcma_driver_chipcommon.h>
 #include <linux/bcma/bcma_driver_pci.h>
+#include <linux/bcma/bcma_driver_pcie2.h>
 #include <linux/bcma/bcma_driver_mips.h>
 #include <linux/bcma/bcma_driver_gmac_cmn.h>
 #include <linux/ssb/ssb.h> /* SPROM sharing */
@@ -157,6 +158,8 @@ struct bcma_host_ops {
 /* Chip IDs of PCIe devices */
 #define BCMA_CHIP_ID_BCM4313   0x4313
 #define BCMA_CHIP_ID_BCM43142  43142
+#define BCMA_CHIP_ID_BCM43217  43217
+#define BCMA_CHIP_ID_BCM43222  43222
 #define BCMA_CHIP_ID_BCM43224  43224
 #define  BCMA_PKG_ID_BCM43224_FAB_CSM  0x8
 #define  BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa
@@ -333,6 +336,7 @@ struct bcma_bus {
 
        struct bcma_drv_cc drv_cc;
        struct bcma_drv_pci drv_pci[2];
+       struct bcma_drv_pcie2 drv_pcie2;
        struct bcma_drv_mips drv_mips;
        struct bcma_drv_gmac_cmn drv_gmac_cmn;
 
diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h
new file mode 100644 (file)
index 0000000..5988b05
--- /dev/null
@@ -0,0 +1,158 @@
+#ifndef LINUX_BCMA_DRIVER_PCIE2_H_
+#define LINUX_BCMA_DRIVER_PCIE2_H_
+
+#define BCMA_CORE_PCIE2_CLK_CONTROL            0x0000
+#define  PCIE2_CLKC_RST_OE                     0x0001 /* When set, drives PCI_RESET out to pin */
+#define  PCIE2_CLKC_RST                                0x0002 /* Value driven out to pin */
+#define  PCIE2_CLKC_SPERST                     0x0004 /* SurvivePeRst */
+#define  PCIE2_CLKC_DISABLE_L1CLK_GATING       0x0010
+#define  PCIE2_CLKC_DLYPERST                   0x0100 /* Delay PeRst to CoE Core */
+#define  PCIE2_CLKC_DISSPROMLD                 0x0200 /* DisableSpromLoadOnPerst */
+#define  PCIE2_CLKC_WAKE_MODE_L2               0x1000 /* Wake on L2 */
+#define BCMA_CORE_PCIE2_RC_PM_CONTROL          0x0004
+#define BCMA_CORE_PCIE2_RC_PM_STATUS           0x0008
+#define BCMA_CORE_PCIE2_EP_PM_CONTROL          0x000C
+#define BCMA_CORE_PCIE2_EP_PM_STATUS           0x0010
+#define BCMA_CORE_PCIE2_EP_LTR_CONTROL         0x0014
+#define BCMA_CORE_PCIE2_EP_LTR_STATUS          0x0018
+#define BCMA_CORE_PCIE2_EP_OBFF_STATUS         0x001C
+#define BCMA_CORE_PCIE2_PCIE_ERR_STATUS                0x0020
+#define BCMA_CORE_PCIE2_RC_AXI_CONFIG          0x0100
+#define BCMA_CORE_PCIE2_EP_AXI_CONFIG          0x0104
+#define BCMA_CORE_PCIE2_RXDEBUG_STATUS0                0x0108
+#define BCMA_CORE_PCIE2_RXDEBUG_CONTROL0       0x010C
+#define BCMA_CORE_PCIE2_CONFIGINDADDR          0x0120
+#define BCMA_CORE_PCIE2_CONFIGINDDATA          0x0124
+#define BCMA_CORE_PCIE2_MDIOCONTROL            0x0128
+#define BCMA_CORE_PCIE2_MDIOWRDATA             0x012C
+#define BCMA_CORE_PCIE2_MDIORDDATA             0x0130
+#define BCMA_CORE_PCIE2_DATAINTF               0x0180
+#define BCMA_CORE_PCIE2_D2H_INTRLAZY_0         0x0188
+#define BCMA_CORE_PCIE2_H2D_INTRLAZY_0         0x018c
+#define BCMA_CORE_PCIE2_H2D_INTSTAT_0          0x0190
+#define BCMA_CORE_PCIE2_H2D_INTMASK_0          0x0194
+#define BCMA_CORE_PCIE2_D2H_INTSTAT_0          0x0198
+#define BCMA_CORE_PCIE2_D2H_INTMASK_0          0x019c
+#define BCMA_CORE_PCIE2_LTR_STATE              0x01A0 /* Latency Tolerance Reporting */
+#define  PCIE2_LTR_ACTIVE                      2
+#define  PCIE2_LTR_ACTIVE_IDLE                 1
+#define  PCIE2_LTR_SLEEP                       0
+#define  PCIE2_LTR_FINAL_MASK                  0x300
+#define  PCIE2_LTR_FINAL_SHIFT                 8
+#define BCMA_CORE_PCIE2_PWR_INT_STATUS         0x01A4
+#define BCMA_CORE_PCIE2_PWR_INT_MASK           0x01A8
+#define BCMA_CORE_PCIE2_CFG_ADDR               0x01F8
+#define BCMA_CORE_PCIE2_CFG_DATA               0x01FC
+#define BCMA_CORE_PCIE2_SYS_EQ_PAGE            0x0200
+#define BCMA_CORE_PCIE2_SYS_MSI_PAGE           0x0204
+#define BCMA_CORE_PCIE2_SYS_MSI_INTREN         0x0208
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL0          0x0210
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL1          0x0214
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL2          0x0218
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL3          0x021C
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL4          0x0220
+#define BCMA_CORE_PCIE2_SYS_MSI_CTRL5          0x0224
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD0           0x0250
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL0           0x0254
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD1           0x0258
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL1           0x025C
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD2           0x0260
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL2           0x0264
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD3           0x0268
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL3           0x026C
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD4           0x0270
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL4           0x0274
+#define BCMA_CORE_PCIE2_SYS_EQ_HEAD5           0x0278
+#define BCMA_CORE_PCIE2_SYS_EQ_TAIL5           0x027C
+#define BCMA_CORE_PCIE2_SYS_RC_INTX_EN         0x0330
+#define BCMA_CORE_PCIE2_SYS_RC_INTX_CSR                0x0334
+#define BCMA_CORE_PCIE2_SYS_MSI_REQ            0x0340
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR_EN       0x0344
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR_CSR      0x0348
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR0         0x0350
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR1         0x0354
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR2         0x0358
+#define BCMA_CORE_PCIE2_SYS_HOST_INTR3         0x035C
+#define BCMA_CORE_PCIE2_SYS_EP_INT_EN0         0x0360
+#define BCMA_CORE_PCIE2_SYS_EP_INT_EN1         0x0364
+#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR0                0x0370
+#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR1                0x0374
+#define BCMA_CORE_PCIE2_SPROM(wordoffset)      (0x0800 + ((wordoffset) * 2))
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_0          0x0C00
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_1          0x0C04
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_2          0x0C08
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_3          0x0C0C
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_4          0x0C10
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_5          0x0C14
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_6          0x0C18
+#define BCMA_CORE_PCIE2_FUNC0_IMAP0_7          0x0C1C
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_0          0x0C20
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_1          0x0C24
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_2          0x0C28
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_3          0x0C2C
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_4          0x0C30
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_5          0x0C34
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_6          0x0C38
+#define BCMA_CORE_PCIE2_FUNC1_IMAP0_7          0x0C3C
+#define BCMA_CORE_PCIE2_FUNC0_IMAP1            0x0C80
+#define BCMA_CORE_PCIE2_FUNC1_IMAP1            0x0C88
+#define BCMA_CORE_PCIE2_FUNC0_IMAP2            0x0CC0
+#define BCMA_CORE_PCIE2_FUNC1_IMAP2            0x0CC8
+#define BCMA_CORE_PCIE2_IARR0_LOWER            0x0D00
+#define BCMA_CORE_PCIE2_IARR0_UPPER            0x0D04
+#define BCMA_CORE_PCIE2_IARR1_LOWER            0x0D08
+#define BCMA_CORE_PCIE2_IARR1_UPPER            0x0D0C
+#define BCMA_CORE_PCIE2_IARR2_LOWER            0x0D10
+#define BCMA_CORE_PCIE2_IARR2_UPPER            0x0D14
+#define BCMA_CORE_PCIE2_OARR0                  0x0D20
+#define BCMA_CORE_PCIE2_OARR1                  0x0D28
+#define BCMA_CORE_PCIE2_OARR2                  0x0D30
+#define BCMA_CORE_PCIE2_OMAP0_LOWER            0x0D40
+#define BCMA_CORE_PCIE2_OMAP0_UPPER            0x0D44
+#define BCMA_CORE_PCIE2_OMAP1_LOWER            0x0D48
+#define BCMA_CORE_PCIE2_OMAP1_UPPER            0x0D4C
+#define BCMA_CORE_PCIE2_OMAP2_LOWER            0x0D50
+#define BCMA_CORE_PCIE2_OMAP2_UPPER            0x0D54
+#define BCMA_CORE_PCIE2_FUNC1_IARR1_SIZE       0x0D58
+#define BCMA_CORE_PCIE2_FUNC1_IARR2_SIZE       0x0D5C
+#define BCMA_CORE_PCIE2_MEM_CONTROL            0x0F00
+#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG0                0x0F04
+#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG1                0x0F08
+#define BCMA_CORE_PCIE2_LINK_STATUS            0x0F0C
+#define BCMA_CORE_PCIE2_STRAP_STATUS           0x0F10
+#define BCMA_CORE_PCIE2_RESET_STATUS           0x0F14
+#define BCMA_CORE_PCIE2_RESETEN_IN_LINKDOWN    0x0F18
+#define BCMA_CORE_PCIE2_MISC_INTR_EN           0x0F1C
+#define BCMA_CORE_PCIE2_TX_DEBUG_CFG           0x0F20
+#define BCMA_CORE_PCIE2_MISC_CONFIG            0x0F24
+#define BCMA_CORE_PCIE2_MISC_STATUS            0x0F28
+#define BCMA_CORE_PCIE2_INTR_EN                        0x0F30
+#define BCMA_CORE_PCIE2_INTR_CLEAR             0x0F34
+#define BCMA_CORE_PCIE2_INTR_STATUS            0x0F38
+
+/* PCIE gen2 config regs */
+#define PCIE2_INTSTATUS                                0x090
+#define PCIE2_INTMASK                          0x094
+#define PCIE2_SBMBX                            0x098
+
+#define PCIE2_PMCR_REFUP                       0x1814 /* Trefup time */
+
+#define PCIE2_CAP_DEVSTSCTRL2_OFFSET           0xD4
+#define PCIE2_CAP_DEVSTSCTRL2_LTRENAB          0x400
+#define PCIE2_PVT_REG_PM_CLK_PERIOD            0x184c
+
+struct bcma_drv_pcie2 {
+       struct bcma_device *core;
+};
+
+#define pcie2_read16(pcie2, offset)            bcma_read16((pcie2)->core, offset)
+#define pcie2_read32(pcie2, offset)            bcma_read32((pcie2)->core, offset)
+#define pcie2_write16(pcie2, offset, val)      bcma_write16((pcie2)->core, offset, val)
+#define pcie2_write32(pcie2, offset, val)      bcma_write32((pcie2)->core, offset, val)
+
+#define pcie2_set32(pcie2, offset, set)                bcma_set32((pcie2)->core, offset, set)
+#define pcie2_mask32(pcie2, offset, mask)      bcma_mask32((pcie2)->core, offset, mask)
+
+void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2);
+
+#endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */
index 6bff13f740505090eb53be8b4d91e0fe805a5191..63ab3873c5ed00ac6fa36e73892d4db709c13bf0 100644 (file)
@@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie {
        u8 oui_type;
 } __packed;
 
+struct ieee80211_wmm_ac_param {
+       u8 aci_aifsn; /* AIFSN, ACM, ACI */
+       u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+       __le16 txop_limit;
+} __packed;
+
+struct ieee80211_wmm_param_ie {
+       u8 element_id; /* Element ID: 221 (0xdd); */
+       u8 len; /* Length: 24 */
+       /* required fields for WMM version 1 */
+       u8 oui[3]; /* 00:50:f2 */
+       u8 oui_type; /* 2 */
+       u8 oui_subtype; /* 1 */
+       u8 version; /* 1 for WMM version 1.0 */
+       u8 qos_info; /* AP/STA specific QoS info */
+       u8 reserved; /* 0 */
+       /* AC_BE, AC_BK, AC_VI, AC_VO */
+       struct ieee80211_wmm_ac_param ac[4];
+} __packed;
+
 /* Control frames */
 struct ieee80211_rts {
        __le16 frame_control;
@@ -1621,6 +1641,9 @@ enum ieee80211_reasoncode {
        WLAN_REASON_INVALID_RSN_IE_CAP = 22,
        WLAN_REASON_IEEE8021X_FAILED = 23,
        WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
+       /* TDLS (802.11z) */
+       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE = 25,
+       WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED = 26,
        /* 802.11e */
        WLAN_REASON_DISASSOC_UNSPECIFIED_QOS = 32,
        WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH = 33,
index f9f931c89e3e2deeda4da48138b6237db968b9c9..f7b9100686c3dd4951bebcd6543b869905a208df 100644 (file)
 #define  SSB_SPROM4_TXPID5GH2_SHIFT    0
 #define  SSB_SPROM4_TXPID5GH3          0xFF00
 #define  SSB_SPROM4_TXPID5GH3_SHIFT    8
+
+/* There are 4 blocks with power info sharing the same layout */
+#define SSB_SPROM4_PWR_INFO_CORE0      0x0080
+#define SSB_SPROM4_PWR_INFO_CORE1      0x00AE
+#define SSB_SPROM4_PWR_INFO_CORE2      0x00DC
+#define SSB_SPROM4_PWR_INFO_CORE3      0x010A
+
+#define SSB_SPROM4_2G_MAXP_ITSSI       0x00    /* 2 GHz ITSSI and 2 GHz Max Power */
+#define  SSB_SPROM4_2G_MAXP            0x00FF
+#define  SSB_SPROM4_2G_ITSSI           0xFF00
+#define  SSB_SPROM4_2G_ITSSI_SHIFT     8
+#define SSB_SPROM4_2G_PA_0             0x02    /* 2 GHz power amp */
+#define SSB_SPROM4_2G_PA_1             0x04
+#define SSB_SPROM4_2G_PA_2             0x06
+#define SSB_SPROM4_2G_PA_3             0x08
+#define SSB_SPROM4_5G_MAXP_ITSSI       0x0A    /* 5 GHz ITSSI and 5.3 GHz Max Power */
+#define  SSB_SPROM4_5G_MAXP            0x00FF
+#define  SSB_SPROM4_5G_ITSSI           0xFF00
+#define  SSB_SPROM4_5G_ITSSI_SHIFT     8
+#define SSB_SPROM4_5GHL_MAXP           0x0C    /* 5.2 GHz and 5.8 GHz Max Power */
+#define  SSB_SPROM4_5GH_MAXP           0x00FF
+#define  SSB_SPROM4_5GL_MAXP           0xFF00
+#define  SSB_SPROM4_5GL_MAXP_SHIFT     8
+#define SSB_SPROM4_5G_PA_0             0x0E    /* 5.3 GHz power amp */
+#define SSB_SPROM4_5G_PA_1             0x10
+#define SSB_SPROM4_5G_PA_2             0x12
+#define SSB_SPROM4_5G_PA_3             0x14
+#define SSB_SPROM4_5GL_PA_0            0x16    /* 5.2 GHz power amp */
+#define SSB_SPROM4_5GL_PA_1            0x18
+#define SSB_SPROM4_5GL_PA_2            0x1A
+#define SSB_SPROM4_5GL_PA_3            0x1C
+#define SSB_SPROM4_5GH_PA_0            0x1E    /* 5.8 GHz power amp */
+#define SSB_SPROM4_5GH_PA_1            0x20
+#define SSB_SPROM4_5GH_PA_2            0x22
+#define SSB_SPROM4_5GH_PA_3            0x24
+
+/* TODO: Make it deprecated */
 #define SSB_SPROM4_MAXP_BG             0x0080  /* Max Power BG in path 1 */
 #define  SSB_SPROM4_MAXP_BG_MASK       0x00FF  /* Mask for Max Power BG */
 #define  SSB_SPROM4_ITSSI_BG           0xFF00  /* Mask for path 1 itssi_bg */
index 904777c1cd2420486a3df25636f61811ab35c29e..373000de610d4d3077f289a2f7a1967910ef09e3 100644 (file)
@@ -260,15 +260,15 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock);
 
 /* Skb helpers */
 struct l2cap_ctrl {
-       unsigned int    sframe:1,
-                       poll:1,
-                       final:1,
-                       fcs:1,
-                       sar:2,
-                       super:2;
-       __u16           reqseq;
-       __u16           txseq;
-       __u8            retries;
+       __u8    sframe:1,
+               poll:1,
+               final:1,
+               fcs:1,
+               sar:2,
+               super:2;
+       __u16   reqseq;
+       __u16   txseq;
+       __u8    retries;
 };
 
 struct hci_dev;
index 16587dcd6a9181b74814f1a4d0865332aa1bd81d..f0a3d88907605eab81264a8a9ee10a07ef32cce1 100644 (file)
 
 /* HCI device quirks */
 enum {
+       /* When this quirk is set, the HCI Reset command is send when
+        * closing the transport instead of when opening it.
+        *
+        * This quirk must be set before hci_register_dev is called.
+        */
        HCI_QUIRK_RESET_ON_CLOSE,
+
+       /* When this quirk is set, the device is turned into a raw-only
+        * device and it will stay in unconfigured state.
+        *
+        * This quirk must be set before hci_register_dev is called.
+        */
        HCI_QUIRK_RAW_DEVICE,
+
+       /* When this quirk is set, the buffer sizes reported by
+        * HCI Read Buffer Size command are corrected if invalid.
+        *
+        * This quirk must be set before hci_register_dev is called.
+        */
        HCI_QUIRK_FIXUP_BUFFER_SIZE,
+
+       /* When this quirk is set, then no stored link key handling
+        * is performed. This is mainly due to the fact that the
+        * HCI Delete Stored Link Key command is advertised, but
+        * not supported.
+        *
+        * This quirk must be set before hci_register_dev is called.
+        */
        HCI_QUIRK_BROKEN_STORED_LINK_KEY,
+
+       /* When this quirk is set, an external configuration step
+        * is required and will be indicated with the controller
+        * configuation.
+        *
+        * This quirk can be set before hci_register_dev is called or
+        * during the hdev->setup vendor callback.
+        */
+       HCI_QUIRK_EXTERNAL_CONFIG,
+
+       /* When this quirk is set, the public Bluetooth address
+        * initially reported by HCI Read BD Address command
+        * is considered invalid. Controller configuration is
+        * required before this device can be used.
+        *
+        * This quirk can be set before hci_register_dev is called or
+        * during the hdev->setup vendor callback.
+        */
+       HCI_QUIRK_INVALID_BDADDR,
 };
 
 /* HCI device flags */
@@ -104,24 +148,34 @@ enum {
        HCI_RESET,
 };
 
+/* BR/EDR and/or LE controller flags: the flags defined here should represent
+ * states configured via debugfs for debugging and testing purposes only.
+ */
+enum {
+       HCI_DUT_MODE,
+       HCI_FORCE_SC,
+       HCI_FORCE_STATIC_ADDR,
+};
+
 /*
  * BR/EDR and/or LE controller flags: the flags defined here should represent
  * states from the controller.
  */
 enum {
        HCI_SETUP,
+       HCI_CONFIG,
        HCI_AUTO_OFF,
        HCI_RFKILLED,
        HCI_MGMT,
        HCI_PAIRABLE,
        HCI_SERVICE_CACHE,
-       HCI_DEBUG_KEYS,
-       HCI_DUT_MODE,
-       HCI_FORCE_SC,
-       HCI_FORCE_STATIC_ADDR,
+       HCI_KEEP_DEBUG_KEYS,
+       HCI_USE_DEBUG_KEYS,
        HCI_UNREGISTER,
+       HCI_UNCONFIGURED,
        HCI_USER_CHANNEL,
-
+       HCI_EXT_CONFIGURED,
+       HCI_LE_ADV,
        HCI_LE_SCAN,
        HCI_SSP_ENABLED,
        HCI_SC_ENABLED,
@@ -139,7 +193,6 @@ enum {
        HCI_PERIODIC_INQ,
        HCI_FAST_CONNECTABLE,
        HCI_BREDR_ENABLED,
-       HCI_6LOWPAN_ENABLED,
        HCI_LE_SCAN_INTERRUPTED,
 };
 
@@ -147,34 +200,7 @@ enum {
  * or the HCI device is closed.
  */
 #define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \
-                             BIT(HCI_FAST_CONNECTABLE))
-
-/* HCI ioctl defines */
-#define HCIDEVUP       _IOW('H', 201, int)
-#define HCIDEVDOWN     _IOW('H', 202, int)
-#define HCIDEVRESET    _IOW('H', 203, int)
-#define HCIDEVRESTAT   _IOW('H', 204, int)
-
-#define HCIGETDEVLIST  _IOR('H', 210, int)
-#define HCIGETDEVINFO  _IOR('H', 211, int)
-#define HCIGETCONNLIST _IOR('H', 212, int)
-#define HCIGETCONNINFO _IOR('H', 213, int)
-#define HCIGETAUTHINFO _IOR('H', 215, int)
-
-#define HCISETRAW      _IOW('H', 220, int)
-#define HCISETSCAN     _IOW('H', 221, int)
-#define HCISETAUTH     _IOW('H', 222, int)
-#define HCISETENCRYPT  _IOW('H', 223, int)
-#define HCISETPTYPE    _IOW('H', 224, int)
-#define HCISETLINKPOL  _IOW('H', 225, int)
-#define HCISETLINKMODE _IOW('H', 226, int)
-#define HCISETACLMTU   _IOW('H', 227, int)
-#define HCISETSCOMTU   _IOW('H', 228, int)
-
-#define HCIBLOCKADDR   _IOW('H', 230, int)
-#define HCIUNBLOCKADDR _IOW('H', 231, int)
-
-#define HCIINQUIRY     _IOR('H', 240, int)
+                             BIT(HCI_FAST_CONNECTABLE) | BIT(HCI_LE_ADV))
 
 /* HCI timeouts */
 #define HCI_DISCONN_TIMEOUT    msecs_to_jiffies(2000)  /* 2 seconds */
@@ -185,6 +211,7 @@ enum {
 #define HCI_AUTO_OFF_TIMEOUT   msecs_to_jiffies(2000)  /* 2 seconds */
 #define HCI_POWER_OFF_TIMEOUT  msecs_to_jiffies(5000)  /* 5 seconds */
 #define HCI_LE_CONN_TIMEOUT    msecs_to_jiffies(20000) /* 20 seconds */
+#define HCI_LE_AUTOCONN_TIMEOUT        msecs_to_jiffies(2000)  /* 2 seconds */
 
 /* HCI data types */
 #define HCI_COMMAND_PKT                0x01
@@ -301,6 +328,11 @@ enum {
 #define LMP_HOST_LE_BREDR      0x04
 #define LMP_HOST_SC            0x08
 
+/* LE features */
+#define HCI_LE_ENCRYPTION              0x01
+#define HCI_LE_CONN_PARAM_REQ_PROC     0x02
+#define HCI_LE_PING                    0x10
+
 /* Connection modes */
 #define HCI_CM_ACTIVE  0x0000
 #define HCI_CM_HOLD    0x0001
@@ -347,17 +379,9 @@ enum {
 #define HCI_LK_CHANGED_COMBINATION     0x06
 #define HCI_LK_UNAUTH_COMBINATION_P256 0x07
 #define HCI_LK_AUTH_COMBINATION_P256   0x08
-/* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */
-#define HCI_SMP_STK                    0x80
-#define HCI_SMP_STK_SLAVE              0x81
-#define HCI_SMP_LTK                    0x82
-#define HCI_SMP_LTK_SLAVE              0x83
-
-/* Long Term Key types */
-#define HCI_LTK_UNAUTH                 0x00
-#define HCI_LTK_AUTH                   0x01
 
 /* ---- HCI Error Codes ---- */
+#define HCI_ERROR_UNKNOWN_CONN_ID      0x02
 #define HCI_ERROR_AUTH_FAILURE         0x05
 #define HCI_ERROR_MEMORY_EXCEEDED      0x07
 #define HCI_ERROR_CONNECTION_TIMEOUT   0x08
@@ -367,6 +391,7 @@ enum {
 #define HCI_ERROR_REMOTE_POWER_OFF     0x15
 #define HCI_ERROR_LOCAL_HOST_TERM      0x16
 #define HCI_ERROR_PAIRING_NOT_ALLOWED  0x18
+#define HCI_ERROR_INVALID_LL_PARAMS    0x1E
 #define HCI_ERROR_ADVERTISING_TIMEOUT  0x3c
 
 /* Flow control modes */
@@ -376,6 +401,9 @@ enum {
 /* The core spec defines 127 as the "not available" value */
 #define HCI_TX_POWER_INVALID   127
 
+#define HCI_ROLE_MASTER                0x00
+#define HCI_ROLE_SLAVE         0x01
+
 /* Extended Inquiry Response field types */
 #define EIR_FLAGS              0x01 /* flags */
 #define EIR_UUID16_SOME                0x02 /* 16-bit UUID, more available */
@@ -536,6 +564,11 @@ struct hci_cp_read_remote_version {
        __le16   handle;
 } __packed;
 
+#define HCI_OP_READ_CLOCK_OFFSET       0x041f
+struct hci_cp_read_clock_offset {
+       __le16   handle;
+} __packed;
+
 #define HCI_OP_SETUP_SYNC_CONN         0x0428
 struct hci_cp_setup_sync_conn {
        __le16   handle;
@@ -1085,6 +1118,18 @@ struct hci_rp_read_rssi {
        __s8     rssi;
 } __packed;
 
+#define HCI_OP_READ_CLOCK              0x1407
+struct hci_cp_read_clock {
+       __le16   handle;
+       __u8     which;
+} __packed;
+struct hci_rp_read_clock {
+       __u8     status;
+       __le16   handle;
+       __le32   clock;
+       __le16   accuracy;
+} __packed;
+
 #define HCI_OP_READ_LOCAL_AMP_INFO     0x1409
 struct hci_rp_read_local_amp_info {
        __u8     status;
@@ -1291,6 +1336,23 @@ struct hci_rp_le_read_supported_states {
        __u8    le_states[8];
 } __packed;
 
+#define HCI_OP_LE_CONN_PARAM_REQ_REPLY 0x2020
+struct hci_cp_le_conn_param_req_reply {
+       __le16  handle;
+       __le16  interval_min;
+       __le16  interval_max;
+       __le16  latency;
+       __le16  timeout;
+       __le16  min_ce_len;
+       __le16  max_ce_len;
+} __packed;
+
+#define HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY     0x2021
+struct hci_cp_le_conn_param_req_neg_reply {
+       __le16  handle;
+       __u8    reason;
+} __packed;
+
 /* ---- HCI Events ---- */
 #define HCI_EV_INQUIRY_COMPLETE                0x01
 
@@ -1654,9 +1716,6 @@ struct hci_ev_sync_train_complete {
 
 #define HCI_EV_SLAVE_PAGE_RESP_TIMEOUT 0x54
 
-/* Low energy meta events */
-#define LE_CONN_ROLE_MASTER    0x00
-
 #define HCI_EV_LE_CONN_COMPLETE                0x01
 struct hci_ev_le_conn_complete {
        __u8     status;
@@ -1670,6 +1729,15 @@ struct hci_ev_le_conn_complete {
        __u8     clk_accurancy;
 } __packed;
 
+#define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03
+struct hci_ev_le_conn_update_complete {
+       __u8     status;
+       __le16   handle;
+       __le16   interval;
+       __le16   latency;
+       __le16   supervision_timeout;
+} __packed;
+
 #define HCI_EV_LE_LTK_REQ              0x05
 struct hci_ev_le_ltk_req {
        __le16  handle;
@@ -1677,6 +1745,15 @@ struct hci_ev_le_ltk_req {
        __le16  ediv;
 } __packed;
 
+#define HCI_EV_LE_REMOTE_CONN_PARAM_REQ        0x06
+struct hci_ev_le_remote_conn_param_req {
+       __le16 handle;
+       __le16 interval_min;
+       __le16 interval_max;
+       __le16 latency;
+       __le16 timeout;
+} __packed;
+
 /* Advertising report event types */
 #define LE_ADV_IND             0x00
 #define LE_ADV_DIRECT_IND      0x01
@@ -1768,126 +1845,4 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
 #define hci_handle(h)          (h & 0x0fff)
 #define hci_flags(h)           (h >> 12)
 
-/* ---- HCI Sockets ---- */
-
-/* Socket options */
-#define HCI_DATA_DIR   1
-#define HCI_FILTER     2
-#define HCI_TIME_STAMP 3
-
-/* CMSG flags */
-#define HCI_CMSG_DIR   0x0001
-#define HCI_CMSG_TSTAMP        0x0002
-
-struct sockaddr_hci {
-       sa_family_t    hci_family;
-       unsigned short hci_dev;
-       unsigned short hci_channel;
-};
-#define HCI_DEV_NONE   0xffff
-
-#define HCI_CHANNEL_RAW                0
-#define HCI_CHANNEL_USER       1
-#define HCI_CHANNEL_MONITOR    2
-#define HCI_CHANNEL_CONTROL    3
-
-struct hci_filter {
-       unsigned long type_mask;
-       unsigned long event_mask[2];
-       __le16 opcode;
-};
-
-struct hci_ufilter {
-       __u32  type_mask;
-       __u32  event_mask[2];
-       __le16 opcode;
-};
-
-#define HCI_FLT_TYPE_BITS      31
-#define HCI_FLT_EVENT_BITS     63
-#define HCI_FLT_OGF_BITS       63
-#define HCI_FLT_OCF_BITS       127
-
-/* ---- HCI Ioctl requests structures ---- */
-struct hci_dev_stats {
-       __u32 err_rx;
-       __u32 err_tx;
-       __u32 cmd_tx;
-       __u32 evt_rx;
-       __u32 acl_tx;
-       __u32 acl_rx;
-       __u32 sco_tx;
-       __u32 sco_rx;
-       __u32 byte_rx;
-       __u32 byte_tx;
-};
-
-struct hci_dev_info {
-       __u16 dev_id;
-       char  name[8];
-
-       bdaddr_t bdaddr;
-
-       __u32 flags;
-       __u8  type;
-
-       __u8  features[8];
-
-       __u32 pkt_type;
-       __u32 link_policy;
-       __u32 link_mode;
-
-       __u16 acl_mtu;
-       __u16 acl_pkts;
-       __u16 sco_mtu;
-       __u16 sco_pkts;
-
-       struct hci_dev_stats stat;
-};
-
-struct hci_conn_info {
-       __u16    handle;
-       bdaddr_t bdaddr;
-       __u8     type;
-       __u8     out;
-       __u16    state;
-       __u32    link_mode;
-};
-
-struct hci_dev_req {
-       __u16  dev_id;
-       __u32  dev_opt;
-};
-
-struct hci_dev_list_req {
-       __u16  dev_num;
-       struct hci_dev_req dev_req[0];  /* hci_dev_req structures */
-};
-
-struct hci_conn_list_req {
-       __u16  dev_id;
-       __u16  conn_num;
-       struct hci_conn_info conn_info[0];
-};
-
-struct hci_conn_info_req {
-       bdaddr_t bdaddr;
-       __u8     type;
-       struct   hci_conn_info conn_info[0];
-};
-
-struct hci_auth_info_req {
-       bdaddr_t bdaddr;
-       __u8     type;
-};
-
-struct hci_inquiry_req {
-       __u16 dev_id;
-       __u16 flags;
-       __u8  lap[3];
-       __u8  length;
-       __u8  num_rsp;
-};
-#define IREQ_CACHE_FLUSH 0x0001
-
 #endif /* __HCI_H */
index b386bf17e6c2c10808c8306ad7cbb4bb42e4713d..996ed065b6c23d04d1c274b7352591bf54aca0d8 100644 (file)
@@ -26,6 +26,7 @@
 #define __HCI_CORE_H
 
 #include <net/bluetooth/hci.h>
+#include <net/bluetooth/hci_sock.h>
 
 /* HCI priority */
 #define HCI_PRIO_MAX   7
@@ -71,6 +72,7 @@ struct discovery_state {
        bdaddr_t                last_adv_addr;
        u8                      last_adv_addr_type;
        s8                      last_adv_rssi;
+       u32                     last_adv_flags;
        u8                      last_adv_data[HCI_MAX_AD_LENGTH];
        u8                      last_adv_data_len;
 };
@@ -81,6 +83,7 @@ struct hci_conn_hash {
        unsigned int     amp_num;
        unsigned int     sco_num;
        unsigned int     le_num;
+       unsigned int     le_num_slave;
 };
 
 struct bdaddr_list {
@@ -170,6 +173,8 @@ struct hci_dev {
        __u8            bus;
        __u8            dev_type;
        bdaddr_t        bdaddr;
+       bdaddr_t        setup_addr;
+       bdaddr_t        public_addr;
        bdaddr_t        random_addr;
        bdaddr_t        static_addr;
        __u8            adv_addr_type;
@@ -203,10 +208,13 @@ struct hci_dev {
        __u16           le_scan_window;
        __u16           le_conn_min_interval;
        __u16           le_conn_max_interval;
+       __u16           le_conn_latency;
+       __u16           le_supv_timeout;
        __u16           discov_interleaved_timeout;
        __u16           conn_info_min_age;
        __u16           conn_info_max_age;
        __u8            ssp_debug_mode;
+       __u32           clock;
 
        __u16           devid_source;
        __u16           devid_vendor;
@@ -273,7 +281,7 @@ struct hci_dev {
 
        struct delayed_work     service_cache;
 
-       struct timer_list       cmd_timer;
+       struct delayed_work     cmd_timer;
 
        struct work_struct      rx_work;
        struct work_struct      cmd_work;
@@ -299,6 +307,7 @@ struct hci_dev {
 
        struct list_head        mgmt_pending;
        struct list_head        blacklist;
+       struct list_head        whitelist;
        struct list_head        uuids;
        struct list_head        link_keys;
        struct list_head        long_term_keys;
@@ -307,6 +316,7 @@ struct hci_dev {
        struct list_head        le_white_list;
        struct list_head        le_conn_params;
        struct list_head        pend_le_conns;
+       struct list_head        pend_le_reports;
 
        struct hci_dev_stats    stat;
 
@@ -318,6 +328,7 @@ struct hci_dev {
 
        struct rfkill           *rfkill;
 
+       unsigned long           dbg_flags;
        unsigned long           dev_flags;
 
        struct delayed_work     le_scan_disable;
@@ -339,6 +350,7 @@ struct hci_dev {
        int (*setup)(struct hci_dev *hdev);
        int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
        void (*notify)(struct hci_dev *hdev, unsigned int evt);
+       int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
 };
 
 #define HCI_PHY_HANDLE(handle) (handle & 0xff)
@@ -360,13 +372,13 @@ struct hci_conn {
        __u16           state;
        __u8            mode;
        __u8            type;
+       __u8            role;
        bool            out;
        __u8            attempt;
        __u8            dev_class[3];
        __u8            features[HCI_MAX_PAGES][8];
        __u16           pkt_type;
        __u16           link_policy;
-       __u32           link_mode;
        __u8            key_type;
        __u8            auth_type;
        __u8            sec_level;
@@ -377,20 +389,26 @@ struct hci_conn {
        __u32           passkey_notify;
        __u8            passkey_entered;
        __u16           disc_timeout;
+       __u16           conn_timeout;
        __u16           setting;
        __u16           le_conn_min_interval;
        __u16           le_conn_max_interval;
+       __u16           le_conn_interval;
+       __u16           le_conn_latency;
+       __u16           le_supv_timeout;
        __s8            rssi;
        __s8            tx_power;
        __s8            max_tx_power;
        unsigned long   flags;
 
+       __u32           clock;
+       __u16           clock_accuracy;
+
        unsigned long   conn_info_timestamp;
 
        __u8            remote_cap;
        __u8            remote_auth;
        __u8            remote_id;
-       bool            flush_key;
 
        unsigned int    sent;
 
@@ -407,7 +425,6 @@ struct hci_conn {
        struct hci_dev  *hdev;
        void            *l2cap_data;
        void            *sco_data;
-       void            *smp_conn;
        struct amp_mgr  *amp_mgr;
 
        struct hci_conn *link;
@@ -428,15 +445,19 @@ struct hci_chan {
 
 struct hci_conn_params {
        struct list_head list;
+       struct list_head action;
 
        bdaddr_t addr;
        u8 addr_type;
 
        u16 conn_min_interval;
        u16 conn_max_interval;
+       u16 conn_latency;
+       u16 supervision_timeout;
 
        enum {
                HCI_AUTO_CONN_DISABLED,
+               HCI_AUTO_CONN_REPORT,
                HCI_AUTO_CONN_ALWAYS,
                HCI_AUTO_CONN_LINK_LOSS,
        } auto_connect;
@@ -501,8 +522,8 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev,
                                                       int state);
 void hci_inquiry_cache_update_resolve(struct hci_dev *hdev,
                                      struct inquiry_entry *ie);
-bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
-                             bool name_known, bool *ssp);
+u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
+                            bool name_known);
 void hci_inquiry_cache_flush(struct hci_dev *hdev);
 
 /* ----- HCI Connections ----- */
@@ -520,7 +541,13 @@ enum {
        HCI_CONN_AES_CCM,
        HCI_CONN_POWER_SAVE,
        HCI_CONN_REMOTE_OOB,
-       HCI_CONN_6LOWPAN,
+       HCI_CONN_FLUSH_KEY,
+       HCI_CONN_ENCRYPT,
+       HCI_CONN_AUTH,
+       HCI_CONN_SECURE,
+       HCI_CONN_FIPS,
+       HCI_CONN_STK_ENCRYPT,
+       HCI_CONN_AUTH_INITIATOR,
 };
 
 static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
@@ -550,6 +577,8 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
                break;
        case LE_LINK:
                h->le_num++;
+               if (c->role == HCI_ROLE_SLAVE)
+                       h->le_num_slave++;
                break;
        case SCO_LINK:
        case ESCO_LINK:
@@ -574,6 +603,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
                break;
        case LE_LINK:
                h->le_num--;
+               if (c->role == HCI_ROLE_SLAVE)
+                       h->le_num_slave--;
                break;
        case SCO_LINK:
        case ESCO_LINK:
@@ -670,7 +701,8 @@ void hci_disconnect(struct hci_conn *conn, __u8 reason);
 bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
 void hci_sco_setup(struct hci_conn *conn, __u8 status);
 
-struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
+                             u8 role);
 int hci_conn_del(struct hci_conn *conn);
 void hci_conn_hash_flush(struct hci_dev *hdev);
 void hci_conn_check_pending(struct hci_dev *hdev);
@@ -681,14 +713,16 @@ void hci_chan_list_flush(struct hci_conn *conn);
 struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle);
 
 struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
-                               u8 dst_type, u8 sec_level, u8 auth_type);
+                               u8 dst_type, u8 sec_level, u16 conn_timeout,
+                               u8 role);
 struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
                                 u8 sec_level, u8 auth_type);
 struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
                                 __u16 setting);
 int hci_conn_check_link_mode(struct hci_conn *conn);
 int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
-int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
+int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
+                     bool initiator);
 int hci_conn_change_link_key(struct hci_conn *conn);
 int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
 
@@ -825,30 +859,25 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg);
 int hci_get_auth_info(struct hci_dev *hdev, void __user *arg);
 int hci_inquiry(void __user *arg);
 
-struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
-                                        bdaddr_t *bdaddr, u8 type);
-int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-
-struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev,
-                                         bdaddr_t *bdaddr, u8 type);
-void hci_white_list_clear(struct hci_dev *hdev);
-int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
+struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *list,
+                                          bdaddr_t *bdaddr, u8 type);
+int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 type);
+int hci_bdaddr_list_del(struct list_head *list, bdaddr_t *bdaddr, u8 type);
+void hci_bdaddr_list_clear(struct list_head *list);
 
 struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
                                               bdaddr_t *addr, u8 addr_type);
-int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
-                       u8 auto_connect, u16 conn_min_interval,
-                       u16 conn_max_interval);
+struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
+                                           bdaddr_t *addr, u8 addr_type);
+int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
+                       u8 auto_connect);
 void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
-void hci_conn_params_clear(struct hci_dev *hdev);
+void hci_conn_params_clear_all(struct hci_dev *hdev);
+void hci_conn_params_clear_disabled(struct hci_dev *hdev);
 
-struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev,
-                                           bdaddr_t *addr, u8 addr_type);
-void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
-void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
-void hci_pend_le_conns_clear(struct hci_dev *hdev);
+struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
+                                                 bdaddr_t *addr,
+                                                 u8 addr_type);
 
 void hci_update_background_scan(struct hci_dev *hdev);
 
@@ -856,15 +885,16 @@ void hci_uuids_clear(struct hci_dev *hdev);
 
 void hci_link_keys_clear(struct hci_dev *hdev);
 struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
-int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
-                    bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len);
+struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
+                                 bdaddr_t *bdaddr, u8 *val, u8 type,
+                                 u8 pin_len, bool *persistent);
 struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
-                            bool master);
+                            u8 role);
 struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
                            u8 addr_type, u8 type, u8 authenticated,
                            u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand);
 struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 addr_type, bool master);
+                                    u8 addr_type, u8 role);
 int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type);
 void hci_smp_ltks_clear(struct hci_dev *hdev);
 int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
@@ -1021,7 +1051,7 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
        if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
                return;
 
-       encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
+       encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
        l2cap_security_cfm(conn, status, encrypt);
 
        if (conn->security_cfm_cb)
@@ -1062,7 +1092,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
        if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
                return;
 
-       encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
+       encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
 
        read_lock(&hci_cb_list_lock);
        list_for_each_entry(cb, &hci_cb_list, list) {
@@ -1147,7 +1177,7 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
 
 static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
 {
-       if (addr_type != 0x01)
+       if (addr_type != ADDR_LE_DEV_RANDOM)
                return false;
 
        if ((bdaddr->b[5] & 0xc0) == 0x40)
@@ -1156,6 +1186,18 @@ static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
        return false;
 }
 
+static inline bool hci_is_identity_address(bdaddr_t *addr, u8 addr_type)
+{
+       if (addr_type == ADDR_LE_DEV_PUBLIC)
+               return true;
+
+       /* Check for Random Static address type */
+       if ((addr->b[5] & 0xc0) == 0xc0)
+               return true;
+
+       return false;
+}
+
 static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev,
                                          bdaddr_t *bdaddr, u8 addr_type)
 {
@@ -1165,6 +1207,27 @@ static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev,
        return hci_find_irk_by_rpa(hdev, bdaddr);
 }
 
+static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
+                                       u16 to_multiplier)
+{
+       u16 max_latency;
+
+       if (min > max || min < 6 || max > 3200)
+               return -EINVAL;
+
+       if (to_multiplier < 10 || to_multiplier > 3200)
+               return -EINVAL;
+
+       if (max >= to_multiplier * 8)
+               return -EINVAL;
+
+       max_latency = (to_multiplier * 8 / max) - 1;
+       if (latency > 499 || latency > max_latency)
+               return -EINVAL;
+
+       return 0;
+}
+
 int hci_register_cb(struct hci_cb *hcb);
 int hci_unregister_cb(struct hci_cb *hcb);
 
@@ -1185,6 +1248,7 @@ void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
 void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
                    const void *param, u8 event);
 void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
+bool hci_req_pending(struct hci_dev *hdev);
 
 void hci_req_add_le_scan_disable(struct hci_request *req);
 void hci_req_add_le_passive_scan(struct hci_request *req);
@@ -1227,15 +1291,13 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event);
 #define DISCOV_BREDR_INQUIRY_LEN       0x08
 
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
+int mgmt_new_settings(struct hci_dev *hdev);
 void mgmt_index_added(struct hci_dev *hdev);
 void mgmt_index_removed(struct hci_dev *hdev);
 void mgmt_set_powered_failed(struct hci_dev *hdev, int err);
 int mgmt_powered(struct hci_dev *hdev, u8 powered);
+int mgmt_update_adv_data(struct hci_dev *hdev);
 void mgmt_discoverable_timeout(struct hci_dev *hdev);
-void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
-void mgmt_connectable(struct hci_dev *hdev, u8 connectable);
-void mgmt_advertising(struct hci_dev *hdev, u8 advertising);
-void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
 void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
                       bool persistent);
 void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
@@ -1281,51 +1343,23 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
                                       u8 *randomizer192, u8 *hash256,
                                       u8 *randomizer256, u8 status);
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
-                      u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp,
-                      u8 scan_rsp_len);
+                      u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
+                      u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
 void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                      u8 addr_type, s8 rssi, u8 *name, u8 name_len);
 void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
-int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent);
 void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk);
 void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
                   bool persistent);
+void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                        u8 bdaddr_type, u8 store_hint, u16 min_interval,
+                        u16 max_interval, u16 latency, u16 timeout);
 void mgmt_reenable_advertising(struct hci_dev *hdev);
 void mgmt_smp_complete(struct hci_conn *conn, bool complete);
 
-/* HCI info for socket */
-#define hci_pi(sk) ((struct hci_pinfo *) sk)
-
-struct hci_pinfo {
-       struct bt_sock    bt;
-       struct hci_dev    *hdev;
-       struct hci_filter filter;
-       __u32             cmsg_mask;
-       unsigned short   channel;
-};
-
-/* HCI security filter */
-#define HCI_SFLT_MAX_OGF  5
-
-struct hci_sec_filter {
-       __u32 type_mask;
-       __u32 event_mask[2];
-       __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
-};
-
-/* ----- HCI requests ----- */
-#define HCI_REQ_DONE     0
-#define HCI_REQ_PEND     1
-#define HCI_REQ_CANCELED  2
-
-#define hci_req_lock(d)                mutex_lock(&d->req_lock)
-#define hci_req_unlock(d)      mutex_unlock(&d->req_lock)
-
-void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
-                                       u16 latency, u16 to_multiplier);
+u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
+                     u16 to_multiplier);
 void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
                                                        __u8 ltk[16]);
 
diff --git a/include/net/bluetooth/hci_sock.h b/include/net/bluetooth/hci_sock.h
new file mode 100644 (file)
index 0000000..9a46d66
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+   SOFTWARE IS DISCLAIMED.
+*/
+
+#ifndef __HCI_SOCK_H
+#define __HCI_SOCK_H
+
+/* Socket options */
+#define HCI_DATA_DIR   1
+#define HCI_FILTER     2
+#define HCI_TIME_STAMP 3
+
+/* CMSG flags */
+#define HCI_CMSG_DIR   0x0001
+#define HCI_CMSG_TSTAMP        0x0002
+
+struct sockaddr_hci {
+       sa_family_t    hci_family;
+       unsigned short hci_dev;
+       unsigned short hci_channel;
+};
+#define HCI_DEV_NONE   0xffff
+
+#define HCI_CHANNEL_RAW                0
+#define HCI_CHANNEL_USER       1
+#define HCI_CHANNEL_MONITOR    2
+#define HCI_CHANNEL_CONTROL    3
+
+struct hci_filter {
+       unsigned long type_mask;
+       unsigned long event_mask[2];
+       __le16 opcode;
+};
+
+struct hci_ufilter {
+       __u32  type_mask;
+       __u32  event_mask[2];
+       __le16 opcode;
+};
+
+#define HCI_FLT_TYPE_BITS      31
+#define HCI_FLT_EVENT_BITS     63
+#define HCI_FLT_OGF_BITS       63
+#define HCI_FLT_OCF_BITS       127
+
+/* Ioctl defines */
+#define HCIDEVUP       _IOW('H', 201, int)
+#define HCIDEVDOWN     _IOW('H', 202, int)
+#define HCIDEVRESET    _IOW('H', 203, int)
+#define HCIDEVRESTAT   _IOW('H', 204, int)
+
+#define HCIGETDEVLIST  _IOR('H', 210, int)
+#define HCIGETDEVINFO  _IOR('H', 211, int)
+#define HCIGETCONNLIST _IOR('H', 212, int)
+#define HCIGETCONNINFO _IOR('H', 213, int)
+#define HCIGETAUTHINFO _IOR('H', 215, int)
+
+#define HCISETRAW      _IOW('H', 220, int)
+#define HCISETSCAN     _IOW('H', 221, int)
+#define HCISETAUTH     _IOW('H', 222, int)
+#define HCISETENCRYPT  _IOW('H', 223, int)
+#define HCISETPTYPE    _IOW('H', 224, int)
+#define HCISETLINKPOL  _IOW('H', 225, int)
+#define HCISETLINKMODE _IOW('H', 226, int)
+#define HCISETACLMTU   _IOW('H', 227, int)
+#define HCISETSCOMTU   _IOW('H', 228, int)
+
+#define HCIBLOCKADDR   _IOW('H', 230, int)
+#define HCIUNBLOCKADDR _IOW('H', 231, int)
+
+#define HCIINQUIRY     _IOR('H', 240, int)
+
+/* Ioctl requests structures */
+struct hci_dev_stats {
+       __u32 err_rx;
+       __u32 err_tx;
+       __u32 cmd_tx;
+       __u32 evt_rx;
+       __u32 acl_tx;
+       __u32 acl_rx;
+       __u32 sco_tx;
+       __u32 sco_rx;
+       __u32 byte_rx;
+       __u32 byte_tx;
+};
+
+struct hci_dev_info {
+       __u16 dev_id;
+       char  name[8];
+
+       bdaddr_t bdaddr;
+
+       __u32 flags;
+       __u8  type;
+
+       __u8  features[8];
+
+       __u32 pkt_type;
+       __u32 link_policy;
+       __u32 link_mode;
+
+       __u16 acl_mtu;
+       __u16 acl_pkts;
+       __u16 sco_mtu;
+       __u16 sco_pkts;
+
+       struct hci_dev_stats stat;
+};
+
+struct hci_conn_info {
+       __u16    handle;
+       bdaddr_t bdaddr;
+       __u8     type;
+       __u8     out;
+       __u16    state;
+       __u32    link_mode;
+};
+
+struct hci_dev_req {
+       __u16  dev_id;
+       __u32  dev_opt;
+};
+
+struct hci_dev_list_req {
+       __u16  dev_num;
+       struct hci_dev_req dev_req[0];  /* hci_dev_req structures */
+};
+
+struct hci_conn_list_req {
+       __u16  dev_id;
+       __u16  conn_num;
+       struct hci_conn_info conn_info[0];
+};
+
+struct hci_conn_info_req {
+       bdaddr_t bdaddr;
+       __u8     type;
+       struct   hci_conn_info conn_info[0];
+};
+
+struct hci_auth_info_req {
+       bdaddr_t bdaddr;
+       __u8     type;
+};
+
+struct hci_inquiry_req {
+       __u16 dev_id;
+       __u16 flags;
+       __u8  lap[3];
+       __u8  length;
+       __u8  num_rsp;
+};
+#define IREQ_CACHE_FLUSH 0x0001
+
+#endif /* __HCI_SOCK_H */
index 4abdcb220e3ac7a0558a9a0115b873a5312f3f07..8df15ad0d43fadc17f82aed2cb2b9115ff6a425f 100644 (file)
@@ -134,10 +134,12 @@ struct l2cap_conninfo {
 #define L2CAP_FCS_CRC16                0x01
 
 /* L2CAP fixed channels */
-#define L2CAP_FC_L2CAP         0x02
+#define L2CAP_FC_SIG_BREDR     0x02
 #define L2CAP_FC_CONNLESS      0x04
 #define L2CAP_FC_A2MP          0x08
-#define L2CAP_FC_6LOWPAN        0x3e /* reserved and temporary value */
+#define L2CAP_FC_ATT           0x10
+#define L2CAP_FC_SIG_LE                0x20
+#define L2CAP_FC_SMP_LE                0x40
 
 /* L2CAP Control Field bit masks */
 #define L2CAP_CTRL_SAR                 0xC000
@@ -579,7 +581,7 @@ struct l2cap_chan {
        struct list_head        global_l;
 
        void                    *data;
-       struct l2cap_ops        *ops;
+       const struct l2cap_ops  *ops;
        struct mutex            lock;
 };
 
@@ -600,7 +602,12 @@ struct l2cap_ops {
        void                    (*set_shutdown) (struct l2cap_chan *chan);
        long                    (*get_sndtimeo) (struct l2cap_chan *chan);
        struct sk_buff          *(*alloc_skb) (struct l2cap_chan *chan,
+                                              unsigned long hdr_len,
                                               unsigned long len, int nb);
+       int                     (*memcpy_fromiovec) (struct l2cap_chan *chan,
+                                                    unsigned char *kdata,
+                                                    struct iovec *iov,
+                                                    int len);
 };
 
 struct l2cap_conn {
@@ -618,11 +625,10 @@ struct l2cap_conn {
 
        struct delayed_work     info_timer;
 
-       spinlock_t              lock;
-
        struct sk_buff          *rx_skb;
        __u32                   rx_len;
        __u8                    tx_ident;
+       struct mutex            ident_lock;
 
        struct sk_buff_head     pending_rx;
        struct work_struct      pending_rx_work;
@@ -856,6 +862,31 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan)
        return 0;
 }
 
+static inline int l2cap_chan_no_memcpy_fromiovec(struct l2cap_chan *chan,
+                                                unsigned char *kdata,
+                                                struct iovec *iov,
+                                                int len)
+{
+       /* Following is safe since for compiler definitions of kvec and
+        * iovec are identical, yielding the same in-core layout and alignment
+        */
+       struct kvec *vec = (struct kvec *)iov;
+
+       while (len > 0) {
+               if (vec->iov_len) {
+                       int copy = min_t(unsigned int, len, vec->iov_len);
+                       memcpy(kdata, vec->iov_base, copy);
+                       len -= copy;
+                       kdata += copy;
+                       vec->iov_base += copy;
+                       vec->iov_len -= copy;
+               }
+               vec++;
+       }
+
+       return 0;
+}
+
 extern bool disable_ertm;
 
 int l2cap_init_sockets(void);
@@ -872,10 +903,9 @@ struct l2cap_chan *l2cap_chan_create(void);
 void l2cap_chan_close(struct l2cap_chan *chan, int reason);
 int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                       bdaddr_t *dst, u8 dst_type);
-int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
-                                                               u32 priority);
+int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);
 void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
-int l2cap_chan_check_security(struct l2cap_chan *chan);
+int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
 void l2cap_chan_set_defaults(struct l2cap_chan *chan);
 int l2cap_ertm_init(struct l2cap_chan *chan);
 void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
index bcffc9ae0c89bec0bd3237d708e76d0911abfa36..623d5203c5926292d626ff5aa2d5311f3dfcb33b 100644 (file)
@@ -97,6 +97,7 @@ struct mgmt_rp_read_index_list {
 #define MGMT_SETTING_SECURE_CONN       0x00000800
 #define MGMT_SETTING_DEBUG_KEYS                0x00001000
 #define MGMT_SETTING_PRIVACY           0x00002000
+#define MGMT_SETTING_CONFIGURATION     0x00004000
 
 #define MGMT_OP_READ_INFO              0x0004
 #define MGMT_READ_INFO_SIZE            0
@@ -424,6 +425,76 @@ struct mgmt_rp_get_conn_info {
        __s8    max_tx_power;
 } __packed;
 
+#define MGMT_OP_GET_CLOCK_INFO         0x0032
+struct mgmt_cp_get_clock_info {
+       struct mgmt_addr_info addr;
+} __packed;
+#define MGMT_GET_CLOCK_INFO_SIZE       MGMT_ADDR_INFO_SIZE
+struct mgmt_rp_get_clock_info {
+       struct mgmt_addr_info addr;
+       __le32  local_clock;
+       __le32  piconet_clock;
+       __le16  accuracy;
+} __packed;
+
+#define MGMT_OP_ADD_DEVICE             0x0033
+struct mgmt_cp_add_device {
+       struct mgmt_addr_info addr;
+       __u8    action;
+} __packed;
+#define MGMT_ADD_DEVICE_SIZE           (MGMT_ADDR_INFO_SIZE + 1)
+
+#define MGMT_OP_REMOVE_DEVICE          0x0034
+struct mgmt_cp_remove_device {
+       struct mgmt_addr_info addr;
+} __packed;
+#define MGMT_REMOVE_DEVICE_SIZE                MGMT_ADDR_INFO_SIZE
+
+struct mgmt_conn_param {
+       struct mgmt_addr_info addr;
+       __le16 min_interval;
+       __le16 max_interval;
+       __le16 latency;
+       __le16 timeout;
+} __packed;
+
+#define MGMT_OP_LOAD_CONN_PARAM                0x0035
+struct mgmt_cp_load_conn_param {
+       __le16 param_count;
+       struct mgmt_conn_param params[0];
+} __packed;
+#define MGMT_LOAD_CONN_PARAM_SIZE      2
+
+#define MGMT_OP_READ_UNCONF_INDEX_LIST 0x0036
+#define MGMT_READ_UNCONF_INDEX_LIST_SIZE 0
+struct mgmt_rp_read_unconf_index_list {
+       __le16  num_controllers;
+       __le16  index[0];
+} __packed;
+
+#define MGMT_OPTION_EXTERNAL_CONFIG    0x00000001
+#define MGMT_OPTION_PUBLIC_ADDRESS     0x00000002
+
+#define MGMT_OP_READ_CONFIG_INFO       0x0037
+#define MGMT_READ_CONFIG_INFO_SIZE     0
+struct mgmt_rp_read_config_info {
+       __le16  manufacturer;
+       __le32  supported_options;
+       __le32  missing_options;
+} __packed;
+
+#define MGMT_OP_SET_EXTERNAL_CONFIG    0x0038
+struct mgmt_cp_set_external_config {
+       __u8 config;
+} __packed;
+#define MGMT_SET_EXTERNAL_CONFIG_SIZE  1
+
+#define MGMT_OP_SET_PUBLIC_ADDRESS     0x0039
+struct mgmt_cp_set_public_address {
+       bdaddr_t bdaddr;
+} __packed;
+#define MGMT_SET_PUBLIC_ADDRESS_SIZE   6
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
@@ -522,6 +593,7 @@ struct mgmt_ev_auth_failed {
 
 #define MGMT_DEV_FOUND_CONFIRM_NAME    0x01
 #define MGMT_DEV_FOUND_LEGACY_PAIRING  0x02
+#define MGMT_DEV_FOUND_NOT_CONNECTABLE 0x04
 
 #define MGMT_EV_DEVICE_FOUND           0x0012
 struct mgmt_ev_device_found {
@@ -578,3 +650,30 @@ struct mgmt_ev_new_csrk {
        __u8 store_hint;
        struct mgmt_csrk_info key;
 } __packed;
+
+#define MGMT_EV_DEVICE_ADDED           0x001a
+struct mgmt_ev_device_added {
+       struct mgmt_addr_info addr;
+       __u8 action;
+} __packed;
+
+#define MGMT_EV_DEVICE_REMOVED         0x001b
+struct mgmt_ev_device_removed {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_NEW_CONN_PARAM         0x001c
+struct mgmt_ev_new_conn_param {
+       struct mgmt_addr_info addr;
+       __u8 store_hint;
+       __le16 min_interval;
+       __le16 max_interval;
+       __le16 latency;
+       __le16 timeout;
+} __packed;
+
+#define MGMT_EV_UNCONF_INDEX_ADDED     0x001d
+
+#define MGMT_EV_UNCONF_INDEX_REMOVED   0x001e
+
+#define MGMT_EV_NEW_CONFIG_OPTIONS     0x001f
index 2019d1a0996a80d9d2fc9ef29fb33e102f2864bd..f40ddb4264fc3c18fa59e86e24f9c2c1bc9b1522 100644 (file)
 
 /* SCO defaults */
 #define SCO_DEFAULT_MTU                500
-#define SCO_DEFAULT_FLUSH_TO   0xFFFF
-
-#define SCO_CONN_TIMEOUT       (HZ * 40)
-#define SCO_DISCONN_TIMEOUT    (HZ * 2)
-#define SCO_CONN_IDLE_TIMEOUT  (HZ * 60)
 
 /* SCO socket address */
 struct sockaddr_sco {
@@ -51,29 +46,4 @@ struct sco_conninfo {
        __u8  dev_class[3];
 };
 
-/* ---- SCO connections ---- */
-struct sco_conn {
-       struct hci_conn *hcon;
-
-       spinlock_t      lock;
-       struct sock     *sk;
-
-       unsigned int    mtu;
-};
-
-#define sco_conn_lock(c)       spin_lock(&c->lock);
-#define sco_conn_unlock(c)     spin_unlock(&c->lock);
-
-/* ----- SCO socket info ----- */
-#define sco_pi(sk) ((struct sco_pinfo *) sk)
-
-struct sco_pinfo {
-       struct bt_sock  bt;
-       bdaddr_t        src;
-       bdaddr_t        dst;
-       __u32           flags;
-       __u16           setting;
-       struct sco_conn *conn;
-};
-
 #endif /* __SCO_H */
index e46c437944f73e66cb275f4adc345a1f869923c8..0a080c4de2754d0b9fc64ecf54e8dc4bcb477cf3 100644 (file)
@@ -2266,10 +2266,6 @@ struct cfg80211_qos_map {
  *
  * @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant).
  *
- * @set_ringparam: Set tx and rx ring sizes.
- *
- * @get_ringparam: Get tx and rx ring current and maximum sizes.
- *
  * @tdls_mgmt: Transmit a TDLS management frame.
  * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup).
  *
@@ -2278,16 +2274,6 @@ struct cfg80211_qos_map {
  *
  * @set_noack_map: Set the NoAck Map for the TIDs.
  *
- * @get_et_sset_count:  Ethtool API to get string-set count.
- *     See @ethtool_ops.get_sset_count
- *
- * @get_et_stats:  Ethtool API to get a set of u64 stats.
- *     See @ethtool_ops.get_ethtool_stats
- *
- * @get_et_strings:  Ethtool API to get a set of strings to describe stats
- *     and perhaps other supported types of ethtool data-sets.
- *     See @ethtool_ops.get_strings
- *
  * @get_channel: Get the current operating channel for the virtual interface.
  *     For monitor interfaces, it should return %NULL unless there's a single
  *     current monitoring channel.
@@ -2315,7 +2301,12 @@ struct cfg80211_qos_map {
  *     reliability. This operation can not fail.
  * @set_coalesce: Set coalesce parameters.
  *
- * @channel_switch: initiate channel-switch procedure (with CSA)
+ * @channel_switch: initiate channel-switch procedure (with CSA). Driver is
+ *     responsible for veryfing if the switch is possible. Since this is
+ *     inherently tricky driver may decide to disconnect an interface later
+ *     with cfg80211_stop_iface(). This doesn't mean driver can accept
+ *     everything. It should do it's best to verify requests and reject them
+ *     as soon as possible.
  *
  * @set_qos_map: Set QoS mapping information to the driver
  *
@@ -2503,10 +2494,6 @@ struct cfg80211_ops {
        int     (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
        int     (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
 
-       int     (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx);
-       void    (*get_ringparam)(struct wiphy *wiphy,
-                                u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
-
        int     (*sched_scan_start)(struct wiphy *wiphy,
                                struct net_device *dev,
                                struct cfg80211_sched_scan_request *request);
@@ -2518,7 +2505,7 @@ struct cfg80211_ops {
        int     (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
                             const u8 *peer, u8 action_code,  u8 dialog_token,
                             u16 status_code, u32 peer_capability,
-                            const u8 *buf, size_t len);
+                            bool initiator, const u8 *buf, size_t len);
        int     (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
                             const u8 *peer, enum nl80211_tdls_operation oper);
 
@@ -2529,13 +2516,6 @@ struct cfg80211_ops {
                                  struct net_device *dev,
                                  u16 noack_map);
 
-       int     (*get_et_sset_count)(struct wiphy *wiphy,
-                                    struct net_device *dev, int sset);
-       void    (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev,
-                               struct ethtool_stats *stats, u64 *data);
-       void    (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev,
-                                 u32 sset, u8 *data);
-
        int     (*get_channel)(struct wiphy *wiphy,
                               struct wireless_dev *wdev,
                               struct cfg80211_chan_def *chandef);
@@ -4843,6 +4823,10 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
  */
 void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
 
+
+/* ethtool helper */
+void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
index 421b6ecb4b2cdee892379212f9d252f3f3ecffbc..dae2e24616e16bf3051af3dd31414f91f4220af9 100644 (file)
@@ -754,20 +754,25 @@ struct ieee80211_tx_info {
 };
 
 /**
- * struct ieee80211_sched_scan_ies - scheduled scan IEs
+ * struct ieee80211_scan_ies - descriptors for different blocks of IEs
  *
- * This structure is used to pass the appropriate IEs to be used in scheduled
- * scans for all bands.  It contains both the IEs passed from the userspace
+ * This structure is used to point to different blocks of IEs in HW scan
+ * and scheduled scan. These blocks contain the IEs passed by userspace
  * and the ones generated by mac80211.
  *
- * @ie: array with the IEs for each supported band
- * @len: array with the total length of the IEs for each band
+ * @ies: pointers to band specific IEs.
+ * @len: lengths of band_specific IEs.
+ * @common_ies: IEs for all bands (especially vendor specific ones)
+ * @common_ie_len: length of the common_ies
  */
-struct ieee80211_sched_scan_ies {
-       u8 *ie[IEEE80211_NUM_BANDS];
+struct ieee80211_scan_ies {
+       const u8 *ies[IEEE80211_NUM_BANDS];
        size_t len[IEEE80211_NUM_BANDS];
+       const u8 *common_ies;
+       size_t common_ie_len;
 };
 
+
 static inline struct ieee80211_tx_info *IEEE80211_SKB_CB(struct sk_buff *skb)
 {
        return (struct ieee80211_tx_info *)skb->cb;
@@ -1601,11 +1606,8 @@ struct ieee80211_tx_control {
  *     is not enabled the default action is to disconnect when getting the
  *     CSA frame.
  *
- * @IEEE80211_HW_CHANGE_RUNNING_CHANCTX: The hardware can change a
- *     channel context on-the-fly.  This is needed for channel switch
- *     on single-channel hardware.  It can also be used as an
- *     optimization in certain channel switch cases with
- *     multi-channel.
+ * @IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
+ *     in one command, mac80211 doesn't have to run separate scans per band.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
@@ -1637,7 +1639,8 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_TIMING_BEACON_ONLY                 = 1<<26,
        IEEE80211_HW_SUPPORTS_HT_CCK_RATES              = 1<<27,
        IEEE80211_HW_CHANCTX_STA_CSA                    = 1<<28,
-       IEEE80211_HW_CHANGE_RUNNING_CHANCTX             = 1<<29,
+       /* bit 29 unused */
+       IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS           = 1<<30,
 };
 
 /**
@@ -1763,6 +1766,19 @@ struct ieee80211_hw {
        const struct ieee80211_cipher_scheme *cipher_schemes;
 };
 
+/**
+ * struct ieee80211_scan_request - hw scan request
+ *
+ * @ies: pointers different parts of IEs (in req.ie)
+ * @req: cfg80211 request.
+ */
+struct ieee80211_scan_request {
+       struct ieee80211_scan_ies ies;
+
+       /* Keep last */
+       struct cfg80211_scan_request req;
+};
+
 /**
  * wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy
  *
@@ -2764,6 +2780,15 @@ enum ieee80211_roc_type {
  *     mac80211 will transmit the frame right away.
  *     The callback is optional and can (should!) sleep.
  *
+ * @mgd_protect_tdls_discover: Protect a TDLS discovery session. After sending
+ *     a TDLS discovery-request, we expect a reply to arrive on the AP's
+ *     channel. We must stay on the channel (no PSM, scan, etc.), since a TDLS
+ *     setup-response is a direct packet not buffered by the AP.
+ *     mac80211 will call this function just before the transmission of a TDLS
+ *     discovery-request. The recommended period of protection is at least
+ *     2 * (DTIM period).
+ *     The callback is optional and can sleep.
+ *
  * @add_chanctx: Notifies device driver about new channel context creation.
  * @remove_chanctx: Notifies device driver about channel context destruction.
  * @change_chanctx: Notifies device driver about channel context changes that
@@ -2865,13 +2890,13 @@ struct ieee80211_ops {
        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);
+                      struct ieee80211_scan_request *req);
        void (*cancel_hw_scan)(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif);
        int (*sched_scan_start)(struct ieee80211_hw *hw,
                                struct ieee80211_vif *vif,
                                struct cfg80211_sched_scan_request *req,
-                               struct ieee80211_sched_scan_ies *ies);
+                               struct ieee80211_scan_ies *ies);
        int (*sched_scan_stop)(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif);
        void (*sw_scan_start)(struct ieee80211_hw *hw);
@@ -2981,6 +3006,9 @@ struct ieee80211_ops {
        void    (*mgd_prepare_tx)(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif);
 
+       void    (*mgd_protect_tdls_discover)(struct ieee80211_hw *hw,
+                                            struct ieee80211_vif *vif);
+
        int (*add_chanctx)(struct ieee80211_hw *hw,
                           struct ieee80211_chanctx_conf *ctx);
        void (*remove_chanctx)(struct ieee80211_hw *hw,
@@ -4524,6 +4552,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
  */
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn);
 
+/**
+ * ieee80211_start_rx_ba_session_offl - start a Rx BA session
+ *
+ * Some device drivers may offload part of the Rx aggregation flow including
+ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
+ * reordering.
+ *
+ * Create structures responsible for reordering so device drivers may call here
+ * when they complete AddBa negotiation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @addr: station mac address
+ * @tid: the rx tid
+ */
+void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                       const u8 *addr, u16 tid);
+
+/**
+ * ieee80211_stop_rx_ba_session_offl - stop a Rx BA session
+ *
+ * Some device drivers may offload part of the Rx aggregation flow including
+ * AddBa/DelBa negotiation but may otherwise be incapable of full Rx
+ * reordering.
+ *
+ * Destroy structures responsible for reordering so device drivers may call here
+ * when they complete DelBa negotiation.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback
+ * @addr: station mac address
+ * @tid: the rx tid
+ */
+void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                      const u8 *addr, u16 tid);
+
 /* Rate control API */
 
 /**
@@ -4815,4 +4877,17 @@ int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
  */
 void ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf);
 
+/**
+ * ieee80211_tdls_oper - request userspace to perform a TDLS operation
+ * @vif: virtual interface
+ * @peer: the peer's destination address
+ * @oper: the requested TDLS operation
+ * @reason_code: reason code for the operation, valid for TDLS teardown
+ * @gfp: allocation flags
+ *
+ * See cfg80211_tdls_oper_request().
+ */
+void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
+                                enum nl80211_tdls_operation oper,
+                                u16 reason_code, gfp_t gfp);
 #endif /* MAC80211_H */
index be9519b52bb10edef5e5be12ddd3ff2065d706ac..f1db15b9c041ccad0f13a4206e73245b59e7c236 100644 (file)
@@ -1591,6 +1591,9 @@ enum nl80211_commands {
  *     creation then the new interface will be owned by the netlink socket
  *     that created it and will be destroyed when the socket is closed
  *
+ * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
+ *     the TDLS link initiator.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1931,6 +1934,8 @@ enum nl80211_attrs {
        NL80211_ATTR_CSA_C_OFFSETS_TX,
        NL80211_ATTR_MAX_CSA_COUNTERS,
 
+       NL80211_ATTR_TDLS_INITIATOR,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig
new file mode 100644 (file)
index 0000000..028a5c6
--- /dev/null
@@ -0,0 +1,6 @@
+config 6LOWPAN
+       bool "6LoWPAN Support"
+       depends on IPV6
+       ---help---
+         This enables IPv6 over Low power Wireless Personal Area Network -
+         "6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
new file mode 100644 (file)
index 0000000..415886b
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_6LOWPAN) := 6lowpan.o
+
+6lowpan-y := iphc.o
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
new file mode 100644 (file)
index 0000000..211b568
--- /dev/null
@@ -0,0 +1,801 @@
+/*
+ * Copyright 2011, Siemens AG
+ * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ */
+
+/*
+ * Based on patches from Jon Smirl <jonsmirl@gmail.com>
+ * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Jon's code is based on 6lowpan implementation for Contiki which is:
+ * Copyright (c) 2008, Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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/bitops.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <net/6lowpan.h>
+#include <net/ipv6.h>
+#include <net/af_ieee802154.h>
+
+/*
+ * Uncompress address function for source and
+ * destination address(non-multicast).
+ *
+ * address_mode is sam value or dam value.
+ */
+static int uncompress_addr(struct sk_buff *skb,
+                               struct in6_addr *ipaddr, const u8 address_mode,
+                               const u8 *lladdr, const u8 addr_type,
+                               const u8 addr_len)
+{
+       bool fail;
+
+       switch (address_mode) {
+       case LOWPAN_IPHC_ADDR_00:
+               /* for global link addresses */
+               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
+               break;
+       case LOWPAN_IPHC_ADDR_01:
+               /* fe:80::XXXX:XXXX:XXXX:XXXX */
+               ipaddr->s6_addr[0] = 0xFE;
+               ipaddr->s6_addr[1] = 0x80;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+               break;
+       case LOWPAN_IPHC_ADDR_02:
+               /* fe:80::ff:fe00:XXXX */
+               ipaddr->s6_addr[0] = 0xFE;
+               ipaddr->s6_addr[1] = 0x80;
+               ipaddr->s6_addr[11] = 0xFF;
+               ipaddr->s6_addr[12] = 0xFE;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+               break;
+       case LOWPAN_IPHC_ADDR_03:
+               fail = false;
+               switch (addr_type) {
+               case IEEE802154_ADDR_LONG:
+                       /* fe:80::XXXX:XXXX:XXXX:XXXX
+                        *        \_________________/
+                        *              hwaddr
+                        */
+                       ipaddr->s6_addr[0] = 0xFE;
+                       ipaddr->s6_addr[1] = 0x80;
+                       memcpy(&ipaddr->s6_addr[8], lladdr, addr_len);
+                       /* second bit-flip (Universe/Local)
+                        * is done according RFC2464
+                        */
+                       ipaddr->s6_addr[8] ^= 0x02;
+                       break;
+               case IEEE802154_ADDR_SHORT:
+                       /* fe:80::ff:fe00:XXXX
+                        *                \__/
+                        *             short_addr
+                        *
+                        * Universe/Local bit is zero.
+                        */
+                       ipaddr->s6_addr[0] = 0xFE;
+                       ipaddr->s6_addr[1] = 0x80;
+                       ipaddr->s6_addr[11] = 0xFF;
+                       ipaddr->s6_addr[12] = 0xFE;
+                       ipaddr->s6_addr16[7] = htons(*((u16 *)lladdr));
+                       break;
+               default:
+                       pr_debug("Invalid addr_type set\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               pr_debug("Invalid address mode value: 0x%x\n", address_mode);
+               return -EINVAL;
+       }
+
+       if (fail) {
+               pr_debug("Failed to fetch skb data\n");
+               return -EIO;
+       }
+
+       raw_dump_inline(NULL, "Reconstructed ipv6 addr is",
+                       ipaddr->s6_addr, 16);
+
+       return 0;
+}
+
+/*
+ * Uncompress address function for source context
+ * based address(non-multicast).
+ */
+static int uncompress_context_based_src_addr(struct sk_buff *skb,
+                                               struct in6_addr *ipaddr,
+                                               const u8 sam)
+{
+       switch (sam) {
+       case LOWPAN_IPHC_ADDR_00:
+               /* unspec address ::
+                * Do nothing, address is already ::
+                */
+               break;
+       case LOWPAN_IPHC_ADDR_01:
+               /* TODO */
+       case LOWPAN_IPHC_ADDR_02:
+               /* TODO */
+       case LOWPAN_IPHC_ADDR_03:
+               /* TODO */
+               netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+               return -EINVAL;
+       default:
+               pr_debug("Invalid sam value: 0x%x\n", sam);
+               return -EINVAL;
+       }
+
+       raw_dump_inline(NULL,
+                       "Reconstructed context based ipv6 src addr is",
+                       ipaddr->s6_addr, 16);
+
+       return 0;
+}
+
+static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
+               struct net_device *dev, skb_delivery_cb deliver_skb)
+{
+       struct sk_buff *new;
+       int stat;
+
+       new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
+                                                               GFP_ATOMIC);
+       kfree_skb(skb);
+
+       if (!new)
+               return -ENOMEM;
+
+       skb_push(new, sizeof(struct ipv6hdr));
+       skb_reset_network_header(new);
+       skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
+
+       new->protocol = htons(ETH_P_IPV6);
+       new->pkt_type = PACKET_HOST;
+       new->dev = dev;
+
+       raw_dump_table(__func__, "raw skb data dump before receiving",
+                       new->data, new->len);
+
+       stat = deliver_skb(new, dev);
+
+       kfree_skb(new);
+
+       return stat;
+}
+
+/* Uncompress function for multicast destination address,
+ * when M bit is set.
+ */
+static int
+lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
+               struct in6_addr *ipaddr,
+               const u8 dam)
+{
+       bool fail;
+
+       switch (dam) {
+       case LOWPAN_IPHC_DAM_00:
+               /* 00:  128 bits.  The full address
+                * is carried in-line.
+                */
+               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
+               break;
+       case LOWPAN_IPHC_DAM_01:
+               /* 01:  48 bits.  The address takes
+                * the form ffXX::00XX:XXXX:XXXX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[11], 5);
+               break;
+       case LOWPAN_IPHC_DAM_10:
+               /* 10:  32 bits.  The address takes
+                * the form ffXX::00XX:XXXX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[13], 3);
+               break;
+       case LOWPAN_IPHC_DAM_11:
+               /* 11:  8 bits.  The address takes
+                * the form ff02::00XX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               ipaddr->s6_addr[1] = 0x02;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[15], 1);
+               break;
+       default:
+               pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+               return -EINVAL;
+       }
+
+       if (fail) {
+               pr_debug("Failed to fetch skb data\n");
+               return -EIO;
+       }
+
+       raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is",
+                               ipaddr->s6_addr, 16);
+
+       return 0;
+}
+
+static int
+uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
+{
+       bool fail;
+       u8 tmp = 0, val = 0;
+
+       if (!uh)
+               goto err;
+
+       fail = lowpan_fetch_skb(skb, &tmp, 1);
+
+       if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
+               pr_debug("UDP header uncompression\n");
+               switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
+               case LOWPAN_NHC_UDP_CS_P_00:
+                       fail |= lowpan_fetch_skb(skb, &uh->source, 2);
+                       fail |= lowpan_fetch_skb(skb, &uh->dest, 2);
+                       break;
+               case LOWPAN_NHC_UDP_CS_P_01:
+                       fail |= lowpan_fetch_skb(skb, &uh->source, 2);
+                       fail |= lowpan_fetch_skb(skb, &val, 1);
+                       uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+                       break;
+               case LOWPAN_NHC_UDP_CS_P_10:
+                       fail |= lowpan_fetch_skb(skb, &val, 1);
+                       uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+                       fail |= lowpan_fetch_skb(skb, &uh->dest, 2);
+                       break;
+               case LOWPAN_NHC_UDP_CS_P_11:
+                       fail |= lowpan_fetch_skb(skb, &val, 1);
+                       uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT +
+                                          (val >> 4));
+                       uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT +
+                                        (val & 0x0f));
+                       break;
+               default:
+                       pr_debug("ERROR: unknown UDP format\n");
+                       goto err;
+                       break;
+               }
+
+               pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
+                        ntohs(uh->source), ntohs(uh->dest));
+
+               /* checksum */
+               if (tmp & LOWPAN_NHC_UDP_CS_C) {
+                       pr_debug_ratelimited("checksum elided currently not supported\n");
+                       goto err;
+               } else {
+                       fail |= lowpan_fetch_skb(skb, &uh->check, 2);
+               }
+
+               /*
+                * UDP lenght needs to be infered from the lower layers
+                * here, we obtain the hint from the remaining size of the
+                * frame
+                */
+               uh->len = htons(skb->len + sizeof(struct udphdr));
+               pr_debug("uncompressed UDP length: src = %d", ntohs(uh->len));
+       } else {
+               pr_debug("ERROR: unsupported NH format\n");
+               goto err;
+       }
+
+       if (fail)
+               goto err;
+
+       return 0;
+err:
+       return -EINVAL;
+}
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
+
+int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
+               const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
+               const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
+               u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb)
+{
+       struct ipv6hdr hdr = {};
+       u8 tmp, num_context = 0;
+       int err;
+
+       raw_dump_table(__func__, "raw skb data dump uncompressed",
+                               skb->data, skb->len);
+
+       /* another if the CID flag is set */
+       if (iphc1 & LOWPAN_IPHC_CID) {
+               pr_debug("CID flag is set, increase header with one\n");
+               if (lowpan_fetch_skb_u8(skb, &num_context))
+                       goto drop;
+       }
+
+       hdr.version = 6;
+
+       /* Traffic Class and Flow Label */
+       switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
+       /*
+        * Traffic Class and FLow Label carried in-line
+        * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+        */
+       case 0: /* 00b */
+               if (lowpan_fetch_skb_u8(skb, &tmp))
+                       goto drop;
+
+               memcpy(&hdr.flow_lbl, &skb->data[0], 3);
+               skb_pull(skb, 3);
+               hdr.priority = ((tmp >> 2) & 0x0f);
+               hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
+                                       (hdr.flow_lbl[0] & 0x0f);
+               break;
+       /*
+        * Traffic class carried in-line
+        * ECN + DSCP (1 byte), Flow Label is elided
+        */
+       case 2: /* 10b */
+               if (lowpan_fetch_skb_u8(skb, &tmp))
+                       goto drop;
+
+               hdr.priority = ((tmp >> 2) & 0x0f);
+               hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
+               break;
+       /*
+        * Flow Label carried in-line
+        * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
+        */
+       case 1: /* 01b */
+               if (lowpan_fetch_skb_u8(skb, &tmp))
+                       goto drop;
+
+               hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
+               memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
+               skb_pull(skb, 2);
+               break;
+       /* Traffic Class and Flow Label are elided */
+       case 3: /* 11b */
+               break;
+       default:
+               break;
+       }
+
+       /* Next Header */
+       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+               /* Next header is carried inline */
+               if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr)))
+                       goto drop;
+
+               pr_debug("NH flag is set, next header carried inline: %02x\n",
+                        hdr.nexthdr);
+       }
+
+       /* Hop Limit */
+       if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
+               hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
+       else {
+               if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit)))
+                       goto drop;
+       }
+
+       /* Extract SAM to the tmp variable */
+       tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
+
+       if (iphc1 & LOWPAN_IPHC_SAC) {
+               /* Source address context based uncompression */
+               pr_debug("SAC bit is set. Handle context based source address.\n");
+               err = uncompress_context_based_src_addr(
+                               skb, &hdr.saddr, tmp);
+       } else {
+               /* Source address uncompression */
+               pr_debug("source address stateless compression\n");
+               err = uncompress_addr(skb, &hdr.saddr, tmp, saddr,
+                                       saddr_type, saddr_len);
+       }
+
+       /* Check on error of previous branch */
+       if (err)
+               goto drop;
+
+       /* Extract DAM to the tmp variable */
+       tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
+
+       /* check for Multicast Compression */
+       if (iphc1 & LOWPAN_IPHC_M) {
+               if (iphc1 & LOWPAN_IPHC_DAC) {
+                       pr_debug("dest: context-based mcast compression\n");
+                       /* TODO: implement this */
+               } else {
+                       err = lowpan_uncompress_multicast_daddr(
+                                               skb, &hdr.daddr, tmp);
+                       if (err)
+                               goto drop;
+               }
+       } else {
+               err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
+                                       daddr_type, daddr_len);
+               pr_debug("dest: stateless compression mode %d dest %pI6c\n",
+                       tmp, &hdr.daddr);
+               if (err)
+                       goto drop;
+       }
+
+       /* UDP data uncompression */
+       if (iphc0 & LOWPAN_IPHC_NH_C) {
+               struct udphdr uh;
+               struct sk_buff *new;
+               if (uncompress_udp_header(skb, &uh))
+                       goto drop;
+
+               /*
+                * replace the compressed UDP head by the uncompressed UDP
+                * header
+                */
+               new = skb_copy_expand(skb, sizeof(struct udphdr),
+                                     skb_tailroom(skb), GFP_ATOMIC);
+               kfree_skb(skb);
+
+               if (!new)
+                       return -ENOMEM;
+
+               skb = new;
+
+               skb_push(skb, sizeof(struct udphdr));
+               skb_reset_transport_header(skb);
+               skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
+
+               raw_dump_table(__func__, "raw UDP header dump",
+                                     (u8 *)&uh, sizeof(uh));
+
+               hdr.nexthdr = UIP_PROTO_UDP;
+       }
+
+       hdr.payload_len = htons(skb->len);
+
+       pr_debug("skb headroom size = %d, data length = %d\n",
+                skb_headroom(skb), skb->len);
+
+       pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n\t"
+                "nexthdr = 0x%02x\n\thop_lim = %d\n\tdest    = %pI6c\n",
+               hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
+               hdr.hop_limit, &hdr.daddr);
+
+       raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
+                                                       sizeof(hdr));
+
+       return skb_deliver(skb, &hdr, dev, deliver_skb);
+
+drop:
+       kfree_skb(skb);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(lowpan_process_data);
+
+static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift,
+                               const struct in6_addr *ipaddr,
+                               const unsigned char *lladdr)
+{
+       u8 val = 0;
+
+       if (is_addr_mac_addr_based(ipaddr, lladdr)) {
+               val = 3; /* 0-bits */
+               pr_debug("address compression 0 bits\n");
+       } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
+               /* compress IID to 16 bits xxxx::XXXX */
+               memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2);
+               *hc06_ptr += 2;
+               val = 2; /* 16-bits */
+               raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
+                       *hc06_ptr - 2, 2);
+       } else {
+               /* do not compress IID => xxxx::IID */
+               memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
+               *hc06_ptr += 8;
+               val = 1; /* 64-bits */
+               raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
+                       *hc06_ptr - 8, 8);
+       }
+
+       return rol8(val, shift);
+}
+
+static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
+{
+       struct udphdr *uh = udp_hdr(skb);
+       u8 tmp;
+
+       if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+            LOWPAN_NHC_UDP_4BIT_PORT) &&
+           ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+            LOWPAN_NHC_UDP_4BIT_PORT)) {
+               pr_debug("UDP header: both ports compression to 4 bits\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_11;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* source and destination port */
+               tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
+                     ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+       } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+                       LOWPAN_NHC_UDP_8BIT_PORT) {
+               pr_debug("UDP header: remove 8 bits of dest\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_01;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source));
+               /* destination port */
+               tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+       } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+                       LOWPAN_NHC_UDP_8BIT_PORT) {
+               pr_debug("UDP header: remove 8 bits of source\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_10;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* destination port */
+               lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest));
+       } else {
+               pr_debug("UDP header: can't compress\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_00;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source));
+               /* destination port */
+               lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest));
+       }
+
+       /* checksum is always inline */
+       lowpan_push_hc_data(hc06_ptr, &uh->check, sizeof(uh->check));
+
+       /* skip the UDP header */
+       skb_pull(skb, sizeof(struct udphdr));
+}
+
+int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
+                       unsigned short type, const void *_daddr,
+                       const void *_saddr, unsigned int len)
+{
+       u8 tmp, iphc0, iphc1, *hc06_ptr;
+       struct ipv6hdr *hdr;
+       u8 head[100] = {};
+
+       if (type != ETH_P_IPV6)
+               return -EINVAL;
+
+       hdr = ipv6_hdr(skb);
+       hc06_ptr = head + 2;
+
+       pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n"
+                "\tnexthdr = 0x%02x\n\thop_lim = %d\n\tdest    = %pI6c\n",
+               hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
+               hdr->hop_limit, &hdr->daddr);
+
+       raw_dump_table(__func__, "raw skb network header dump",
+               skb_network_header(skb), sizeof(struct ipv6hdr));
+
+       /*
+        * As we copy some bit-length fields, in the IPHC encoding bytes,
+        * we sometimes use |=
+        * If the field is 0, and the current bit value in memory is 1,
+        * this does not work. We therefore reset the IPHC encoding here
+        */
+       iphc0 = LOWPAN_DISPATCH_IPHC;
+       iphc1 = 0;
+
+       /* TODO: context lookup */
+
+       raw_dump_inline(__func__, "saddr",
+                       (unsigned char *)_saddr, IEEE802154_ADDR_LEN);
+       raw_dump_inline(__func__, "daddr",
+                       (unsigned char *)_daddr, IEEE802154_ADDR_LEN);
+
+       raw_dump_table(__func__,
+                       "sending raw skb network uncompressed packet",
+                       skb->data, skb->len);
+
+       /*
+        * Traffic class, flow label
+        * If flow label is 0, compress it. If traffic class is 0, compress it
+        * We have to process both in the same time as the offset of traffic
+        * class depends on the presence of version and flow label
+        */
+
+       /* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */
+       tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
+       tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
+
+       if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
+            (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
+               /* flow label can be compressed */
+               iphc0 |= LOWPAN_IPHC_FL_C;
+               if ((hdr->priority == 0) &&
+                  ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+                       /* compress (elide) all */
+                       iphc0 |= LOWPAN_IPHC_TC_C;
+               } else {
+                       /* compress only the flow label */
+                       *hc06_ptr = tmp;
+                       hc06_ptr += 1;
+               }
+       } else {
+               /* Flow label cannot be compressed */
+               if ((hdr->priority == 0) &&
+                  ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+                       /* compress only traffic class */
+                       iphc0 |= LOWPAN_IPHC_TC_C;
+                       *hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
+                       memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2);
+                       hc06_ptr += 3;
+               } else {
+                       /* compress nothing */
+                       memcpy(hc06_ptr, hdr, 4);
+                       /* replace the top byte with new ECN | DSCP format */
+                       *hc06_ptr = tmp;
+                       hc06_ptr += 4;
+               }
+       }
+
+       /* NOTE: payload length is always compressed */
+
+       /* Next Header is compress if UDP */
+       if (hdr->nexthdr == UIP_PROTO_UDP)
+               iphc0 |= LOWPAN_IPHC_NH_C;
+
+       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+               *hc06_ptr = hdr->nexthdr;
+               hc06_ptr += 1;
+       }
+
+       /*
+        * Hop limit
+        * if 1:   compress, encoding is 01
+        * if 64:  compress, encoding is 10
+        * if 255: compress, encoding is 11
+        * else do not compress
+        */
+       switch (hdr->hop_limit) {
+       case 1:
+               iphc0 |= LOWPAN_IPHC_TTL_1;
+               break;
+       case 64:
+               iphc0 |= LOWPAN_IPHC_TTL_64;
+               break;
+       case 255:
+               iphc0 |= LOWPAN_IPHC_TTL_255;
+               break;
+       default:
+               *hc06_ptr = hdr->hop_limit;
+               hc06_ptr += 1;
+               break;
+       }
+
+       /* source address compression */
+       if (is_addr_unspecified(&hdr->saddr)) {
+               pr_debug("source address is unspecified, setting SAC\n");
+               iphc1 |= LOWPAN_IPHC_SAC;
+       /* TODO: context lookup */
+       } else if (is_addr_link_local(&hdr->saddr)) {
+               iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+                               LOWPAN_IPHC_SAM_BIT, &hdr->saddr, _saddr);
+               pr_debug("source address unicast link-local %pI6c "
+                       "iphc1 0x%02x\n", &hdr->saddr, iphc1);
+       } else {
+               pr_debug("send the full source address\n");
+               memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16);
+               hc06_ptr += 16;
+       }
+
+       /* destination address compression */
+       if (is_addr_mcast(&hdr->daddr)) {
+               pr_debug("destination address is multicast: ");
+               iphc1 |= LOWPAN_IPHC_M;
+               if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
+                       pr_debug("compressed to 1 octet\n");
+                       iphc1 |= LOWPAN_IPHC_DAM_11;
+                       /* use last byte */
+                       *hc06_ptr = hdr->daddr.s6_addr[15];
+                       hc06_ptr += 1;
+               } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
+                       pr_debug("compressed to 4 octets\n");
+                       iphc1 |= LOWPAN_IPHC_DAM_10;
+                       /* second byte + the last three */
+                       *hc06_ptr = hdr->daddr.s6_addr[1];
+                       memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3);
+                       hc06_ptr += 4;
+               } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
+                       pr_debug("compressed to 6 octets\n");
+                       iphc1 |= LOWPAN_IPHC_DAM_01;
+                       /* second byte + the last five */
+                       *hc06_ptr = hdr->daddr.s6_addr[1];
+                       memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5);
+                       hc06_ptr += 6;
+               } else {
+                       pr_debug("using full address\n");
+                       iphc1 |= LOWPAN_IPHC_DAM_00;
+                       memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16);
+                       hc06_ptr += 16;
+               }
+       } else {
+               /* TODO: context lookup */
+               if (is_addr_link_local(&hdr->daddr)) {
+                       iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+                               LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr);
+                       pr_debug("dest address unicast link-local %pI6c "
+                               "iphc1 0x%02x\n", &hdr->daddr, iphc1);
+               } else {
+                       pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
+                       memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16);
+                       hc06_ptr += 16;
+               }
+       }
+
+       /* UDP header compression */
+       if (hdr->nexthdr == UIP_PROTO_UDP)
+               compress_udp_header(&hc06_ptr, skb);
+
+       head[0] = iphc0;
+       head[1] = iphc1;
+
+       skb_pull(skb, sizeof(struct ipv6hdr));
+       skb_reset_transport_header(skb);
+       memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
+       skb_reset_network_header(skb);
+
+       pr_debug("header len %d skb %u\n", (int)(hc06_ptr - head), skb->len);
+
+       raw_dump_table(__func__, "raw skb data dump compressed",
+                               skb->data, skb->len);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(lowpan_header_compress);
+
+MODULE_LICENSE("GPL");
index d92afe4204d9c4f55be1017abde876f90c54fde9..4051fdfa43677ccf7142244c97e2e383515e77e8 100644 (file)
@@ -214,6 +214,7 @@ source "drivers/net/appletalk/Kconfig"
 source "net/x25/Kconfig"
 source "net/lapb/Kconfig"
 source "net/phonet/Kconfig"
+source "net/6lowpan/Kconfig"
 source "net/ieee802154/Kconfig"
 source "net/mac802154/Kconfig"
 source "net/sched/Kconfig"
index cbbbe6d657caf15e670c46b8999872bf360ac801..7ed1970074b07bfcf0803f468f62efba7a9018ac 100644 (file)
@@ -57,7 +57,8 @@ obj-$(CONFIG_CAIF)            += caif/
 ifneq ($(CONFIG_DCB),)
 obj-y                          += dcb/
 endif
-obj-y                          += ieee802154/
+obj-$(CONFIG_6LOWPAN)          += 6lowpan/
+obj-$(CONFIG_IEEE802154)       += ieee802154/
 obj-$(CONFIG_MAC802154)                += mac802154/
 
 ifeq ($(CONFIG_NET),y)
index 8796ffa08b43b4f57ae1485f4a5867c0f532807c..5a7f81df603c482bda59685f8c8fdce8b962fb38 100644 (file)
@@ -1,5 +1,5 @@
 /*
-   Copyright (c) 2013 Intel Corp.
+   Copyright (c) 2013-2014 Intel Corp.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,8 @@
 #include <linux/if_arp.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
 
 #include <net/ipv6.h>
 #include <net/ip6_route.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
 
-#include "6lowpan.h"
-
 #include <net/6lowpan.h> /* for the compression support */
 
+#define VERSION "0.1"
+
+static struct dentry *lowpan_psm_debugfs;
+static struct dentry *lowpan_control_debugfs;
+
 #define IFACE_NAME_TEMPLATE "bt%d"
 #define EUI64_ADDR_LEN 8
 
 struct skb_cb {
        struct in6_addr addr;
-       struct l2cap_conn *conn;
+       struct l2cap_chan *chan;
+       int status;
 };
 #define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
 
@@ -48,9 +54,19 @@ struct skb_cb {
 static LIST_HEAD(bt_6lowpan_devices);
 static DEFINE_RWLOCK(devices_lock);
 
+/* If psm is set to 0 (default value), then 6lowpan is disabled.
+ * Other values are used to indicate a Protocol Service Multiplexer
+ * value for 6lowpan.
+ */
+static u16 psm_6lowpan;
+
+/* We are listening incoming connections via this channel
+ */
+static struct l2cap_chan *listen_chan;
+
 struct lowpan_peer {
        struct list_head list;
-       struct l2cap_conn *conn;
+       struct l2cap_chan *chan;
 
        /* peer addresses in various formats */
        unsigned char eui64_addr[EUI64_ADDR_LEN];
@@ -84,6 +100,8 @@ static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
 {
        list_del(&peer->list);
 
+       module_put(THIS_MODULE);
+
        if (atomic_dec_and_test(&dev->peer_count)) {
                BT_DBG("last peer");
                return true;
@@ -101,13 +119,26 @@ static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev,
               ba, type);
 
        list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
-               BT_DBG("addr %pMR type %d",
-                      &peer->conn->hcon->dst, peer->conn->hcon->dst_type);
+               BT_DBG("dst addr %pMR dst type %d",
+                      &peer->chan->dst, peer->chan->dst_type);
 
-               if (bacmp(&peer->conn->hcon->dst, ba))
+               if (bacmp(&peer->chan->dst, ba))
                        continue;
 
-               if (type == peer->conn->hcon->dst_type)
+               if (type == peer->chan->dst_type)
+                       return peer;
+       }
+
+       return NULL;
+}
+
+static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
+                                                  struct l2cap_chan *chan)
+{
+       struct lowpan_peer *peer, *tmp;
+
+       list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+               if (peer->chan == chan)
                        return peer;
        }
 
@@ -120,7 +151,7 @@ static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
        struct lowpan_peer *peer, *tmp;
 
        list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
-               if (peer->conn == conn)
+               if (peer->chan->conn == conn)
                        return peer;
        }
 
@@ -176,16 +207,16 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
                return -ENOMEM;
 
        ret = netif_rx(skb_cp);
-
-       BT_DBG("receive skb %d", ret);
-       if (ret < 0)
+       if (ret < 0) {
+               BT_DBG("receive skb %d", ret);
                return NET_RX_DROP;
+       }
 
        return ret;
 }
 
 static int process_data(struct sk_buff *skb, struct net_device *netdev,
-                       struct l2cap_conn *conn)
+                       struct l2cap_chan *chan)
 {
        const u8 *saddr, *daddr;
        u8 iphc0, iphc1;
@@ -196,7 +227,7 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev,
        dev = lowpan_dev(netdev);
 
        read_lock_irqsave(&devices_lock, flags);
-       peer = peer_lookup_conn(dev, conn);
+       peer = peer_lookup_chan(dev, chan);
        read_unlock_irqrestore(&devices_lock, flags);
        if (!peer)
                goto drop;
@@ -225,7 +256,7 @@ drop:
 }
 
 static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
-                   struct l2cap_conn *conn)
+                   struct l2cap_chan *chan)
 {
        struct sk_buff *local_skb;
        int ret;
@@ -269,7 +300,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
                        if (!local_skb)
                                goto drop;
 
-                       ret = process_data(local_skb, dev, conn);
+                       ret = process_data(local_skb, dev, chan);
                        if (ret != NET_RX_SUCCESS)
                                goto drop;
 
@@ -286,147 +317,39 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
        return NET_RX_SUCCESS;
 
 drop:
+       dev->stats.rx_dropped++;
        kfree_skb(skb);
        return NET_RX_DROP;
 }
 
 /* Packet from BT LE device */
-int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
 {
        struct lowpan_dev *dev;
        struct lowpan_peer *peer;
        int err;
 
-       peer = lookup_peer(conn);
+       peer = lookup_peer(chan->conn);
        if (!peer)
                return -ENOENT;
 
-       dev = lookup_dev(conn);
+       dev = lookup_dev(chan->conn);
        if (!dev || !dev->netdev)
                return -ENOENT;
 
-       err = recv_pkt(skb, dev->netdev, conn);
-       BT_DBG("recv pkt %d", err);
-
-       return err;
-}
-
-static inline int skbuff_copy(void *msg, int len, int count, int mtu,
-                             struct sk_buff *skb, struct net_device *dev)
-{
-       struct sk_buff **frag;
-       int sent = 0;
-
-       memcpy(skb_put(skb, count), msg, count);
-
-       sent += count;
-       msg  += count;
-       len  -= count;
-
-       dev->stats.tx_bytes += count;
-       dev->stats.tx_packets++;
-
-       raw_dump_table(__func__, "Sending", skb->data, skb->len);
-
-       /* Continuation fragments (no L2CAP header) */
-       frag = &skb_shinfo(skb)->frag_list;
-       while (len > 0) {
-               struct sk_buff *tmp;
-
-               count = min_t(unsigned int, mtu, len);
-
-               tmp = bt_skb_alloc(count, GFP_ATOMIC);
-               if (!tmp)
-                       return -ENOMEM;
-
-               *frag = tmp;
-
-               memcpy(skb_put(*frag, count), msg, count);
-
-               raw_dump_table(__func__, "Sending fragment",
-                              (*frag)->data, count);
-
-               (*frag)->priority = skb->priority;
-
-               sent += count;
-               msg  += count;
-               len  -= count;
-
-               skb->len += (*frag)->len;
-               skb->data_len += (*frag)->len;
-
-               frag = &(*frag)->next;
-
-               dev->stats.tx_bytes += count;
-               dev->stats.tx_packets++;
+       err = recv_pkt(skb, dev->netdev, chan);
+       if (err) {
+               BT_DBG("recv pkt %d", err);
+               err = -EAGAIN;
        }
 
-       return sent;
-}
-
-static struct sk_buff *create_pdu(struct l2cap_conn *conn, void *msg,
-                                 size_t len, u32 priority,
-                                 struct net_device *dev)
-{
-       struct sk_buff *skb;
-       int err, count;
-       struct l2cap_hdr *lh;
-
-       /* FIXME: This mtu check should be not needed and atm is only used for
-        * testing purposes
-        */
-       if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
-               conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE;
-
-       count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
-
-       BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count);
-
-       skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_ATOMIC);
-       if (!skb)
-               return ERR_PTR(-ENOMEM);
-
-       skb->priority = priority;
-
-       lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE);
-       lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN);
-       lh->len = cpu_to_le16(len);
-
-       err = skbuff_copy(msg, len, count, conn->mtu, skb, dev);
-       if (unlikely(err < 0)) {
-               kfree_skb(skb);
-               BT_DBG("skbuff copy %d failed", err);
-               return ERR_PTR(err);
-       }
-
-       return skb;
-}
-
-static int conn_send(struct l2cap_conn *conn,
-                    void *msg, size_t len, u32 priority,
-                    struct net_device *dev)
-{
-       struct sk_buff *skb;
-
-       skb = create_pdu(conn, msg, len, priority, dev);
-       if (IS_ERR(skb))
-               return -EINVAL;
-
-       BT_DBG("conn %p skb %p len %d priority %u", conn, skb, skb->len,
-              skb->priority);
-
-       hci_send_acl(conn->hchan, skb, ACL_START);
-
-       return 0;
+       return err;
 }
 
 static u8 get_addr_type_from_eui64(u8 byte)
 {
-       /* Is universal(0) or local(1) bit,  */
-       if (byte & 0x02)
-               return ADDR_LE_DEV_RANDOM;
-
-       return ADDR_LE_DEV_PUBLIC;
+       /* Is universal(0) or local(1) bit */
+       return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC);
 }
 
 static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr)
@@ -475,7 +398,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
        if (ipv6_addr_is_multicast(&hdr->daddr)) {
                memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
                       sizeof(struct in6_addr));
-               lowpan_cb(skb)->conn = NULL;
+               lowpan_cb(skb)->chan = NULL;
        } else {
                unsigned long flags;
 
@@ -484,9 +407,8 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
                 */
                convert_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
 
-               BT_DBG("dest addr %pMR type %s IP %pI6c", &addr,
-                      addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM",
-                      &hdr->daddr);
+               BT_DBG("dest addr %pMR type %d IP %pI6c", &addr,
+                      addr_type, &hdr->daddr);
 
                read_lock_irqsave(&devices_lock, flags);
                peer = peer_lookup_ba(dev, &addr, addr_type);
@@ -501,7 +423,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
 
                memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
                       sizeof(struct in6_addr));
-               lowpan_cb(skb)->conn = peer->conn;
+               lowpan_cb(skb)->chan = peer->chan;
        }
 
        saddr = dev->netdev->dev_addr;
@@ -510,14 +432,42 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
 }
 
 /* Packet to BT LE device */
-static int send_pkt(struct l2cap_conn *conn, const void *saddr,
-                   const void *daddr, struct sk_buff *skb,
+static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
                    struct net_device *netdev)
 {
-       raw_dump_table(__func__, "raw skb data dump before fragmentation",
-                      skb->data, skb->len);
+       struct msghdr msg;
+       struct kvec iv;
+       int err;
+
+       /* Remember the skb so that we can send EAGAIN to the caller if
+        * we run out of credits.
+        */
+       chan->data = skb;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = (struct iovec *) &iv;
+       msg.msg_iovlen = 1;
+       iv.iov_base = skb->data;
+       iv.iov_len = skb->len;
+
+       err = l2cap_chan_send(chan, &msg, skb->len);
+       if (err > 0) {
+               netdev->stats.tx_bytes += err;
+               netdev->stats.tx_packets++;
+               return 0;
+       }
+
+       if (!err)
+               err = lowpan_cb(skb)->status;
 
-       return conn_send(conn, skb->data, skb->len, 0, netdev);
+       if (err < 0) {
+               if (err == -EAGAIN)
+                       netdev->stats.tx_dropped++;
+               else
+                       netdev->stats.tx_errors++;
+       }
+
+       return err;
 }
 
 static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
@@ -540,8 +490,7 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
                list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) {
                        local_skb = skb_clone(skb, GFP_ATOMIC);
 
-                       send_pkt(pentry->conn, netdev->dev_addr,
-                                pentry->eui64_addr, local_skb, netdev);
+                       send_pkt(pentry->chan, local_skb, netdev);
 
                        kfree_skb(local_skb);
                }
@@ -553,7 +502,6 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
 static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
 {
        int err = 0;
-       unsigned char *eui64_addr;
        struct lowpan_dev *dev;
        struct lowpan_peer *peer;
        bdaddr_t addr;
@@ -568,21 +516,20 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
                unsigned long flags;
 
                convert_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
-               eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
                dev = lowpan_dev(netdev);
 
                read_lock_irqsave(&devices_lock, flags);
                peer = peer_lookup_ba(dev, &addr, addr_type);
                read_unlock_irqrestore(&devices_lock, flags);
 
-               BT_DBG("xmit %s to %pMR type %s IP %pI6c peer %p",
-                      netdev->name, &addr,
-                      addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM",
+               BT_DBG("xmit %s to %pMR type %d IP %pI6c peer %p",
+                      netdev->name, &addr, addr_type,
                       &lowpan_cb(skb)->addr, peer);
 
-               if (peer && peer->conn)
-                       err = send_pkt(peer->conn, netdev->dev_addr,
-                                      eui64_addr, skb, netdev);
+               if (peer && peer->chan)
+                       err = send_pkt(peer->chan, skb, netdev);
+               else
+                       err = -ENOENT;
        }
        dev_kfree_skb(skb);
 
@@ -634,7 +581,7 @@ static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
        eui[7] = addr[0];
 
        /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */
-       if (addr_type == ADDR_LE_DEV_PUBLIC)
+       if (addr_type == BDADDR_LE_PUBLIC)
                eui[0] &= ~0x02;
        else
                eui[0] |= 0x02;
@@ -660,6 +607,17 @@ static void ifup(struct net_device *netdev)
        rtnl_unlock();
 }
 
+static void ifdown(struct net_device *netdev)
+{
+       int err;
+
+       rtnl_lock();
+       err = dev_close(netdev);
+       if (err < 0)
+               BT_INFO("iface %s cannot be closed (%d)", netdev->name, err);
+       rtnl_unlock();
+}
+
 static void do_notify_peers(struct work_struct *work)
 {
        struct lowpan_dev *dev = container_of(work, struct lowpan_dev,
@@ -673,26 +631,64 @@ static bool is_bt_6lowpan(struct hci_conn *hcon)
        if (hcon->type != LE_LINK)
                return false;
 
-       return test_bit(HCI_CONN_6LOWPAN, &hcon->flags);
+       if (!psm_6lowpan)
+               return false;
+
+       return true;
+}
+
+static struct l2cap_chan *chan_create(void)
+{
+       struct l2cap_chan *chan;
+
+       chan = l2cap_chan_create();
+       if (!chan)
+               return NULL;
+
+       l2cap_chan_set_defaults(chan);
+
+       chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
+       chan->mode = L2CAP_MODE_LE_FLOWCTL;
+       chan->omtu = 65535;
+       chan->imtu = chan->omtu;
+
+       return chan;
 }
 
-static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
+static struct l2cap_chan *chan_open(struct l2cap_chan *pchan)
+{
+       struct l2cap_chan *chan;
+
+       chan = chan_create();
+       if (!chan)
+               return NULL;
+
+       chan->remote_mps = chan->omtu;
+       chan->mps = chan->omtu;
+
+       chan->state = BT_CONNECTED;
+
+       return chan;
+}
+
+static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
+                                       struct lowpan_dev *dev)
 {
        struct lowpan_peer *peer;
        unsigned long flags;
 
        peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
        if (!peer)
-               return -ENOMEM;
+               return NULL;
 
-       peer->conn = conn;
+       peer->chan = chan;
        memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
 
        /* RFC 2464 ch. 5 */
        peer->peer_addr.s6_addr[0] = 0xFE;
        peer->peer_addr.s6_addr[1] = 0x80;
-       set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b,
-                conn->hcon->dst_type);
+       set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b,
+                chan->dst_type);
 
        memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
               EUI64_ADDR_LEN);
@@ -706,40 +702,24 @@ static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
        INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
        schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
 
-       return 0;
+       return peer->chan;
 }
 
-/* This gets called when BT LE 6LoWPAN device is connected. We then
- * create network device that acts as a proxy between BT LE device
- * and kernel network stack.
- */
-int bt_6lowpan_add_conn(struct l2cap_conn *conn)
+static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
 {
-       struct lowpan_peer *peer = NULL;
-       struct lowpan_dev *dev;
        struct net_device *netdev;
        int err = 0;
        unsigned long flags;
 
-       if (!is_bt_6lowpan(conn->hcon))
-               return 0;
-
-       peer = lookup_peer(conn);
-       if (peer)
-               return -EEXIST;
-
-       dev = lookup_dev(conn);
-       if (dev)
-               return add_peer_conn(conn, dev);
-
-       netdev = alloc_netdev(sizeof(*dev), IFACE_NAME_TEMPLATE, netdev_setup);
+       netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE,
+                             netdev_setup);
        if (!netdev)
                return -ENOMEM;
 
-       set_dev_addr(netdev, &conn->hcon->src, conn->hcon->src_type);
+       set_dev_addr(netdev, &chan->src, chan->src_type);
 
        netdev->netdev_ops = &netdev_ops;
-       SET_NETDEV_DEV(netdev, &conn->hcon->dev);
+       SET_NETDEV_DEV(netdev, &chan->conn->hcon->dev);
        SET_NETDEV_DEVTYPE(netdev, &bt_type);
 
        err = register_netdev(netdev);
@@ -749,28 +729,61 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn)
                goto out;
        }
 
-       BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
-              netdev->ifindex, &conn->hcon->dst, &conn->hcon->src);
+       BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d",
+              netdev->ifindex, &chan->dst, chan->dst_type,
+              &chan->src, chan->src_type);
        set_bit(__LINK_STATE_PRESENT, &netdev->state);
 
-       dev = netdev_priv(netdev);
-       dev->netdev = netdev;
-       dev->hdev = conn->hcon->hdev;
-       INIT_LIST_HEAD(&dev->peers);
+       *dev = netdev_priv(netdev);
+       (*dev)->netdev = netdev;
+       (*dev)->hdev = chan->conn->hcon->hdev;
+       INIT_LIST_HEAD(&(*dev)->peers);
 
        write_lock_irqsave(&devices_lock, flags);
-       INIT_LIST_HEAD(&dev->list);
-       list_add(&dev->list, &bt_6lowpan_devices);
+       INIT_LIST_HEAD(&(*dev)->list);
+       list_add(&(*dev)->list, &bt_6lowpan_devices);
        write_unlock_irqrestore(&devices_lock, flags);
 
-       ifup(netdev);
-
-       return add_peer_conn(conn, dev);
+       return 0;
 
 out:
        return err;
 }
 
+static inline void chan_ready_cb(struct l2cap_chan *chan)
+{
+       struct lowpan_dev *dev;
+
+       dev = lookup_dev(chan->conn);
+
+       BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev);
+
+       if (!dev) {
+               if (setup_netdev(chan, &dev) < 0) {
+                       l2cap_chan_del(chan, -ENOENT);
+                       return;
+               }
+       }
+
+       if (!try_module_get(THIS_MODULE))
+               return;
+
+       add_peer_chan(chan, dev);
+       ifup(dev->netdev);
+}
+
+static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *chan)
+{
+       struct l2cap_chan *pchan;
+
+       pchan = chan_open(chan);
+       pchan->ops = chan->ops;
+
+       BT_DBG("chan %p pchan %p", chan, pchan);
+
+       return pchan;
+}
+
 static void delete_netdev(struct work_struct *work)
 {
        struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
@@ -781,26 +794,43 @@ static void delete_netdev(struct work_struct *work)
        /* The entry pointer is deleted in device_event() */
 }
 
-int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+static void chan_close_cb(struct l2cap_chan *chan)
 {
        struct lowpan_dev *entry, *tmp;
        struct lowpan_dev *dev = NULL;
        struct lowpan_peer *peer;
        int err = -ENOENT;
        unsigned long flags;
-       bool last = false;
+       bool last = false, removed = true;
 
-       if (!conn || !is_bt_6lowpan(conn->hcon))
-               return 0;
+       BT_DBG("chan %p conn %p", chan, chan->conn);
+
+       if (chan->conn && chan->conn->hcon) {
+               if (!is_bt_6lowpan(chan->conn->hcon))
+                       return;
+
+               /* If conn is set, then the netdev is also there and we should
+                * not remove it.
+                */
+               removed = false;
+       }
 
        write_lock_irqsave(&devices_lock, flags);
 
        list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
                dev = lowpan_dev(entry->netdev);
-               peer = peer_lookup_conn(dev, conn);
+               peer = peer_lookup_chan(dev, chan);
                if (peer) {
                        last = peer_del(dev, peer);
                        err = 0;
+
+                       BT_DBG("dev %p removing %speer %p", dev,
+                              last ? "last " : "1 ", peer);
+                       BT_DBG("chan %p orig refcnt %d", chan,
+                              atomic_read(&chan->kref.refcount));
+
+                       l2cap_chan_put(chan);
+                       kfree(peer);
                        break;
                }
        }
@@ -810,18 +840,402 @@ int bt_6lowpan_del_conn(struct l2cap_conn *conn)
 
                cancel_delayed_work_sync(&dev->notify_peers);
 
-               /* bt_6lowpan_del_conn() is called with hci dev lock held which
-                * means that we must delete the netdevice in worker thread.
-                */
-               INIT_WORK(&entry->delete_netdev, delete_netdev);
-               schedule_work(&entry->delete_netdev);
+               ifdown(dev->netdev);
+
+               if (!removed) {
+                       INIT_WORK(&entry->delete_netdev, delete_netdev);
+                       schedule_work(&entry->delete_netdev);
+               }
        } else {
                write_unlock_irqrestore(&devices_lock, flags);
        }
 
+       return;
+}
+
+static void chan_state_change_cb(struct l2cap_chan *chan, int state, int err)
+{
+       BT_DBG("chan %p conn %p state %s err %d", chan, chan->conn,
+              state_to_string(state), err);
+}
+
+static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan,
+                                        unsigned long hdr_len,
+                                        unsigned long len, int nb)
+{
+       /* Note that we must allocate using GFP_ATOMIC here as
+        * this function is called originally from netdev hard xmit
+        * function in atomic context.
+        */
+       return bt_skb_alloc(hdr_len + len, GFP_ATOMIC);
+}
+
+static void chan_suspend_cb(struct l2cap_chan *chan)
+{
+       struct sk_buff *skb = chan->data;
+
+       BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
+
+       lowpan_cb(skb)->status = -EAGAIN;
+}
+
+static void chan_resume_cb(struct l2cap_chan *chan)
+{
+       struct sk_buff *skb = chan->data;
+
+       BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb);
+
+       lowpan_cb(skb)->status = 0;
+}
+
+static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
+{
+       return msecs_to_jiffies(1000);
+}
+
+static const struct l2cap_ops bt_6lowpan_chan_ops = {
+       .name                   = "L2CAP 6LoWPAN channel",
+       .new_connection         = chan_new_conn_cb,
+       .recv                   = chan_recv_cb,
+       .close                  = chan_close_cb,
+       .state_change           = chan_state_change_cb,
+       .ready                  = chan_ready_cb,
+       .resume                 = chan_resume_cb,
+       .suspend                = chan_suspend_cb,
+       .get_sndtimeo           = chan_get_sndtimeo_cb,
+       .alloc_skb              = chan_alloc_skb_cb,
+       .memcpy_fromiovec       = l2cap_chan_no_memcpy_fromiovec,
+
+       .teardown               = l2cap_chan_no_teardown,
+       .defer                  = l2cap_chan_no_defer,
+       .set_shutdown           = l2cap_chan_no_set_shutdown,
+};
+
+static inline __u8 bdaddr_type(__u8 type)
+{
+       if (type == ADDR_LE_DEV_PUBLIC)
+               return BDADDR_LE_PUBLIC;
+       else
+               return BDADDR_LE_RANDOM;
+}
+
+static struct l2cap_chan *chan_get(void)
+{
+       struct l2cap_chan *pchan;
+
+       pchan = chan_create();
+       if (!pchan)
+               return NULL;
+
+       pchan->ops = &bt_6lowpan_chan_ops;
+
+       return pchan;
+}
+
+static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
+{
+       struct l2cap_chan *pchan;
+       int err;
+
+       pchan = chan_get();
+       if (!pchan)
+               return -EINVAL;
+
+       err = l2cap_chan_connect(pchan, cpu_to_le16(psm_6lowpan), 0,
+                                addr, dst_type);
+
+       BT_DBG("chan %p err %d", pchan, err);
+       if (err < 0)
+               l2cap_chan_put(pchan);
+
        return err;
 }
 
+static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
+{
+       struct lowpan_peer *peer;
+
+       BT_DBG("conn %p dst type %d", conn, dst_type);
+
+       peer = lookup_peer(conn);
+       if (!peer)
+               return -ENOENT;
+
+       BT_DBG("peer %p chan %p", peer, peer->chan);
+
+       l2cap_chan_close(peer->chan, ENOENT);
+
+       return 0;
+}
+
+static struct l2cap_chan *bt_6lowpan_listen(void)
+{
+       bdaddr_t *addr = BDADDR_ANY;
+       struct l2cap_chan *pchan;
+       int err;
+
+       if (psm_6lowpan == 0)
+               return NULL;
+
+       pchan = chan_get();
+       if (!pchan)
+               return NULL;
+
+       pchan->state = BT_LISTEN;
+       pchan->src_type = BDADDR_LE_PUBLIC;
+
+       BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
+              pchan->src_type);
+
+       err = l2cap_add_psm(pchan, addr, cpu_to_le16(psm_6lowpan));
+       if (err) {
+               l2cap_chan_put(pchan);
+               BT_ERR("psm cannot be added err %d", err);
+               return NULL;
+       }
+
+       return pchan;
+}
+
+static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
+                         struct l2cap_conn **conn)
+{
+       struct hci_conn *hcon;
+       struct hci_dev *hdev;
+       bdaddr_t *src = BDADDR_ANY;
+       int n;
+
+       n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
+                  &addr->b[5], &addr->b[4], &addr->b[3],
+                  &addr->b[2], &addr->b[1], &addr->b[0],
+                  addr_type);
+
+       if (n < 7)
+               return -EINVAL;
+
+       hdev = hci_get_route(addr, src);
+       if (!hdev)
+               return -ENOENT;
+
+       hci_dev_lock(hdev);
+       hcon = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
+       hci_dev_unlock(hdev);
+
+       if (!hcon)
+               return -ENOENT;
+
+       *conn = (struct l2cap_conn *)hcon->l2cap_data;
+
+       BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type);
+
+       return 0;
+}
+
+static void disconnect_all_peers(void)
+{
+       struct lowpan_dev *entry, *tmp_dev;
+       struct lowpan_peer *peer, *tmp_peer, *new_peer;
+       struct list_head peers;
+       unsigned long flags;
+
+       INIT_LIST_HEAD(&peers);
+
+       /* We make a separate list of peers as the close_cb() will
+        * modify the device peers list so it is better not to mess
+        * with the same list at the same time.
+        */
+
+       read_lock_irqsave(&devices_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
+               list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) {
+                       new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
+                       if (!new_peer)
+                               break;
+
+                       new_peer->chan = peer->chan;
+                       INIT_LIST_HEAD(&new_peer->list);
+
+                       list_add(&new_peer->list, &peers);
+               }
+       }
+
+       read_unlock_irqrestore(&devices_lock, flags);
+
+       list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
+               l2cap_chan_close(peer->chan, ENOENT);
+               kfree(peer);
+       }
+}
+
+static int lowpan_psm_set(void *data, u64 val)
+{
+       u16 psm;
+
+       psm = val;
+       if (psm == 0 || psm_6lowpan != psm)
+               /* Disconnect existing connections if 6lowpan is
+                * disabled (psm = 0), or if psm changes.
+                */
+               disconnect_all_peers();
+
+       psm_6lowpan = psm;
+
+       if (listen_chan) {
+               l2cap_chan_close(listen_chan, 0);
+               l2cap_chan_put(listen_chan);
+       }
+
+       listen_chan = bt_6lowpan_listen();
+
+       return 0;
+}
+
+static int lowpan_psm_get(void *data, u64 *val)
+{
+       *val = psm_6lowpan;
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_psm_fops, lowpan_psm_get,
+                       lowpan_psm_set, "%llu\n");
+
+static ssize_t lowpan_control_write(struct file *fp,
+                                   const char __user *user_buffer,
+                                   size_t count,
+                                   loff_t *position)
+{
+       char buf[32];
+       size_t buf_size = min(count, sizeof(buf) - 1);
+       int ret;
+       bdaddr_t addr;
+       u8 addr_type;
+       struct l2cap_conn *conn = NULL;
+
+       if (copy_from_user(buf, user_buffer, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+
+       if (memcmp(buf, "connect ", 8) == 0) {
+               ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn);
+               if (ret == -EINVAL)
+                       return ret;
+
+               if (listen_chan) {
+                       l2cap_chan_close(listen_chan, 0);
+                       l2cap_chan_put(listen_chan);
+                       listen_chan = NULL;
+               }
+
+               if (conn) {
+                       struct lowpan_peer *peer;
+
+                       if (!is_bt_6lowpan(conn->hcon))
+                               return -EINVAL;
+
+                       peer = lookup_peer(conn);
+                       if (peer) {
+                               BT_DBG("6LoWPAN connection already exists");
+                               return -EALREADY;
+                       }
+
+                       BT_DBG("conn %p dst %pMR type %d user %d", conn,
+                              &conn->hcon->dst, conn->hcon->dst_type,
+                              addr_type);
+               }
+
+               ret = bt_6lowpan_connect(&addr, addr_type);
+               if (ret < 0)
+                       return ret;
+
+               return count;
+       }
+
+       if (memcmp(buf, "disconnect ", 11) == 0) {
+               ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn);
+               if (ret < 0)
+                       return ret;
+
+               ret = bt_6lowpan_disconnect(conn, addr_type);
+               if (ret < 0)
+                       return ret;
+
+               return count;
+       }
+
+       return count;
+}
+
+static int lowpan_control_show(struct seq_file *f, void *ptr)
+{
+       struct lowpan_dev *entry, *tmp_dev;
+       struct lowpan_peer *peer, *tmp_peer;
+       unsigned long flags;
+
+       read_lock_irqsave(&devices_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
+               list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list)
+                       seq_printf(f, "%pMR (type %u)\n",
+                                  &peer->chan->dst, peer->chan->dst_type);
+       }
+
+       read_unlock_irqrestore(&devices_lock, flags);
+
+       return 0;
+}
+
+static int lowpan_control_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, lowpan_control_show, inode->i_private);
+}
+
+static const struct file_operations lowpan_control_fops = {
+       .open           = lowpan_control_open,
+       .read           = seq_read,
+       .write          = lowpan_control_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static void disconnect_devices(void)
+{
+       struct lowpan_dev *entry, *tmp, *new_dev;
+       struct list_head devices;
+       unsigned long flags;
+
+       INIT_LIST_HEAD(&devices);
+
+       /* We make a separate list of devices because the unregister_netdev()
+        * will call device_event() which will also want to modify the same
+        * devices list.
+        */
+
+       read_lock_irqsave(&devices_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+               new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
+               if (!new_dev)
+                       break;
+
+               new_dev->netdev = entry->netdev;
+               INIT_LIST_HEAD(&new_dev->list);
+
+               list_add(&new_dev->list, &devices);
+       }
+
+       read_unlock_irqrestore(&devices_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp, &devices, list) {
+               ifdown(entry->netdev);
+               BT_DBG("Unregistering netdev %s %p",
+                      entry->netdev->name, entry->netdev);
+               unregister_netdev(entry->netdev);
+               kfree(entry);
+       }
+}
+
 static int device_event(struct notifier_block *unused,
                        unsigned long event, void *ptr)
 {
@@ -838,6 +1252,8 @@ static int device_event(struct notifier_block *unused,
                list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
                                         list) {
                        if (entry->netdev == netdev) {
+                               BT_DBG("Unregistered netdev %s %p",
+                                      netdev->name, netdev);
                                list_del(&entry->list);
                                kfree(entry);
                                break;
@@ -854,12 +1270,37 @@ static struct notifier_block bt_6lowpan_dev_notifier = {
        .notifier_call = device_event,
 };
 
-int bt_6lowpan_init(void)
+static int __init bt_6lowpan_init(void)
 {
+       lowpan_psm_debugfs = debugfs_create_file("6lowpan_psm", 0644,
+                                                bt_debugfs, NULL,
+                                                &lowpan_psm_fops);
+       lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644,
+                                                    bt_debugfs, NULL,
+                                                    &lowpan_control_fops);
+
        return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
 }
 
-void bt_6lowpan_cleanup(void)
+static void __exit bt_6lowpan_exit(void)
 {
+       debugfs_remove(lowpan_psm_debugfs);
+       debugfs_remove(lowpan_control_debugfs);
+
+       if (listen_chan) {
+               l2cap_chan_close(listen_chan, 0);
+               l2cap_chan_put(listen_chan);
+       }
+
+       disconnect_devices();
+
        unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
 }
+
+module_init(bt_6lowpan_init);
+module_exit(bt_6lowpan_exit);
+
+MODULE_AUTHOR("Jukka Rissanen <jukka.rissanen@linux.intel.com>");
+MODULE_DESCRIPTION("Bluetooth 6LoWPAN");
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
deleted file mode 100644 (file)
index 5d281f1..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-   Copyright (c) 2013 Intel Corp.
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 and
-   only 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.
-*/
-
-#ifndef __6LOWPAN_H
-#define __6LOWPAN_H
-
-#include <linux/errno.h>
-#include <linux/skbuff.h>
-#include <net/bluetooth/l2cap.h>
-
-#if IS_ENABLED(CONFIG_BT_6LOWPAN)
-int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb);
-int bt_6lowpan_add_conn(struct l2cap_conn *conn);
-int bt_6lowpan_del_conn(struct l2cap_conn *conn);
-int bt_6lowpan_init(void);
-void bt_6lowpan_cleanup(void);
-#else
-static int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
-{
-       return -EOPNOTSUPP;
-}
-static int bt_6lowpan_add_conn(struct l2cap_conn *conn)
-{
-       return -EOPNOTSUPP;
-}
-int bt_6lowpan_del_conn(struct l2cap_conn *conn)
-{
-       return -EOPNOTSUPP;
-}
-static int bt_6lowpan_init(void)
-{
-       return -EOPNOTSUPP;
-}
-static void bt_6lowpan_cleanup(void) { }
-#endif
-
-#endif /* __6LOWPAN_H */
index 06ec14499ca129d2e5b8833a01b7530bc829802d..600fb29288f47c48ce17583d769d502746854bd1 100644 (file)
@@ -6,7 +6,6 @@ menuconfig BT
        tristate "Bluetooth subsystem support"
        depends on NET && !S390
        depends on RFKILL || !RFKILL
-       select 6LOWPAN_IPHC if BT_6LOWPAN
        select CRC16
        select CRYPTO
        select CRYPTO_BLKCIPHER
@@ -41,10 +40,10 @@ menuconfig BT
          more information, see <http://www.bluez.org/>.
 
 config BT_6LOWPAN
-       bool "Bluetooth 6LoWPAN support"
-       depends on BT && IPV6
+       tristate "Bluetooth 6LoWPAN support"
+       depends on BT && 6LOWPAN
        help
-         IPv6 compression over Bluetooth.
+         IPv6 compression over Bluetooth Low Energy.
 
 source "net/bluetooth/rfcomm/Kconfig"
 
index ca51246b1016f2496a53220256385f8072c5b360..886e9aa3ecf1ffa3d7cd93d7aea873bfb25b5c1e 100644 (file)
@@ -7,10 +7,12 @@ obj-$(CONFIG_BT_RFCOMM)       += rfcomm/
 obj-$(CONFIG_BT_BNEP)  += bnep/
 obj-$(CONFIG_BT_CMTP)  += cmtp/
 obj-$(CONFIG_BT_HIDP)  += hidp/
+obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
+
+bluetooth_6lowpan-y := 6lowpan.o
 
 bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
        hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
        a2mp.o amp.o
-bluetooth-$(CONFIG_BT_6LOWPAN) += 6lowpan.o
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
index 9514cc9e850ca9f4662cc0bedd898f07cc7f96f5..5dcade511fdbf7da80995ac2c8218171663b339f 100644 (file)
@@ -63,7 +63,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
        msg.msg_iov = (struct iovec *) &iv;
        msg.msg_iovlen = 1;
 
-       l2cap_chan_send(chan, &msg, total_len, 0);
+       l2cap_chan_send(chan, &msg, total_len);
 
        kfree(cmd);
 }
@@ -693,18 +693,19 @@ static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state,
 }
 
 static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
+                                             unsigned long hdr_len,
                                              unsigned long len, int nb)
 {
        struct sk_buff *skb;
 
-       skb = bt_skb_alloc(len, GFP_KERNEL);
+       skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL);
        if (!skb)
                return ERR_PTR(-ENOMEM);
 
        return skb;
 }
 
-static struct l2cap_ops a2mp_chan_ops = {
+static const struct l2cap_ops a2mp_chan_ops = {
        .name = "L2CAP A2MP channel",
        .recv = a2mp_chan_recv_cb,
        .close = a2mp_chan_close_cb,
@@ -719,6 +720,7 @@ static struct l2cap_ops a2mp_chan_ops = {
        .resume = l2cap_chan_no_resume,
        .set_shutdown = l2cap_chan_no_set_shutdown,
        .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
+       .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
 };
 
 static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
index 2021c481cdb657a8011938f5e8486c4c93f0c116..4dca0299ed96875b16a80929a98b3a6972003be4 100644 (file)
@@ -639,7 +639,7 @@ static int bt_seq_show(struct seq_file *seq, void *v)
        return 0;
 }
 
-static struct seq_operations bt_seq_ops = {
+static const struct seq_operations bt_seq_ops = {
        .start = bt_seq_start,
        .next  = bt_seq_next,
        .stop  = bt_seq_stop,
index bb39509b3f065e2a0d18e1a53cfcfabc8bfe779e..016cdb66df6cfa5bfd4bb7fcd808a616e9fcde42 100644 (file)
@@ -113,8 +113,9 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
 {
        bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst;
        struct hci_conn *hcon;
+       u8 role = out ? HCI_ROLE_MASTER : HCI_ROLE_SLAVE;
 
-       hcon = hci_conn_add(hdev, AMP_LINK, dst);
+       hcon = hci_conn_add(hdev, AMP_LINK, dst, role);
        if (!hcon)
                return NULL;
 
@@ -125,7 +126,6 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
        hcon->handle = __next_handle(mgr);
        hcon->remote_id = remote_id;
        hcon->amp_mgr = amp_mgr_get(mgr);
-       hcon->out = out;
 
        return hcon;
 }
@@ -133,8 +133,8 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
 /* AMP crypto key generation interface */
 static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
 {
-       int ret = 0;
        struct crypto_shash *tfm;
+       int ret;
 
        if (!ksize)
                return -EINVAL;
index cd75e4d64b9093df7f3d07c711b43ba983779197..1ca8a87a078776d75d223a204aa4f42770c229a3 100644 (file)
@@ -362,12 +362,6 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
                CAPIMSG_SETCONTROL(skb->data, contr);
        }
 
-       if (!ctrl) {
-               BT_ERR("Can't find controller %d for message", session->num);
-               kfree_skb(skb);
-               return;
-       }
-
        capi_ctr_handle_message(ctrl, appl, skb);
 }
 
index a7a27bc2c0b1d8a7200e0a627c69b329e08cf838..b50dabb3f86ab49667cb939af29cef53a03b2ac2 100644 (file)
@@ -66,8 +66,7 @@ static void hci_acl_create_connection(struct hci_conn *conn)
 
        conn->state = BT_CONNECT;
        conn->out = true;
-
-       conn->link_mode = HCI_LM_MASTER;
+       conn->role = HCI_ROLE_MASTER;
 
        conn->attempt++;
 
@@ -136,7 +135,7 @@ void hci_disconnect(struct hci_conn *conn, __u8 reason)
        hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
 }
 
-static void hci_amp_disconn(struct hci_conn *conn, __u8 reason)
+static void hci_amp_disconn(struct hci_conn *conn)
 {
        struct hci_cp_disconn_phy_link cp;
 
@@ -145,7 +144,7 @@ static void hci_amp_disconn(struct hci_conn *conn, __u8 reason)
        conn->state = BT_DISCONN;
 
        cp.phy_handle = HCI_PHY_HANDLE(conn->handle);
-       cp.reason = reason;
+       cp.reason = hci_proto_disconn_ind(conn);
        hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK,
                     sizeof(cp), &cp);
 }
@@ -213,14 +212,26 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
        return true;
 }
 
-void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
-                       u16 latency, u16 to_multiplier)
+u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
+                     u16 to_multiplier)
 {
-       struct hci_cp_le_conn_update cp;
        struct hci_dev *hdev = conn->hdev;
+       struct hci_conn_params *params;
+       struct hci_cp_le_conn_update cp;
 
-       memset(&cp, 0, sizeof(cp));
+       hci_dev_lock(hdev);
 
+       params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+       if (params) {
+               params->conn_min_interval = min;
+               params->conn_max_interval = max;
+               params->conn_latency = latency;
+               params->supervision_timeout = to_multiplier;
+       }
+
+       hci_dev_unlock(hdev);
+
+       memset(&cp, 0, sizeof(cp));
        cp.handle               = cpu_to_le16(conn->handle);
        cp.conn_interval_min    = cpu_to_le16(min);
        cp.conn_interval_max    = cpu_to_le16(max);
@@ -230,6 +241,11 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
        cp.max_ce_len           = cpu_to_le16(0x0000);
 
        hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);
+
+       if (params)
+               return 0x01;
+
+       return 0x00;
 }
 
 void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
@@ -271,20 +287,6 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
        }
 }
 
-static void hci_conn_disconnect(struct hci_conn *conn)
-{
-       __u8 reason = hci_proto_disconn_ind(conn);
-
-       switch (conn->type) {
-       case AMP_LINK:
-               hci_amp_disconn(conn, reason);
-               break;
-       default:
-               hci_disconnect(conn, reason);
-               break;
-       }
-}
-
 static void hci_conn_timeout(struct work_struct *work)
 {
        struct hci_conn *conn = container_of(work, struct hci_conn,
@@ -319,7 +321,31 @@ static void hci_conn_timeout(struct work_struct *work)
                break;
        case BT_CONFIG:
        case BT_CONNECTED:
-               hci_conn_disconnect(conn);
+               if (conn->type == AMP_LINK) {
+                       hci_amp_disconn(conn);
+               } else {
+                       __u8 reason = hci_proto_disconn_ind(conn);
+
+                       /* When we are master of an established connection
+                        * and it enters the disconnect timeout, then go
+                        * ahead and try to read the current clock offset.
+                        *
+                        * Processing of the result is done within the
+                        * event handling and hci_clock_offset_evt function.
+                        */
+                       if (conn->type == ACL_LINK &&
+                           conn->role == HCI_ROLE_MASTER) {
+                               struct hci_dev *hdev = conn->hdev;
+                               struct hci_cp_read_clock_offset cp;
+
+                               cp.handle = cpu_to_le16(conn->handle);
+
+                               hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET,
+                                            sizeof(cp), &cp);
+                       }
+
+                       hci_disconnect(conn, reason);
+               }
                break;
        default:
                conn->state = BT_CLOSED;
@@ -336,9 +362,6 @@ static void hci_conn_idle(struct work_struct *work)
 
        BT_DBG("hcon %p mode %d", conn, conn->mode);
 
-       if (test_bit(HCI_RAW, &hdev->flags))
-               return;
-
        if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
                return;
 
@@ -398,13 +421,14 @@ static void le_conn_timeout(struct work_struct *work)
        hci_le_create_connection_cancel(conn);
 }
 
-struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
+                             u8 role)
 {
        struct hci_conn *conn;
 
        BT_DBG("%s dst %pMR", hdev->name, dst);
 
-       conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL);
+       conn = kzalloc(sizeof(*conn), GFP_KERNEL);
        if (!conn)
                return NULL;
 
@@ -412,6 +436,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        bacpy(&conn->src, &hdev->bdaddr);
        conn->hdev  = hdev;
        conn->type  = type;
+       conn->role  = role;
        conn->mode  = HCI_CM_ACTIVE;
        conn->state = BT_OPEN;
        conn->auth_type = HCI_AT_GENERAL_BONDING;
@@ -424,6 +449,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
        set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
        conn->disc_timeout = HCI_DISCONN_TIMEOUT;
 
+       if (conn->role == HCI_ROLE_MASTER)
+               conn->out = true;
+
        switch (type) {
        case ACL_LINK:
                conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK;
@@ -529,7 +557,6 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
 
        list_for_each_entry(d, &hci_dev_list, list) {
                if (!test_bit(HCI_UP, &d->flags) ||
-                   test_bit(HCI_RAW, &d->flags) ||
                    test_bit(HCI_USER_CHANNEL, &d->dev_flags) ||
                    d->dev_type != HCI_BREDR)
                        continue;
@@ -627,7 +654,8 @@ static void hci_req_add_le_create_conn(struct hci_request *req,
        cp.own_address_type = own_addr_type;
        cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
        cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
-       cp.supervision_timeout = cpu_to_le16(0x002a);
+       cp.conn_latency = cpu_to_le16(conn->le_conn_latency);
+       cp.supervision_timeout = cpu_to_le16(conn->le_supv_timeout);
        cp.min_ce_len = cpu_to_le16(0x0000);
        cp.max_ce_len = cpu_to_le16(0x0000);
 
@@ -644,15 +672,12 @@ static void hci_req_directed_advertising(struct hci_request *req,
        u8 own_addr_type;
        u8 enable;
 
-       enable = 0x00;
-       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
-
-       /* Clear the HCI_ADVERTISING bit temporarily so that the
+       /* Clear the HCI_LE_ADV bit temporarily so that the
         * hci_update_random_address knows that it's safe to go ahead
         * and write a new random address. The flag will be set back on
         * as soon as the SET_ADV_ENABLE HCI command completes.
         */
-       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+       clear_bit(HCI_LE_ADV, &hdev->dev_flags);
 
        /* Set require_privacy to false so that the remote device has a
         * chance of identifying us.
@@ -676,7 +701,8 @@ static void hci_req_directed_advertising(struct hci_request *req,
 }
 
 struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
-                               u8 dst_type, u8 sec_level, u8 auth_type)
+                               u8 dst_type, u8 sec_level, u16 conn_timeout,
+                               u8 role)
 {
        struct hci_conn_params *params;
        struct hci_conn *conn;
@@ -696,7 +722,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
        conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
        if (conn) {
                conn->pending_sec_level = sec_level;
-               conn->auth_type = auth_type;
                goto done;
        }
 
@@ -726,32 +751,56 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                dst_type = ADDR_LE_DEV_RANDOM;
        }
 
-       conn = hci_conn_add(hdev, LE_LINK, dst);
+       conn = hci_conn_add(hdev, LE_LINK, dst, role);
        if (!conn)
                return ERR_PTR(-ENOMEM);
 
        conn->dst_type = dst_type;
        conn->sec_level = BT_SECURITY_LOW;
        conn->pending_sec_level = sec_level;
-       conn->auth_type = auth_type;
+       conn->conn_timeout = conn_timeout;
 
        hci_req_init(&req, hdev);
 
-       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+       /* Disable advertising if we're active. For master role
+        * connections most controllers will refuse to connect if
+        * advertising is enabled, and for slave role connections we
+        * anyway have to disable it in order to start directed
+        * advertising.
+        */
+       if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
+               u8 enable = 0x00;
+               hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
+                           &enable);
+       }
+
+       /* If requested to connect as slave use directed advertising */
+       if (conn->role == HCI_ROLE_SLAVE) {
+               /* If we're active scanning most controllers are unable
+                * to initiate advertising. Simply reject the attempt.
+                */
+               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+                   hdev->le_scan_type == LE_SCAN_ACTIVE) {
+                       skb_queue_purge(&req.cmd_q);
+                       hci_conn_del(conn);
+                       return ERR_PTR(-EBUSY);
+               }
+
                hci_req_directed_advertising(&req, conn);
                goto create_conn;
        }
 
-       conn->out = true;
-       conn->link_mode |= HCI_LM_MASTER;
-
        params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
        if (params) {
                conn->le_conn_min_interval = params->conn_min_interval;
                conn->le_conn_max_interval = params->conn_max_interval;
+               conn->le_conn_latency = params->conn_latency;
+               conn->le_supv_timeout = params->supervision_timeout;
        } else {
                conn->le_conn_min_interval = hdev->le_conn_min_interval;
                conn->le_conn_max_interval = hdev->le_conn_max_interval;
+               conn->le_conn_latency = hdev->le_conn_latency;
+               conn->le_supv_timeout = hdev->le_supv_timeout;
        }
 
        /* If controller is scanning, we stop it since some controllers are
@@ -785,11 +834,11 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
        struct hci_conn *acl;
 
        if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
-               return ERR_PTR(-ENOTSUPP);
+               return ERR_PTR(-EOPNOTSUPP);
 
        acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
        if (!acl) {
-               acl = hci_conn_add(hdev, ACL_LINK, dst);
+               acl = hci_conn_add(hdev, ACL_LINK, dst, HCI_ROLE_MASTER);
                if (!acl)
                        return ERR_PTR(-ENOMEM);
        }
@@ -818,7 +867,7 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
 
        sco = hci_conn_hash_lookup_ba(hdev, type, dst);
        if (!sco) {
-               sco = hci_conn_add(hdev, type, dst);
+               sco = hci_conn_add(hdev, type, dst, HCI_ROLE_MASTER);
                if (!sco) {
                        hci_conn_drop(acl);
                        return ERR_PTR(-ENOMEM);
@@ -865,7 +914,8 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
                        return 0;
        }
 
-       if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT))
+       if (hci_conn_ssp_enabled(conn) &&
+           !test_bit(HCI_CONN_ENCRYPT, &conn->flags))
                return 0;
 
        return 1;
@@ -881,7 +931,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
 
        if (sec_level > conn->sec_level)
                conn->pending_sec_level = sec_level;
-       else if (conn->link_mode & HCI_LM_AUTH)
+       else if (test_bit(HCI_CONN_AUTH, &conn->flags))
                return 1;
 
        /* Make sure we preserve an existing MITM requirement*/
@@ -899,7 +949,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
                /* If we're already encrypted set the REAUTH_PEND flag,
                 * otherwise set the ENCRYPT_PEND.
                 */
-               if (conn->link_mode & HCI_LM_ENCRYPT)
+               if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
                        set_bit(HCI_CONN_REAUTH_PEND, &conn->flags);
                else
                        set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
@@ -923,7 +973,8 @@ static void hci_conn_encrypt(struct hci_conn *conn)
 }
 
 /* Enable security */
-int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
+int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
+                     bool initiator)
 {
        BT_DBG("hcon %p", conn);
 
@@ -940,7 +991,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
                return 1;
 
        /* For other security levels we need the link key. */
-       if (!(conn->link_mode & HCI_LM_AUTH))
+       if (!test_bit(HCI_CONN_AUTH, &conn->flags))
                goto auth;
 
        /* An authenticated FIPS approved combination key has sufficient
@@ -976,11 +1027,14 @@ auth:
        if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
                return 0;
 
+       if (initiator)
+               set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
+
        if (!hci_conn_auth(conn, sec_level, auth_type))
                return 0;
 
 encrypt:
-       if (conn->link_mode & HCI_LM_ENCRYPT)
+       if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
                return 1;
 
        hci_conn_encrypt(conn);
@@ -1027,7 +1081,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
 {
        BT_DBG("hcon %p", conn);
 
-       if (!role && conn->link_mode & HCI_LM_MASTER)
+       if (role == conn->role)
                return 1;
 
        if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) {
@@ -1048,9 +1102,6 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
 
        BT_DBG("hcon %p mode %d", conn, conn->mode);
 
-       if (test_bit(HCI_RAW, &hdev->flags))
-               return;
-
        if (conn->mode != HCI_CM_SNIFF)
                goto timer;
 
@@ -1101,6 +1152,28 @@ void hci_conn_check_pending(struct hci_dev *hdev)
        hci_dev_unlock(hdev);
 }
 
+static u32 get_link_mode(struct hci_conn *conn)
+{
+       u32 link_mode = 0;
+
+       if (conn->role == HCI_ROLE_MASTER)
+               link_mode |= HCI_LM_MASTER;
+
+       if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
+               link_mode |= HCI_LM_ENCRYPT;
+
+       if (test_bit(HCI_CONN_AUTH, &conn->flags))
+               link_mode |= HCI_LM_AUTH;
+
+       if (test_bit(HCI_CONN_SECURE, &conn->flags))
+               link_mode |= HCI_LM_SECURE;
+
+       if (test_bit(HCI_CONN_FIPS, &conn->flags))
+               link_mode |= HCI_LM_FIPS;
+
+       return link_mode;
+}
+
 int hci_get_conn_list(void __user *arg)
 {
        struct hci_conn *c;
@@ -1136,7 +1209,7 @@ int hci_get_conn_list(void __user *arg)
                (ci + n)->type  = c->type;
                (ci + n)->out   = c->out;
                (ci + n)->state = c->state;
-               (ci + n)->link_mode = c->link_mode;
+               (ci + n)->link_mode = get_link_mode(c);
                if (++n >= req.conn_num)
                        break;
        }
@@ -1172,7 +1245,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
                ci.type  = conn->type;
                ci.out   = conn->out;
                ci.state = conn->state;
-               ci.link_mode = conn->link_mode;
+               ci.link_mode = get_link_mode(conn);
        }
        hci_dev_unlock(hdev);
 
@@ -1209,7 +1282,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
 
        BT_DBG("%s hcon %p", hdev->name, conn);
 
-       chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL);
+       chan = kzalloc(sizeof(*chan), GFP_KERNEL);
        if (!chan)
                return NULL;
 
index 0a43cce9a914b84613c7ee2d6fc30fdfdb2a0bc5..cfcb6055ced8f21df5f559484ac910d0e25e965e 100644 (file)
@@ -35,6 +35,7 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/mgmt.h>
 
 #include "smp.h"
 
@@ -53,6 +54,15 @@ DEFINE_RWLOCK(hci_cb_list_lock);
 /* HCI ID Numbering */
 static DEFINE_IDA(hci_index_ida);
 
+/* ----- HCI requests ----- */
+
+#define HCI_REQ_DONE     0
+#define HCI_REQ_PEND     1
+#define HCI_REQ_CANCELED  2
+
+#define hci_req_lock(d)                mutex_lock(&d->req_lock)
+#define hci_req_unlock(d)      mutex_unlock(&d->req_lock)
+
 /* ---- HCI notifications ---- */
 
 static void hci_notify(struct hci_dev *hdev, int event)
@@ -68,7 +78,7 @@ static ssize_t dut_mode_read(struct file *file, char __user *user_buf,
        struct hci_dev *hdev = file->private_data;
        char buf[3];
 
-       buf[0] = test_bit(HCI_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N';
+       buf[0] = test_bit(HCI_DUT_MODE, &hdev->dbg_flags) ? 'Y': 'N';
        buf[1] = '\n';
        buf[2] = '\0';
        return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
@@ -94,7 +104,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
        if (strtobool(buf, &enable))
                return -EINVAL;
 
-       if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags))
+       if (enable == test_bit(HCI_DUT_MODE, &hdev->dbg_flags))
                return -EALREADY;
 
        hci_req_lock(hdev);
@@ -115,7 +125,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
        if (err < 0)
                return err;
 
-       change_bit(HCI_DUT_MODE, &hdev->dev_flags);
+       change_bit(HCI_DUT_MODE, &hdev->dbg_flags);
 
        return count;
 }
@@ -190,6 +200,31 @@ static const struct file_operations blacklist_fops = {
        .release        = single_release,
 };
 
+static int whitelist_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bdaddr_list *b;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(b, &hdev->whitelist, list)
+               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int whitelist_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, whitelist_show, inode->i_private);
+}
+
+static const struct file_operations whitelist_fops = {
+       .open           = whitelist_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 static int uuids_show(struct seq_file *f, void *p)
 {
        struct hci_dev *hdev = f->private;
@@ -352,62 +387,13 @@ static int auto_accept_delay_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
                        auto_accept_delay_set, "%llu\n");
 
-static int ssp_debug_mode_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-       struct sk_buff *skb;
-       __u8 mode;
-       int err;
-
-       if (val != 0 && val != 1)
-               return -EINVAL;
-
-       if (!test_bit(HCI_UP, &hdev->flags))
-               return -ENETDOWN;
-
-       hci_req_lock(hdev);
-       mode = val;
-       skb = __hci_cmd_sync(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode),
-                            &mode, HCI_CMD_TIMEOUT);
-       hci_req_unlock(hdev);
-
-       if (IS_ERR(skb))
-               return PTR_ERR(skb);
-
-       err = -bt_to_errno(skb->data[0]);
-       kfree_skb(skb);
-
-       if (err < 0)
-               return err;
-
-       hci_dev_lock(hdev);
-       hdev->ssp_debug_mode = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int ssp_debug_mode_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->ssp_debug_mode;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get,
-                       ssp_debug_mode_set, "%llu\n");
-
 static ssize_t force_sc_support_read(struct file *file, char __user *user_buf,
                                     size_t count, loff_t *ppos)
 {
        struct hci_dev *hdev = file->private_data;
        char buf[3];
 
-       buf[0] = test_bit(HCI_FORCE_SC, &hdev->dev_flags) ? 'Y': 'N';
+       buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N';
        buf[1] = '\n';
        buf[2] = '\0';
        return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
@@ -432,10 +418,10 @@ static ssize_t force_sc_support_write(struct file *file,
        if (strtobool(buf, &enable))
                return -EINVAL;
 
-       if (enable == test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+       if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
                return -EALREADY;
 
-       change_bit(HCI_FORCE_SC, &hdev->dev_flags);
+       change_bit(HCI_FORCE_SC, &hdev->dbg_flags);
 
        return count;
 }
@@ -719,7 +705,7 @@ static ssize_t force_static_address_read(struct file *file,
        struct hci_dev *hdev = file->private_data;
        char buf[3];
 
-       buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ? 'Y': 'N';
+       buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N';
        buf[1] = '\n';
        buf[2] = '\0';
        return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
@@ -744,10 +730,10 @@ static ssize_t force_static_address_write(struct file *file,
        if (strtobool(buf, &enable))
                return -EINVAL;
 
-       if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags))
+       if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags))
                return -EALREADY;
 
-       change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags);
+       change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags);
 
        return count;
 }
@@ -900,177 +886,113 @@ static int conn_max_interval_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
                        conn_max_interval_set, "%llu\n");
 
-static int adv_channel_map_set(void *data, u64 val)
+static int conn_latency_set(void *data, u64 val)
 {
        struct hci_dev *hdev = data;
 
-       if (val < 0x01 || val > 0x07)
+       if (val > 0x01f3)
                return -EINVAL;
 
        hci_dev_lock(hdev);
-       hdev->le_adv_channel_map = val;
+       hdev->le_conn_latency = val;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-static int adv_channel_map_get(void *data, u64 *val)
+static int conn_latency_get(void *data, u64 *val)
 {
        struct hci_dev *hdev = data;
 
        hci_dev_lock(hdev);
-       *val = hdev->le_adv_channel_map;
+       *val = hdev->le_conn_latency;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
-                       adv_channel_map_set, "%llu\n");
-
-static ssize_t lowpan_read(struct file *file, char __user *user_buf,
-                          size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[3];
-
-       buf[0] = test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags) ? 'Y' : 'N';
-       buf[1] = '\n';
-       buf[2] = '\0';
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
+DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get,
+                       conn_latency_set, "%llu\n");
 
-static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer,
-                           size_t count, loff_t *position)
+static int supervision_timeout_set(void *data, u64 val)
 {
-       struct hci_dev *hdev = fp->private_data;
-       bool enable;
-       char buf[32];
-       size_t buf_size = min(count, (sizeof(buf)-1));
-
-       if (copy_from_user(buf, user_buffer, buf_size))
-               return -EFAULT;
-
-       buf[buf_size] = '\0';
+       struct hci_dev *hdev = data;
 
-       if (strtobool(buf, &enable) < 0)
+       if (val < 0x000a || val > 0x0c80)
                return -EINVAL;
 
-       if (enable == test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
-               return -EALREADY;
-
-       change_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+       hci_dev_lock(hdev);
+       hdev->le_supv_timeout = val;
+       hci_dev_unlock(hdev);
 
-       return count;
+       return 0;
 }
 
-static const struct file_operations lowpan_debugfs_fops = {
-       .open           = simple_open,
-       .read           = lowpan_read,
-       .write          = lowpan_write,
-       .llseek         = default_llseek,
-};
-
-static int le_auto_conn_show(struct seq_file *sf, void *ptr)
+static int supervision_timeout_get(void *data, u64 *val)
 {
-       struct hci_dev *hdev = sf->private;
-       struct hci_conn_params *p;
+       struct hci_dev *hdev = data;
 
        hci_dev_lock(hdev);
-
-       list_for_each_entry(p, &hdev->le_conn_params, list) {
-               seq_printf(sf, "%pMR %u %u\n", &p->addr, p->addr_type,
-                          p->auto_connect);
-       }
-
+       *val = hdev->le_supv_timeout;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-static int le_auto_conn_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, le_auto_conn_show, inode->i_private);
-}
+DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get,
+                       supervision_timeout_set, "%llu\n");
 
-static ssize_t le_auto_conn_write(struct file *file, const char __user *data,
-                                 size_t count, loff_t *offset)
+static int adv_channel_map_set(void *data, u64 val)
 {
-       struct seq_file *sf = file->private_data;
-       struct hci_dev *hdev = sf->private;
-       u8 auto_connect = 0;
-       bdaddr_t addr;
-       u8 addr_type;
-       char *buf;
-       int err = 0;
-       int n;
+       struct hci_dev *hdev = data;
 
-       /* Don't allow partial write */
-       if (*offset != 0)
+       if (val < 0x01 || val > 0x07)
                return -EINVAL;
 
-       if (count < 3)
-               return -EINVAL;
+       hci_dev_lock(hdev);
+       hdev->le_adv_channel_map = val;
+       hci_dev_unlock(hdev);
 
-       buf = memdup_user(data, count);
-       if (IS_ERR(buf))
-               return PTR_ERR(buf);
+       return 0;
+}
 
-       if (memcmp(buf, "add", 3) == 0) {
-               n = sscanf(&buf[4], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu %hhu",
-                          &addr.b[5], &addr.b[4], &addr.b[3], &addr.b[2],
-                          &addr.b[1], &addr.b[0], &addr_type,
-                          &auto_connect);
+static int adv_channel_map_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
 
-               if (n < 7) {
-                       err = -EINVAL;
-                       goto done;
-               }
+       hci_dev_lock(hdev);
+       *val = hdev->le_adv_channel_map;
+       hci_dev_unlock(hdev);
 
-               hci_dev_lock(hdev);
-               err = hci_conn_params_add(hdev, &addr, addr_type, auto_connect,
-                                         hdev->le_conn_min_interval,
-                                         hdev->le_conn_max_interval);
-               hci_dev_unlock(hdev);
+       return 0;
+}
 
-               if (err)
-                       goto done;
-       } else if (memcmp(buf, "del", 3) == 0) {
-               n = sscanf(&buf[4], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu",
-                          &addr.b[5], &addr.b[4], &addr.b[3], &addr.b[2],
-                          &addr.b[1], &addr.b[0], &addr_type);
+DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
+                       adv_channel_map_set, "%llu\n");
 
-               if (n < 7) {
-                       err = -EINVAL;
-                       goto done;
-               }
+static int device_list_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct hci_conn_params *p;
 
-               hci_dev_lock(hdev);
-               hci_conn_params_del(hdev, &addr, addr_type);
-               hci_dev_unlock(hdev);
-       } else if (memcmp(buf, "clr", 3) == 0) {
-               hci_dev_lock(hdev);
-               hci_conn_params_clear(hdev);
-               hci_pend_le_conns_clear(hdev);
-               hci_update_background_scan(hdev);
-               hci_dev_unlock(hdev);
-       } else {
-               err = -EINVAL;
+       hci_dev_lock(hdev);
+       list_for_each_entry(p, &hdev->le_conn_params, list) {
+               seq_printf(f, "%pMR %u %u\n", &p->addr, p->addr_type,
+                          p->auto_connect);
        }
+       hci_dev_unlock(hdev);
 
-done:
-       kfree(buf);
+       return 0;
+}
 
-       if (err)
-               return err;
-       else
-               return count;
+static int device_list_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, device_list_show, inode->i_private);
 }
 
-static const struct file_operations le_auto_conn_fops = {
-       .open           = le_auto_conn_open,
+static const struct file_operations device_list_fops = {
+       .open           = device_list_open,
        .read           = seq_read,
-       .write          = le_auto_conn_write,
        .llseek         = seq_lseek,
        .release        = single_release,
 };
@@ -1426,9 +1348,6 @@ static void le_setup(struct hci_request *req)
        /* Read LE Supported States */
        hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL);
 
-       /* Read LE Advertising Channel TX Power */
-       hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
-
        /* Read LE White List Size */
        hci_req_add(req, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL);
 
@@ -1503,14 +1422,17 @@ static void hci_setup_event_mask(struct hci_request *req)
                /* Use a different default for LE-only devices */
                memset(events, 0, sizeof(events));
                events[0] |= 0x10; /* Disconnection Complete */
-               events[0] |= 0x80; /* Encryption Change */
                events[1] |= 0x08; /* Read Remote Version Information Complete */
                events[1] |= 0x20; /* Command Complete */
                events[1] |= 0x40; /* Command Status */
                events[1] |= 0x80; /* Hardware Error */
                events[2] |= 0x04; /* Number of Completed Packets */
                events[3] |= 0x02; /* Data Buffer Overflow */
-               events[5] |= 0x80; /* Encryption Key Refresh Complete */
+
+               if (hdev->le_features[0] & HCI_LE_ENCRYPTION) {
+                       events[0] |= 0x80; /* Encryption Change */
+                       events[5] |= 0x80; /* Encryption Key Refresh Complete */
+               }
        }
 
        if (lmp_inq_rssi_capable(hdev))
@@ -1549,13 +1471,6 @@ static void hci_setup_event_mask(struct hci_request *req)
                events[7] |= 0x20;      /* LE Meta-Event */
 
        hci_req_add(req, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
-
-       if (lmp_le_capable(hdev)) {
-               memset(events, 0, sizeof(events));
-               events[0] = 0x1f;
-               hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK,
-                           sizeof(events), events);
-       }
 }
 
 static void hci_init2_req(struct hci_request *req, unsigned long opt)
@@ -1570,8 +1485,6 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
        if (lmp_le_capable(hdev))
                le_setup(req);
 
-       hci_setup_event_mask(req);
-
        /* AVM Berlin (31), aka "BlueFRITZ!", doesn't support the read
         * local supported commands HCI command.
         */
@@ -1688,7 +1601,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
        }
 
        /* Enable Authenticated Payload Timeout Expired event if supported */
-       if (lmp_ping_capable(hdev))
+       if (lmp_ping_capable(hdev) || hdev->le_features[0] & HCI_LE_PING)
                events[2] |= 0x80;
 
        hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
@@ -1699,6 +1612,8 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
        struct hci_dev *hdev = req->hdev;
        u8 p;
 
+       hci_setup_event_mask(req);
+
        /* Some Broadcom based Bluetooth controllers do not support the
         * Delete Stored Link Key command. They are clearly indicating its
         * absence in the bit mask of supported commands.
@@ -1725,8 +1640,33 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
        if (hdev->commands[5] & 0x10)
                hci_setup_link_policy(req);
 
-       if (lmp_le_capable(hdev))
+       if (lmp_le_capable(hdev)) {
+               u8 events[8];
+
+               memset(events, 0, sizeof(events));
+               events[0] = 0x0f;
+
+               if (hdev->le_features[0] & HCI_LE_ENCRYPTION)
+                       events[0] |= 0x10;      /* LE Long Term Key Request */
+
+               /* If controller supports the Connection Parameters Request
+                * Link Layer Procedure, enable the corresponding event.
+                */
+               if (hdev->le_features[0] & HCI_LE_CONN_PARAM_REQ_PROC)
+                       events[0] |= 0x20;      /* LE Remote Connection
+                                                * Parameter Request
+                                                */
+
+               hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events),
+                           events);
+
+               if (hdev->commands[25] & 0x40) {
+                       /* Read LE Advertising Channel TX Power */
+                       hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
+               }
+
                hci_set_le_support(req);
+       }
 
        /* Read features beyond page 1 if available */
        for (p = 2; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
@@ -1752,7 +1692,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
 
        /* Enable Secure Connections if supported and configured */
        if ((lmp_sc_capable(hdev) ||
-            test_bit(HCI_FORCE_SC, &hdev->dev_flags)) &&
+            test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) &&
            test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
                u8 support = 0x01;
                hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
@@ -1809,6 +1749,8 @@ static int __hci_init(struct hci_dev *hdev)
        debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
        debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
                            &blacklist_fops);
+       debugfs_create_file("whitelist", 0444, hdev->debugfs, hdev,
+                           &whitelist_fops);
        debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
 
        debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
@@ -1830,8 +1772,6 @@ static int __hci_init(struct hci_dev *hdev)
        if (lmp_ssp_capable(hdev)) {
                debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
                                    hdev, &auto_accept_delay_fops);
-               debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs,
-                                   hdev, &ssp_debug_mode_fops);
                debugfs_create_file("force_sc_support", 0644, hdev->debugfs,
                                    hdev, &force_sc_support_fops);
                debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
@@ -1879,12 +1819,14 @@ static int __hci_init(struct hci_dev *hdev)
                                    hdev, &conn_min_interval_fops);
                debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
                                    hdev, &conn_max_interval_fops);
+               debugfs_create_file("conn_latency", 0644, hdev->debugfs,
+                                   hdev, &conn_latency_fops);
+               debugfs_create_file("supervision_timeout", 0644, hdev->debugfs,
+                                   hdev, &supervision_timeout_fops);
                debugfs_create_file("adv_channel_map", 0644, hdev->debugfs,
                                    hdev, &adv_channel_map_fops);
-               debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
-                                   &lowpan_debugfs_fops);
-               debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev,
-                                   &le_auto_conn_fops);
+               debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
+                                   &device_list_fops);
                debugfs_create_u16("discov_interleaved_timeout", 0644,
                                   hdev->debugfs,
                                   &hdev->discov_interleaved_timeout);
@@ -1893,6 +1835,38 @@ static int __hci_init(struct hci_dev *hdev)
        return 0;
 }
 
+static void hci_init0_req(struct hci_request *req, unsigned long opt)
+{
+       struct hci_dev *hdev = req->hdev;
+
+       BT_DBG("%s %ld", hdev->name, opt);
+
+       /* Reset */
+       if (!test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks))
+               hci_reset_req(req, 0);
+
+       /* Read Local Version */
+       hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
+
+       /* Read BD Address */
+       if (hdev->set_bdaddr)
+               hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL);
+}
+
+static int __hci_unconf_init(struct hci_dev *hdev)
+{
+       int err;
+
+       if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
+               return 0;
+
+       err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
 static void hci_scan_req(struct hci_request *req, unsigned long opt)
 {
        __u8 scan = opt;
@@ -1973,16 +1947,20 @@ bool hci_discovery_active(struct hci_dev *hdev)
 
 void hci_discovery_set_state(struct hci_dev *hdev, int state)
 {
+       int old_state = hdev->discovery.state;
+
        BT_DBG("%s state %u -> %u", hdev->name, hdev->discovery.state, state);
 
-       if (hdev->discovery.state == state)
+       if (old_state == state)
                return;
 
+       hdev->discovery.state = state;
+
        switch (state) {
        case DISCOVERY_STOPPED:
                hci_update_background_scan(hdev);
 
-               if (hdev->discovery.state != DISCOVERY_STARTING)
+               if (old_state != DISCOVERY_STARTING)
                        mgmt_discovering(hdev, 0);
                break;
        case DISCOVERY_STARTING:
@@ -1995,8 +1973,6 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state)
        case DISCOVERY_STOPPING:
                break;
        }
-
-       hdev->discovery.state = state;
 }
 
 void hci_inquiry_cache_flush(struct hci_dev *hdev)
@@ -2083,22 +2059,24 @@ void hci_inquiry_cache_update_resolve(struct hci_dev *hdev,
        list_add(&ie->list, pos);
 }
 
-bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
-                             bool name_known, bool *ssp)
+u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
+                            bool name_known)
 {
        struct discovery_state *cache = &hdev->discovery;
        struct inquiry_entry *ie;
+       u32 flags = 0;
 
        BT_DBG("cache %p, %pMR", cache, &data->bdaddr);
 
        hci_remove_remote_oob_data(hdev, &data->bdaddr);
 
-       *ssp = data->ssp_mode;
+       if (!data->ssp_mode)
+               flags |= MGMT_DEV_FOUND_LEGACY_PAIRING;
 
        ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr);
        if (ie) {
-               if (ie->data.ssp_mode)
-                       *ssp = true;
+               if (!ie->data.ssp_mode)
+                       flags |= MGMT_DEV_FOUND_LEGACY_PAIRING;
 
                if (ie->name_state == NAME_NEEDED &&
                    data->rssi != ie->data.rssi) {
@@ -2110,9 +2088,11 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
        }
 
        /* Entry not in the cache. Add new one. */
-       ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC);
-       if (!ie)
-               return false;
+       ie = kzalloc(sizeof(*ie), GFP_KERNEL);
+       if (!ie) {
+               flags |= MGMT_DEV_FOUND_CONFIRM_NAME;
+               goto done;
+       }
 
        list_add(&ie->all, &cache->all);
 
@@ -2135,9 +2115,10 @@ update:
        cache->timestamp = jiffies;
 
        if (ie->name_state == NAME_NOT_KNOWN)
-               return false;
+               flags |= MGMT_DEV_FOUND_CONFIRM_NAME;
 
-       return true;
+done:
+       return flags;
 }
 
 static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
@@ -2213,6 +2194,11 @@ int hci_inquiry(void __user *arg)
                goto done;
        }
 
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
        if (hdev->dev_type != HCI_BREDR) {
                err = -EOPNOTSUPP;
                goto done;
@@ -2295,7 +2281,8 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                goto done;
        }
 
-       if (!test_bit(HCI_SETUP, &hdev->dev_flags)) {
+       if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
+           !test_bit(HCI_CONFIG, &hdev->dev_flags)) {
                /* Check for rfkill but allow the HCI setup stage to
                 * proceed (which in itself doesn't cause any RF activity).
                 */
@@ -2338,14 +2325,47 @@ static int hci_dev_do_open(struct hci_dev *hdev)
        atomic_set(&hdev->cmd_cnt, 1);
        set_bit(HCI_INIT, &hdev->flags);
 
-       if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags))
-               ret = hdev->setup(hdev);
+       if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               if (hdev->setup)
+                       ret = hdev->setup(hdev);
 
-       if (!ret) {
-               if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
-                       set_bit(HCI_RAW, &hdev->flags);
+               /* The transport driver can set these quirks before
+                * creating the HCI device or in its setup callback.
+                *
+                * In case any of them is set, the controller has to
+                * start up as unconfigured.
+                */
+               if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
+                   test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks))
+                       set_bit(HCI_UNCONFIGURED, &hdev->dev_flags);
+
+               /* For an unconfigured controller it is required to
+                * read at least the version information provided by
+                * the Read Local Version Information command.
+                *
+                * If the set_bdaddr driver callback is provided, then
+                * also the original Bluetooth public device address
+                * will be read using the Read BD Address command.
+                */
+               if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+                       ret = __hci_unconf_init(hdev);
+       }
 
-               if (!test_bit(HCI_RAW, &hdev->flags) &&
+       if (test_bit(HCI_CONFIG, &hdev->dev_flags)) {
+               /* If public address change is configured, ensure that
+                * the address gets programmed. If the driver does not
+                * support changing the public address, fail the power
+                * on procedure.
+                */
+               if (bacmp(&hdev->public_addr, BDADDR_ANY) &&
+                   hdev->set_bdaddr)
+                       ret = hdev->set_bdaddr(hdev, &hdev->public_addr);
+               else
+                       ret = -EADDRNOTAVAIL;
+       }
+
+       if (!ret) {
+               if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
                    !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
                        ret = __hci_init(hdev);
        }
@@ -2358,6 +2378,8 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                set_bit(HCI_UP, &hdev->flags);
                hci_notify(hdev, HCI_DEV_UP);
                if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
+                   !test_bit(HCI_CONFIG, &hdev->dev_flags) &&
+                   !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
                    !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
                    hdev->dev_type == HCI_BREDR) {
                        hci_dev_lock(hdev);
@@ -2382,7 +2404,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                }
 
                hdev->close(hdev);
-               hdev->flags = 0;
+               hdev->flags &= BIT(HCI_RAW);
        }
 
 done:
@@ -2401,6 +2423,21 @@ int hci_dev_open(__u16 dev)
        if (!hdev)
                return -ENODEV;
 
+       /* Devices that are marked as unconfigured can only be powered
+        * up as user channel. Trying to bring them up as normal devices
+        * will result into a failure. Only user channel operation is
+        * possible.
+        *
+        * When this function is called for a user channel, the flag
+        * HCI_USER_CHANNEL will be set first before attempting to
+        * open the device.
+        */
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
+           !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
        /* We need to ensure that no other power on/off work is pending
         * before proceeding to call hci_dev_do_open. This is
         * particularly important if the setup procedure has not yet
@@ -2415,13 +2452,34 @@ int hci_dev_open(__u16 dev)
         */
        flush_workqueue(hdev->req_workqueue);
 
+       /* For controllers not using the management interface and that
+        * are brought up using legacy ioctl, set the HCI_PAIRABLE bit
+        * so that pairing works for them. Once the management interface
+        * is in use this bit will be cleared again and userspace has
+        * to explicitly enable it.
+        */
+       if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
+           !test_bit(HCI_MGMT, &hdev->dev_flags))
+               set_bit(HCI_PAIRABLE, &hdev->dev_flags);
+
        err = hci_dev_do_open(hdev);
 
+done:
        hci_dev_put(hdev);
-
        return err;
 }
 
+/* This function requires the caller holds hdev->lock */
+static void hci_pend_le_actions_clear(struct hci_dev *hdev)
+{
+       struct hci_conn_params *p;
+
+       list_for_each_entry(p, &hdev->le_conn_params, list)
+               list_del_init(&p->action);
+
+       BT_DBG("All LE pending actions cleared");
+}
+
 static int hci_dev_do_close(struct hci_dev *hdev)
 {
        BT_DBG("%s %p", hdev->name, hdev);
@@ -2432,7 +2490,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        hci_req_lock(hdev);
 
        if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
-               del_timer_sync(&hdev->cmd_timer);
+               cancel_delayed_work_sync(&hdev->cmd_timer);
                hci_req_unlock(hdev);
                return 0;
        }
@@ -2459,7 +2517,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        hci_dev_lock(hdev);
        hci_inquiry_cache_flush(hdev);
        hci_conn_hash_flush(hdev);
-       hci_pend_le_conns_clear(hdev);
+       hci_pend_le_actions_clear(hdev);
        hci_dev_unlock(hdev);
 
        hci_notify(hdev, HCI_DEV_DOWN);
@@ -2470,8 +2528,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        /* Reset device */
        skb_queue_purge(&hdev->cmd_q);
        atomic_set(&hdev->cmd_cnt, 1);
-       if (!test_bit(HCI_RAW, &hdev->flags) &&
-           !test_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
+       if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags) &&
+           !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
            test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) {
                set_bit(HCI_INIT, &hdev->flags);
                __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
@@ -2488,7 +2546,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
 
        /* Drop last sent command */
        if (hdev->sent_cmd) {
-               del_timer_sync(&hdev->cmd_timer);
+               cancel_delayed_work_sync(&hdev->cmd_timer);
                kfree_skb(hdev->sent_cmd);
                hdev->sent_cmd = NULL;
        }
@@ -2501,7 +2559,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
        hdev->close(hdev);
 
        /* Clear flags */
-       hdev->flags = 0;
+       hdev->flags &= BIT(HCI_RAW);
        hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
 
        if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
@@ -2570,6 +2628,11 @@ int hci_dev_reset(__u16 dev)
                goto done;
        }
 
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+               ret = -EOPNOTSUPP;
+               goto done;
+       }
+
        /* Drop queues */
        skb_queue_purge(&hdev->rx_q);
        skb_queue_purge(&hdev->cmd_q);
@@ -2585,8 +2648,7 @@ int hci_dev_reset(__u16 dev)
        atomic_set(&hdev->cmd_cnt, 1);
        hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
 
-       if (!test_bit(HCI_RAW, &hdev->flags))
-               ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
+       ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT);
 
 done:
        hci_req_unlock(hdev);
@@ -2608,6 +2670,11 @@ int hci_dev_reset_stat(__u16 dev)
                goto done;
        }
 
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+               ret = -EOPNOTSUPP;
+               goto done;
+       }
+
        memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
 
 done:
@@ -2615,6 +2682,42 @@ done:
        return ret;
 }
 
+static void hci_update_scan_state(struct hci_dev *hdev, u8 scan)
+{
+       bool conn_changed, discov_changed;
+
+       BT_DBG("%s scan 0x%02x", hdev->name, scan);
+
+       if ((scan & SCAN_PAGE))
+               conn_changed = !test_and_set_bit(HCI_CONNECTABLE,
+                                                &hdev->dev_flags);
+       else
+               conn_changed = test_and_clear_bit(HCI_CONNECTABLE,
+                                                 &hdev->dev_flags);
+
+       if ((scan & SCAN_INQUIRY)) {
+               discov_changed = !test_and_set_bit(HCI_DISCOVERABLE,
+                                                  &hdev->dev_flags);
+       } else {
+               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
+               discov_changed = test_and_clear_bit(HCI_DISCOVERABLE,
+                                                   &hdev->dev_flags);
+       }
+
+       if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+               return;
+
+       if (conn_changed || discov_changed) {
+               /* In case this was disabled through mgmt */
+               set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
+
+               if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+                       mgmt_update_adv_data(hdev);
+
+               mgmt_new_settings(hdev);
+       }
+}
+
 int hci_dev_cmd(unsigned int cmd, void __user *arg)
 {
        struct hci_dev *hdev;
@@ -2633,6 +2736,11 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
                goto done;
        }
 
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+               err = -EOPNOTSUPP;
+               goto done;
+       }
+
        if (hdev->dev_type != HCI_BREDR) {
                err = -EOPNOTSUPP;
                goto done;
@@ -2670,6 +2778,12 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
        case HCISETSCAN:
                err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt,
                                   HCI_INIT_TIMEOUT);
+
+               /* Ensure that the connectable and discoverable states
+                * get correctly modified as this was a non-mgmt change.
+                */
+               if (!err)
+                       hci_update_scan_state(hdev, dr.dev_opt);
                break;
 
        case HCISETLINKPOL:
@@ -2730,14 +2844,17 @@ int hci_get_dev_list(void __user *arg)
 
        read_lock(&hci_dev_list_lock);
        list_for_each_entry(hdev, &hci_dev_list, list) {
-               if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
-                       cancel_delayed_work(&hdev->power_off);
+               unsigned long flags = hdev->flags;
 
-               if (!test_bit(HCI_MGMT, &hdev->dev_flags))
-                       set_bit(HCI_PAIRABLE, &hdev->dev_flags);
+               /* When the auto-off is configured it means the transport
+                * is running, but in that case still indicate that the
+                * device is actually down.
+                */
+               if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+                       flags &= ~BIT(HCI_UP);
 
                (dr + n)->dev_id  = hdev->id;
-               (dr + n)->dev_opt = hdev->flags;
+               (dr + n)->dev_opt = flags;
 
                if (++n >= dev_num)
                        break;
@@ -2757,6 +2874,7 @@ int hci_get_dev_info(void __user *arg)
 {
        struct hci_dev *hdev;
        struct hci_dev_info di;
+       unsigned long flags;
        int err = 0;
 
        if (copy_from_user(&di, arg, sizeof(di)))
@@ -2766,16 +2884,19 @@ int hci_get_dev_info(void __user *arg)
        if (!hdev)
                return -ENODEV;
 
-       if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
-               cancel_delayed_work_sync(&hdev->power_off);
-
-       if (!test_bit(HCI_MGMT, &hdev->dev_flags))
-               set_bit(HCI_PAIRABLE, &hdev->dev_flags);
+       /* When the auto-off is configured it means the transport
+        * is running, but in that case still indicate that the
+        * device is actually down.
+        */
+       if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+               flags = hdev->flags & ~BIT(HCI_UP);
+       else
+               flags = hdev->flags;
 
        strcpy(di.name, hdev->name);
        di.bdaddr   = hdev->bdaddr;
        di.type     = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4);
-       di.flags    = hdev->flags;
+       di.flags    = flags;
        di.pkt_type = hdev->pkt_type;
        if (lmp_bredr_capable(hdev)) {
                di.acl_mtu  = hdev->acl_mtu;
@@ -2815,7 +2936,8 @@ static int hci_rfkill_set_block(void *data, bool blocked)
 
        if (blocked) {
                set_bit(HCI_RFKILLED, &hdev->dev_flags);
-               if (!test_bit(HCI_SETUP, &hdev->dev_flags))
+               if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
+                   !test_bit(HCI_CONFIG, &hdev->dev_flags))
                        hci_dev_do_close(hdev);
        } else {
                clear_bit(HCI_RFKILLED, &hdev->dev_flags);
@@ -2846,6 +2968,7 @@ static void hci_power_on(struct work_struct *work)
         * valid, it is important to turn the device back off.
         */
        if (test_bit(HCI_RFKILLED, &hdev->dev_flags) ||
+           test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) ||
            (hdev->dev_type == HCI_BREDR &&
             !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
             !bacmp(&hdev->static_addr, BDADDR_ANY))) {
@@ -2856,8 +2979,34 @@ static void hci_power_on(struct work_struct *work)
                                   HCI_AUTO_OFF_TIMEOUT);
        }
 
-       if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags))
+       if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) {
+               /* For unconfigured devices, set the HCI_RAW flag
+                * so that userspace can easily identify them.
+                */
+               if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+                       set_bit(HCI_RAW, &hdev->flags);
+
+               /* For fully configured devices, this will send
+                * the Index Added event. For unconfigured devices,
+                * it will send Unconfigued Index Added event.
+                *
+                * Devices with HCI_QUIRK_RAW_DEVICE are ignored
+                * and no event will be send.
+                */
+               mgmt_index_added(hdev);
+       } else if (test_and_clear_bit(HCI_CONFIG, &hdev->dev_flags)) {
+               /* When the controller is now configured, then it
+                * is important to clear the HCI_RAW flag.
+                */
+               if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+                       clear_bit(HCI_RAW, &hdev->flags);
+
+               /* Powering on the controller with HCI_CONFIG set only
+                * happens with the transition from unconfigured to
+                * configured. This will send the Index Added event.
+                */
                mgmt_index_added(hdev);
+       }
 }
 
 static void hci_power_off(struct work_struct *work)
@@ -2972,16 +3121,16 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
        return false;
 }
 
-static bool ltk_type_master(u8 type)
+static u8 ltk_role(u8 type)
 {
-       if (type == HCI_SMP_STK || type == HCI_SMP_LTK)
-               return true;
+       if (type == SMP_LTK)
+               return HCI_ROLE_MASTER;
 
-       return false;
+       return HCI_ROLE_SLAVE;
 }
 
 struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
-                            bool master)
+                            u8 role)
 {
        struct smp_ltk *k;
 
@@ -2989,7 +3138,7 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
                if (k->ediv != ediv || k->rand != rand)
                        continue;
 
-               if (ltk_type_master(k->type) != master)
+               if (ltk_role(k->type) != role)
                        continue;
 
                return k;
@@ -2999,14 +3148,14 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
 }
 
 struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 addr_type, bool master)
+                                    u8 addr_type, u8 role)
 {
        struct smp_ltk *k;
 
        list_for_each_entry(k, &hdev->long_term_keys, list)
                if (addr_type == k->bdaddr_type &&
                    bacmp(bdaddr, &k->bdaddr) == 0 &&
-                   ltk_type_master(k->type) == master)
+                   ltk_role(k->type) == role)
                        return k;
 
        return NULL;
@@ -3049,12 +3198,12 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
        return NULL;
 }
 
-int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
-                    bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
+struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
+                                 bdaddr_t *bdaddr, u8 *val, u8 type,
+                                 u8 pin_len, bool *persistent)
 {
        struct link_key *key, *old_key;
        u8 old_key_type;
-       bool persistent;
 
        old_key = hci_find_link_key(hdev, bdaddr);
        if (old_key) {
@@ -3064,7 +3213,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
                old_key_type = conn ? conn->key_type : 0xff;
                key = kzalloc(sizeof(*key), GFP_KERNEL);
                if (!key)
-                       return -ENOMEM;
+                       return NULL;
                list_add(&key->list, &hdev->link_keys);
        }
 
@@ -3089,17 +3238,11 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
        else
                key->type = type;
 
-       if (!new_key)
-               return 0;
-
-       persistent = hci_persistent_key(hdev, conn, type, old_key_type);
+       if (persistent)
+               *persistent = hci_persistent_key(hdev, conn, type,
+                                                old_key_type);
 
-       mgmt_new_link_key(hdev, key, persistent);
-
-       if (conn)
-               conn->flush_key = !persistent;
-
-       return 0;
+       return key;
 }
 
 struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -3107,9 +3250,9 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
                            u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand)
 {
        struct smp_ltk *key, *old_key;
-       bool master = ltk_type_master(type);
+       u8 role = ltk_role(type);
 
-       old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, master);
+       old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, role);
        if (old_key)
                key = old_key;
        else {
@@ -3205,9 +3348,10 @@ void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
 }
 
 /* HCI command timer function */
-static void hci_cmd_timeout(unsigned long arg)
+static void hci_cmd_timeout(struct work_struct *work)
 {
-       struct hci_dev *hdev = (void *) arg;
+       struct hci_dev *hdev = container_of(work, struct hci_dev,
+                                           cmd_timer.work);
 
        if (hdev->sent_cmd) {
                struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data;
@@ -3313,12 +3457,12 @@ int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
        return 0;
 }
 
-struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
+struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list,
                                         bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *b;
 
-       list_for_each_entry(b, &hdev->blacklist, list) {
+       list_for_each_entry(b, bdaddr_list, list) {
                if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type)
                        return b;
        }
@@ -3326,11 +3470,11 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
        return NULL;
 }
 
-static void hci_blacklist_clear(struct hci_dev *hdev)
+void hci_bdaddr_list_clear(struct list_head *bdaddr_list)
 {
        struct list_head *p, *n;
 
-       list_for_each_safe(p, n, &hdev->blacklist) {
+       list_for_each_safe(p, n, bdaddr_list) {
                struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
 
                list_del(p);
@@ -3338,99 +3482,38 @@ static void hci_blacklist_clear(struct hci_dev *hdev)
        }
 }
 
-int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
+int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
        if (!bacmp(bdaddr, BDADDR_ANY))
                return -EBADF;
 
-       if (hci_blacklist_lookup(hdev, bdaddr, type))
+       if (hci_bdaddr_list_lookup(list, bdaddr, type))
                return -EEXIST;
 
-       entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
        if (!entry)
                return -ENOMEM;
 
        bacpy(&entry->bdaddr, bdaddr);
        entry->bdaddr_type = type;
 
-       list_add(&entry->list, &hdev->blacklist);
+       list_add(&entry->list, list);
 
-       return mgmt_device_blocked(hdev, bdaddr, type);
+       return 0;
 }
 
-int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
+int hci_bdaddr_list_del(struct list_head *list, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
        if (!bacmp(bdaddr, BDADDR_ANY)) {
-               hci_blacklist_clear(hdev);
+               hci_bdaddr_list_clear(list);
                return 0;
        }
 
-       entry = hci_blacklist_lookup(hdev, bdaddr, type);
-       if (!entry)
-               return -ENOENT;
-
-       list_del(&entry->list);
-       kfree(entry);
-
-       return mgmt_device_unblocked(hdev, bdaddr, type);
-}
-
-struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev,
-                                         bdaddr_t *bdaddr, u8 type)
-{
-       struct bdaddr_list *b;
-
-       list_for_each_entry(b, &hdev->le_white_list, list) {
-               if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type)
-                       return b;
-       }
-
-       return NULL;
-}
-
-void hci_white_list_clear(struct hci_dev *hdev)
-{
-       struct list_head *p, *n;
-
-       list_for_each_safe(p, n, &hdev->le_white_list) {
-               struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list);
-
-               list_del(p);
-               kfree(b);
-       }
-}
-
-int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
-{
-       struct bdaddr_list *entry;
-
-       if (!bacmp(bdaddr, BDADDR_ANY))
-               return -EBADF;
-
-       entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
-       if (!entry)
-               return -ENOMEM;
-
-       bacpy(&entry->bdaddr, bdaddr);
-       entry->bdaddr_type = type;
-
-       list_add(&entry->list, &hdev->le_white_list);
-
-       return 0;
-}
-
-int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
-{
-       struct bdaddr_list *entry;
-
-       if (!bacmp(bdaddr, BDADDR_ANY))
-               return -EBADF;
-
-       entry = hci_white_list_lookup(hdev, bdaddr, type);
+       entry = hci_bdaddr_list_lookup(list, bdaddr, type);
        if (!entry)
                return -ENOENT;
 
@@ -3446,6 +3529,10 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
 {
        struct hci_conn_params *params;
 
+       /* The conn params list only contains identity addresses */
+       if (!hci_is_identity_address(addr, addr_type))
+               return NULL;
+
        list_for_each_entry(params, &hdev->le_conn_params, list) {
                if (bacmp(&params->addr, addr) == 0 &&
                    params->addr_type == addr_type) {
@@ -3473,62 +3560,97 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
        return true;
 }
 
-static bool is_identity_address(bdaddr_t *addr, u8 addr_type)
+/* This function requires the caller holds hdev->lock */
+struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
+                                                 bdaddr_t *addr, u8 addr_type)
 {
-       if (addr_type == ADDR_LE_DEV_PUBLIC)
-               return true;
+       struct hci_conn_params *param;
 
-       /* Check for Random Static address type */
-       if ((addr->b[5] & 0xc0) == 0xc0)
-               return true;
+       /* The list only contains identity addresses */
+       if (!hci_is_identity_address(addr, addr_type))
+               return NULL;
 
-       return false;
+       list_for_each_entry(param, list, action) {
+               if (bacmp(&param->addr, addr) == 0 &&
+                   param->addr_type == addr_type)
+                       return param;
+       }
+
+       return NULL;
 }
 
 /* This function requires the caller holds hdev->lock */
-int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
-                       u8 auto_connect, u16 conn_min_interval,
-                       u16 conn_max_interval)
+struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
+                                           bdaddr_t *addr, u8 addr_type)
 {
        struct hci_conn_params *params;
 
-       if (!is_identity_address(addr, addr_type))
-               return -EINVAL;
+       if (!hci_is_identity_address(addr, addr_type))
+               return NULL;
 
        params = hci_conn_params_lookup(hdev, addr, addr_type);
        if (params)
-               goto update;
+               return params;
 
        params = kzalloc(sizeof(*params), GFP_KERNEL);
        if (!params) {
                BT_ERR("Out of memory");
-               return -ENOMEM;
+               return NULL;
        }
 
        bacpy(&params->addr, addr);
        params->addr_type = addr_type;
 
        list_add(&params->list, &hdev->le_conn_params);
+       INIT_LIST_HEAD(&params->action);
 
-update:
-       params->conn_min_interval = conn_min_interval;
-       params->conn_max_interval = conn_max_interval;
-       params->auto_connect = auto_connect;
+       params->conn_min_interval = hdev->le_conn_min_interval;
+       params->conn_max_interval = hdev->le_conn_max_interval;
+       params->conn_latency = hdev->le_conn_latency;
+       params->supervision_timeout = hdev->le_supv_timeout;
+       params->auto_connect = HCI_AUTO_CONN_DISABLED;
+
+       BT_DBG("addr %pMR (type %u)", addr, addr_type);
+
+       return params;
+}
+
+/* This function requires the caller holds hdev->lock */
+int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
+                       u8 auto_connect)
+{
+       struct hci_conn_params *params;
+
+       params = hci_conn_params_add(hdev, addr, addr_type);
+       if (!params)
+               return -EIO;
+
+       if (params->auto_connect == auto_connect)
+               return 0;
+
+       list_del_init(&params->action);
 
        switch (auto_connect) {
        case HCI_AUTO_CONN_DISABLED:
        case HCI_AUTO_CONN_LINK_LOSS:
-               hci_pend_le_conn_del(hdev, addr, addr_type);
+               hci_update_background_scan(hdev);
+               break;
+       case HCI_AUTO_CONN_REPORT:
+               list_add(&params->action, &hdev->pend_le_reports);
+               hci_update_background_scan(hdev);
                break;
        case HCI_AUTO_CONN_ALWAYS:
-               if (!is_connected(hdev, addr, addr_type))
-                       hci_pend_le_conn_add(hdev, addr, addr_type);
+               if (!is_connected(hdev, addr, addr_type)) {
+                       list_add(&params->action, &hdev->pend_le_conns);
+                       hci_update_background_scan(hdev);
+               }
                break;
        }
 
-       BT_DBG("addr %pMR (type %u) auto_connect %u conn_min_interval 0x%.4x "
-              "conn_max_interval 0x%.4x", addr, addr_type, auto_connect,
-              conn_min_interval, conn_max_interval);
+       params->auto_connect = auto_connect;
+
+       BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
+              auto_connect);
 
        return 0;
 }
@@ -3542,97 +3664,44 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
        if (!params)
                return;
 
-       hci_pend_le_conn_del(hdev, addr, addr_type);
-
+       list_del(&params->action);
        list_del(&params->list);
        kfree(params);
 
+       hci_update_background_scan(hdev);
+
        BT_DBG("addr %pMR (type %u)", addr, addr_type);
 }
 
 /* This function requires the caller holds hdev->lock */
-void hci_conn_params_clear(struct hci_dev *hdev)
+void hci_conn_params_clear_disabled(struct hci_dev *hdev)
 {
        struct hci_conn_params *params, *tmp;
 
        list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) {
+               if (params->auto_connect != HCI_AUTO_CONN_DISABLED)
+                       continue;
                list_del(&params->list);
                kfree(params);
        }
 
-       BT_DBG("All LE connection parameters were removed");
+       BT_DBG("All LE disabled connection parameters were removed");
 }
 
 /* This function requires the caller holds hdev->lock */
-struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev,
-                                           bdaddr_t *addr, u8 addr_type)
+void hci_conn_params_clear_all(struct hci_dev *hdev)
 {
-       struct bdaddr_list *entry;
-
-       list_for_each_entry(entry, &hdev->pend_le_conns, list) {
-               if (bacmp(&entry->bdaddr, addr) == 0 &&
-                   entry->bdaddr_type == addr_type)
-                       return entry;
-       }
-
-       return NULL;
-}
-
-/* This function requires the caller holds hdev->lock */
-void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
-{
-       struct bdaddr_list *entry;
-
-       entry = hci_pend_le_conn_lookup(hdev, addr, addr_type);
-       if (entry)
-               goto done;
+       struct hci_conn_params *params, *tmp;
 
-       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
-       if (!entry) {
-               BT_ERR("Out of memory");
-               return;
+       list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) {
+               list_del(&params->action);
+               list_del(&params->list);
+               kfree(params);
        }
 
-       bacpy(&entry->bdaddr, addr);
-       entry->bdaddr_type = addr_type;
-
-       list_add(&entry->list, &hdev->pend_le_conns);
-
-       BT_DBG("addr %pMR (type %u)", addr, addr_type);
-
-done:
-       hci_update_background_scan(hdev);
-}
-
-/* This function requires the caller holds hdev->lock */
-void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
-{
-       struct bdaddr_list *entry;
-
-       entry = hci_pend_le_conn_lookup(hdev, addr, addr_type);
-       if (!entry)
-               goto done;
-
-       list_del(&entry->list);
-       kfree(entry);
-
-       BT_DBG("addr %pMR (type %u)", addr, addr_type);
-
-done:
        hci_update_background_scan(hdev);
-}
 
-/* This function requires the caller holds hdev->lock */
-void hci_pend_le_conns_clear(struct hci_dev *hdev)
-{
-       struct bdaddr_list *entry, *tmp;
-
-       list_for_each_entry_safe(entry, tmp, &hdev->pend_le_conns, list) {
-               list_del(&entry->list);
-               kfree(entry);
-       }
-
-       BT_DBG("All LE pending connections cleared");
+       BT_DBG("All LE connection parameters were removed");
 }
 
 static void inquiry_complete(struct hci_dev *hdev, u8 status)
@@ -3722,7 +3791,7 @@ static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
         * In this kind of scenario skip the update and let the random
         * address be updated at the next cycle.
         */
-       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) ||
+       if (test_bit(HCI_LE_ADV, &hdev->dev_flags) ||
            hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) {
                BT_DBG("Deferring random address update");
                return;
@@ -3784,7 +3853,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
         * the HCI command if the current random address is already the
         * static one.
         */
-       if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ||
+       if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
            !bacmp(&hdev->bdaddr, BDADDR_ANY)) {
                *own_addr_type = ADDR_LE_DEV_RANDOM;
                if (bacmp(&hdev->static_addr, &hdev->random_addr))
@@ -3813,7 +3882,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
 void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
                               u8 *bdaddr_type)
 {
-       if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ||
+       if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
            !bacmp(&hdev->bdaddr, BDADDR_ANY)) {
                bacpy(bdaddr, &hdev->static_addr);
                *bdaddr_type = ADDR_LE_DEV_RANDOM;
@@ -3828,7 +3897,7 @@ struct hci_dev *hci_alloc_dev(void)
 {
        struct hci_dev *hdev;
 
-       hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL);
+       hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
        if (!hdev)
                return NULL;
 
@@ -3837,6 +3906,7 @@ struct hci_dev *hci_alloc_dev(void)
        hdev->link_mode = (HCI_LM_ACCEPT);
        hdev->num_iac = 0x01;           /* One IAC support is mandatory */
        hdev->io_capability = 0x03;     /* No Input No Output */
+       hdev->manufacturer = 0xffff;    /* Default to internal use */
        hdev->inq_tx_power = HCI_TX_POWER_INVALID;
        hdev->adv_tx_power = HCI_TX_POWER_INVALID;
 
@@ -3848,6 +3918,8 @@ struct hci_dev *hci_alloc_dev(void)
        hdev->le_scan_window = 0x0030;
        hdev->le_conn_min_interval = 0x0028;
        hdev->le_conn_max_interval = 0x0038;
+       hdev->le_conn_latency = 0x0000;
+       hdev->le_supv_timeout = 0x002a;
 
        hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
        hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT;
@@ -3859,6 +3931,7 @@ struct hci_dev *hci_alloc_dev(void)
 
        INIT_LIST_HEAD(&hdev->mgmt_pending);
        INIT_LIST_HEAD(&hdev->blacklist);
+       INIT_LIST_HEAD(&hdev->whitelist);
        INIT_LIST_HEAD(&hdev->uuids);
        INIT_LIST_HEAD(&hdev->link_keys);
        INIT_LIST_HEAD(&hdev->long_term_keys);
@@ -3867,6 +3940,7 @@ struct hci_dev *hci_alloc_dev(void)
        INIT_LIST_HEAD(&hdev->le_white_list);
        INIT_LIST_HEAD(&hdev->le_conn_params);
        INIT_LIST_HEAD(&hdev->pend_le_conns);
+       INIT_LIST_HEAD(&hdev->pend_le_reports);
        INIT_LIST_HEAD(&hdev->conn_hash.list);
 
        INIT_WORK(&hdev->rx_work, hci_rx_work);
@@ -3884,7 +3958,7 @@ struct hci_dev *hci_alloc_dev(void)
 
        init_waitqueue_head(&hdev->req_wait_q);
 
-       setup_timer(&hdev->cmd_timer, hci_cmd_timeout, (unsigned long) hdev);
+       INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout);
 
        hci_init_sysfs(hdev);
        discovery_init(hdev);
@@ -3906,7 +3980,7 @@ int hci_register_dev(struct hci_dev *hdev)
 {
        int id, error;
 
-       if (!hdev->open || !hdev->close)
+       if (!hdev->open || !hdev->close || !hdev->send)
                return -EINVAL;
 
        /* Do not allow HCI_AMP devices to register at index 0,
@@ -3991,6 +4065,12 @@ int hci_register_dev(struct hci_dev *hdev)
        list_add(&hdev->list, &hci_dev_list);
        write_unlock(&hci_dev_list_lock);
 
+       /* Devices that are marked for raw-only usage are unconfigured
+        * and should not be included in normal operation.
+        */
+       if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
+               set_bit(HCI_UNCONFIGURED, &hdev->dev_flags);
+
        hci_notify(hdev, HCI_DEV_REG);
        hci_dev_hold(hdev);
 
@@ -4033,7 +4113,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
        cancel_work_sync(&hdev->power_on);
 
        if (!test_bit(HCI_INIT, &hdev->flags) &&
-           !test_bit(HCI_SETUP, &hdev->dev_flags)) {
+           !test_bit(HCI_SETUP, &hdev->dev_flags) &&
+           !test_bit(HCI_CONFIG, &hdev->dev_flags)) {
                hci_dev_lock(hdev);
                mgmt_index_removed(hdev);
                hci_dev_unlock(hdev);
@@ -4061,15 +4142,15 @@ void hci_unregister_dev(struct hci_dev *hdev)
        destroy_workqueue(hdev->req_workqueue);
 
        hci_dev_lock(hdev);
-       hci_blacklist_clear(hdev);
+       hci_bdaddr_list_clear(&hdev->blacklist);
+       hci_bdaddr_list_clear(&hdev->whitelist);
        hci_uuids_clear(hdev);
        hci_link_keys_clear(hdev);
        hci_smp_ltks_clear(hdev);
        hci_smp_irks_clear(hdev);
        hci_remote_oob_data_clear(hdev);
-       hci_white_list_clear(hdev);
-       hci_conn_params_clear(hdev);
-       hci_pend_le_conns_clear(hdev);
+       hci_bdaddr_list_clear(&hdev->le_white_list);
+       hci_conn_params_clear_all(hdev);
        hci_dev_unlock(hdev);
 
        hci_dev_put(hdev);
@@ -4307,6 +4388,8 @@ EXPORT_SYMBOL(hci_unregister_cb);
 
 static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
+       int err;
+
        BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 
        /* Time stamp */
@@ -4323,8 +4406,11 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
        /* Get rid of skb owner, prior to sending to the driver. */
        skb_orphan(skb);
 
-       if (hdev->send(hdev, skb) < 0)
-               BT_ERR("%s sending frame failed", hdev->name);
+       err = hdev->send(hdev, skb);
+       if (err < 0) {
+               BT_ERR("%s sending frame failed (%d)", hdev->name, err);
+               kfree_skb(skb);
+       }
 }
 
 void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
@@ -4366,6 +4452,11 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
        return 0;
 }
 
+bool hci_req_pending(struct hci_dev *hdev)
+{
+       return (hdev->req_status == HCI_REQ_PEND);
+}
+
 static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode,
                                       u32 plen, const void *param)
 {
@@ -4798,7 +4889,7 @@ static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb)
 
 static void __check_timeout(struct hci_dev *hdev, unsigned int cnt)
 {
-       if (!test_bit(HCI_RAW, &hdev->flags)) {
+       if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
                /* ACL tx timeout must be longer than maximum
                 * link supervision timeout (40.9 seconds) */
                if (!cnt && time_after(jiffies, hdev->acl_last_tx +
@@ -4981,7 +5072,7 @@ static void hci_sched_le(struct hci_dev *hdev)
        if (!hci_conn_num(hdev, LE_LINK))
                return;
 
-       if (!test_bit(HCI_RAW, &hdev->flags)) {
+       if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
                /* LE tx timeout must be longer than maximum
                 * link supervision timeout (40.9 seconds) */
                if (!hdev->le_cnt && hdev->le_pkts &&
@@ -5226,8 +5317,7 @@ static void hci_rx_work(struct work_struct *work)
                        hci_send_to_sock(hdev, skb);
                }
 
-               if (test_bit(HCI_RAW, &hdev->flags) ||
-                   test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
+               if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
                        kfree_skb(skb);
                        continue;
                }
@@ -5287,10 +5377,10 @@ static void hci_cmd_work(struct work_struct *work)
                        atomic_dec(&hdev->cmd_cnt);
                        hci_send_frame(hdev, skb);
                        if (test_bit(HCI_RESET, &hdev->flags))
-                               del_timer(&hdev->cmd_timer);
+                               cancel_delayed_work(&hdev->cmd_timer);
                        else
-                               mod_timer(&hdev->cmd_timer,
-                                         jiffies + HCI_CMD_TIMEOUT);
+                               schedule_delayed_work(&hdev->cmd_timer,
+                                                     HCI_CMD_TIMEOUT);
                } else {
                        skb_queue_head(&hdev->cmd_q, skb);
                        queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -5314,12 +5404,13 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
        struct hci_dev *hdev = req->hdev;
        u8 own_addr_type;
 
-       /* Set require_privacy to true to avoid identification from
-        * unknown peer devices. Since this is passive scanning, no
-        * SCAN_REQ using the local identity should be sent. Mandating
-        * privacy is just an extra precaution.
+       /* Set require_privacy to false since no SCAN_REQ are send
+        * during passive scanning. Not using an unresolvable address
+        * here is important so that peer devices using direct
+        * advertising with our address will be correctly reported
+        * by the controller.
         */
-       if (hci_update_random_address(req, true, &own_addr_type))
+       if (hci_update_random_address(req, false, &own_addr_type))
                return;
 
        memset(&param_cp, 0, sizeof(param_cp));
@@ -5356,11 +5447,29 @@ void hci_update_background_scan(struct hci_dev *hdev)
        struct hci_conn *conn;
        int err;
 
+       if (!test_bit(HCI_UP, &hdev->flags) ||
+           test_bit(HCI_INIT, &hdev->flags) ||
+           test_bit(HCI_SETUP, &hdev->dev_flags) ||
+           test_bit(HCI_CONFIG, &hdev->dev_flags) ||
+           test_bit(HCI_AUTO_OFF, &hdev->dev_flags) ||
+           test_bit(HCI_UNREGISTER, &hdev->dev_flags))
+               return;
+
+       /* No point in doing scanning if LE support hasn't been enabled */
+       if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+               return;
+
+       /* If discovery is active don't interfere with it */
+       if (hdev->discovery.state != DISCOVERY_STOPPED)
+               return;
+
        hci_req_init(&req, hdev);
 
-       if (list_empty(&hdev->pend_le_conns)) {
-               /* If there is no pending LE connections, we should stop
-                * the background scanning.
+       if (list_empty(&hdev->pend_le_conns) &&
+           list_empty(&hdev->pend_le_reports)) {
+               /* If there is no pending LE connections or devices
+                * to be scanned for, we should stop the background
+                * scanning.
                 */
 
                /* If controller is not scanning we are done. */
index 640c54ec1bd29038101a06e6c1e3098e02336a8d..4c41774aa5565cbd7aa0c99ad74adbacea87e8ce 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "a2mp.h"
 #include "amp.h"
+#include "smp.h"
 
 /* Handle HCI Event packets */
 
@@ -100,12 +101,8 @@ static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_lock(hdev);
 
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
-       if (conn) {
-               if (rp->role)
-                       conn->link_mode &= ~HCI_LM_MASTER;
-               else
-                       conn->link_mode |= HCI_LM_MASTER;
-       }
+       if (conn)
+               conn->role = rp->role;
 
        hci_dev_unlock(hdev);
 }
@@ -174,12 +171,14 @@ static void hci_cc_write_def_link_policy(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
+       if (status)
+               return;
+
        sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_LINK_POLICY);
        if (!sent)
                return;
 
-       if (!status)
-               hdev->link_policy = get_unaligned_le16(sent);
+       hdev->link_policy = get_unaligned_le16(sent);
 }
 
 static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
@@ -269,28 +268,30 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
 static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
+       __u8 param;
        void *sent;
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
+       if (status)
+               return;
+
        sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_ENCRYPT_MODE);
        if (!sent)
                return;
 
-       if (!status) {
-               __u8 param = *((__u8 *) sent);
+       param = *((__u8 *) sent);
 
-               if (param)
-                       set_bit(HCI_ENCRYPT, &hdev->flags);
-               else
-                       clear_bit(HCI_ENCRYPT, &hdev->flags);
-       }
+       if (param)
+               set_bit(HCI_ENCRYPT, &hdev->flags);
+       else
+               clear_bit(HCI_ENCRYPT, &hdev->flags);
 }
 
 static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
 {
-       __u8 param, status = *((__u8 *) skb->data);
-       int old_pscan, old_iscan;
+       __u8 status = *((__u8 *) skb->data);
+       __u8 param;
        void *sent;
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -304,32 +305,19 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_lock(hdev);
 
        if (status) {
-               mgmt_write_scan_failed(hdev, param, status);
                hdev->discov_timeout = 0;
                goto done;
        }
 
-       /* We need to ensure that we set this back on if someone changed
-        * the scan mode through a raw HCI socket.
-        */
-       set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags);
-
-       old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags);
-       old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
-
-       if (param & SCAN_INQUIRY) {
+       if (param & SCAN_INQUIRY)
                set_bit(HCI_ISCAN, &hdev->flags);
-               if (!old_iscan)
-                       mgmt_discoverable(hdev, 1);
-       } else if (old_iscan)
-               mgmt_discoverable(hdev, 0);
+       else
+               clear_bit(HCI_ISCAN, &hdev->flags);
 
-       if (param & SCAN_PAGE) {
+       if (param & SCAN_PAGE)
                set_bit(HCI_PSCAN, &hdev->flags);
-               if (!old_pscan)
-                       mgmt_connectable(hdev, 1);
-       } else if (old_pscan)
-               mgmt_connectable(hdev, 0);
+       else
+               clear_bit(HCI_ISCAN, &hdev->flags);
 
 done:
        hci_dev_unlock(hdev);
@@ -601,8 +589,10 @@ static void hci_cc_read_flow_control_mode(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (!rp->status)
-               hdev->flow_ctl_mode = rp->mode;
+       if (rp->status)
+               return;
+
+       hdev->flow_ctl_mode = rp->mode;
 }
 
 static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
@@ -637,8 +627,14 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (!rp->status)
+       if (rp->status)
+               return;
+
+       if (test_bit(HCI_INIT, &hdev->flags))
                bacpy(&hdev->bdaddr, &rp->bdaddr);
+
+       if (test_bit(HCI_SETUP, &hdev->dev_flags))
+               bacpy(&hdev->setup_addr, &rp->bdaddr);
 }
 
 static void hci_cc_read_page_scan_activity(struct hci_dev *hdev,
@@ -648,7 +644,10 @@ static void hci_cc_read_page_scan_activity(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (test_bit(HCI_INIT, &hdev->flags) && !rp->status) {
+       if (rp->status)
+               return;
+
+       if (test_bit(HCI_INIT, &hdev->flags)) {
                hdev->page_scan_interval = __le16_to_cpu(rp->interval);
                hdev->page_scan_window = __le16_to_cpu(rp->window);
        }
@@ -680,7 +679,10 @@ static void hci_cc_read_page_scan_type(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (test_bit(HCI_INIT, &hdev->flags) && !rp->status)
+       if (rp->status)
+               return;
+
+       if (test_bit(HCI_INIT, &hdev->flags))
                hdev->page_scan_type = rp->type;
 }
 
@@ -720,6 +722,41 @@ static void hci_cc_read_data_block_size(struct hci_dev *hdev,
               hdev->block_cnt, hdev->block_len);
 }
 
+static void hci_cc_read_clock(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_rp_read_clock *rp = (void *) skb->data;
+       struct hci_cp_read_clock *cp;
+       struct hci_conn *conn;
+
+       BT_DBG("%s", hdev->name);
+
+       if (skb->len < sizeof(*rp))
+               return;
+
+       if (rp->status)
+               return;
+
+       hci_dev_lock(hdev);
+
+       cp = hci_sent_cmd_data(hdev, HCI_OP_READ_CLOCK);
+       if (!cp)
+               goto unlock;
+
+       if (cp->which == 0x00) {
+               hdev->clock = le32_to_cpu(rp->clock);
+               goto unlock;
+       }
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+       if (conn) {
+               conn->clock = le32_to_cpu(rp->clock);
+               conn->clock_accuracy = le16_to_cpu(rp->accuracy);
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
 static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
                                       struct sk_buff *skb)
 {
@@ -789,8 +826,10 @@ static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (!rp->status)
-               hdev->inq_tx_power = rp->tx_power;
+       if (rp->status)
+               return;
+
+       hdev->inq_tx_power = rp->tx_power;
 }
 
 static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
@@ -861,8 +900,10 @@ static void hci_cc_le_read_local_features(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (!rp->status)
-               memcpy(hdev->le_features, rp->features, 8);
+       if (rp->status)
+               return;
+
+       memcpy(hdev->le_features, rp->features, 8);
 }
 
 static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
@@ -872,8 +913,10 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (!rp->status)
-               hdev->adv_tx_power = rp->tx_power;
+       if (rp->status)
+               return;
+
+       hdev->adv_tx_power = rp->tx_power;
 }
 
 static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
@@ -973,14 +1016,16 @@ static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
+       if (status)
+               return;
+
        sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_RANDOM_ADDR);
        if (!sent)
                return;
 
        hci_dev_lock(hdev);
 
-       if (!status)
-               bacpy(&hdev->random_addr, sent);
+       bacpy(&hdev->random_addr, sent);
 
        hci_dev_unlock(hdev);
 }
@@ -991,11 +1036,11 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
-       sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE);
-       if (!sent)
+       if (status)
                return;
 
-       if (status)
+       sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE);
+       if (!sent)
                return;
 
        hci_dev_lock(hdev);
@@ -1006,15 +1051,17 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
        if (*sent) {
                struct hci_conn *conn;
 
+               set_bit(HCI_LE_ADV, &hdev->dev_flags);
+
                conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
                if (conn)
                        queue_delayed_work(hdev->workqueue,
                                           &conn->le_conn_timeout,
-                                          HCI_LE_CONN_TIMEOUT);
+                                          conn->conn_timeout);
+       } else {
+               clear_bit(HCI_LE_ADV, &hdev->dev_flags);
        }
 
-       mgmt_advertising(hdev, *sent);
-
        hci_dev_unlock(hdev);
 }
 
@@ -1025,14 +1072,16 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
+       if (status)
+               return;
+
        cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_PARAM);
        if (!cp)
                return;
 
        hci_dev_lock(hdev);
 
-       if (!status)
-               hdev->le_scan_type = cp->type;
+       hdev->le_scan_type = cp->type;
 
        hci_dev_unlock(hdev);
 }
@@ -1053,13 +1102,15 @@ static void clear_pending_adv_report(struct hci_dev *hdev)
 }
 
 static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
+                                    u8 bdaddr_type, s8 rssi, u32 flags,
+                                    u8 *data, u8 len)
 {
        struct discovery_state *d = &hdev->discovery;
 
        bacpy(&d->last_adv_addr, bdaddr);
        d->last_adv_addr_type = bdaddr_type;
        d->last_adv_rssi = rssi;
+       d->last_adv_flags = flags;
        memcpy(d->last_adv_data, data, len);
        d->last_adv_data_len = len;
 }
@@ -1072,11 +1123,11 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
-       cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE);
-       if (!cp)
+       if (status)
                return;
 
-       if (status)
+       cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE);
+       if (!cp)
                return;
 
        switch (cp->enable) {
@@ -1096,7 +1147,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
 
                        mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
                                          d->last_adv_addr_type, NULL,
-                                         d->last_adv_rssi, 0, 1,
+                                         d->last_adv_rssi, d->last_adv_flags,
                                          d->last_adv_data,
                                          d->last_adv_data_len, NULL, 0);
                }
@@ -1107,13 +1158,21 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
                cancel_delayed_work(&hdev->le_scan_disable);
 
                clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
+
                /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we
                 * interrupted scanning due to a connect request. Mark
-                * therefore discovery as stopped.
+                * therefore discovery as stopped. If this was not
+                * because of a connect request advertising might have
+                * been disabled because of active scanning, so
+                * re-enable it again if necessary.
                 */
                if (test_and_clear_bit(HCI_LE_SCAN_INTERRUPTED,
                                       &hdev->dev_flags))
                        hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+               else if (!test_bit(HCI_LE_ADV, &hdev->dev_flags) &&
+                        hdev->discovery.state == DISCOVERY_FINDING)
+                       mgmt_reenable_advertising(hdev);
+
                break;
 
        default:
@@ -1129,8 +1188,10 @@ static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
 
        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;
+       if (rp->status)
+               return;
+
+       hdev->le_white_list_size = rp->size;
 }
 
 static void hci_cc_le_clear_white_list(struct hci_dev *hdev,
@@ -1140,8 +1201,10 @@ static void hci_cc_le_clear_white_list(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
-       if (!status)
-               hci_white_list_clear(hdev);
+       if (status)
+               return;
+
+       hci_bdaddr_list_clear(&hdev->le_white_list);
 }
 
 static void hci_cc_le_add_to_white_list(struct hci_dev *hdev,
@@ -1152,12 +1215,15 @@ static void hci_cc_le_add_to_white_list(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
+       if (status)
+               return;
+
        sent = hci_sent_cmd_data(hdev, HCI_OP_LE_ADD_TO_WHITE_LIST);
        if (!sent)
                return;
 
-       if (!status)
-               hci_white_list_add(hdev, &sent->bdaddr, sent->bdaddr_type);
+       hci_bdaddr_list_add(&hdev->le_white_list, &sent->bdaddr,
+                          sent->bdaddr_type);
 }
 
 static void hci_cc_le_del_from_white_list(struct hci_dev *hdev,
@@ -1168,12 +1234,15 @@ static void hci_cc_le_del_from_white_list(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
+       if (status)
+               return;
+
        sent = hci_sent_cmd_data(hdev, HCI_OP_LE_DEL_FROM_WHITE_LIST);
        if (!sent)
                return;
 
-       if (!status)
-               hci_white_list_del(hdev, &sent->bdaddr, sent->bdaddr_type);
+       hci_bdaddr_list_del(&hdev->le_white_list, &sent->bdaddr,
+                           sent->bdaddr_type);
 }
 
 static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
@@ -1183,8 +1252,10 @@ static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
-       if (!rp->status)
-               memcpy(hdev->le_states, rp->le_states, 8);
+       if (rp->status)
+               return;
+
+       memcpy(hdev->le_states, rp->le_states, 8);
 }
 
 static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
@@ -1195,25 +1266,26 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
 
+       if (status)
+               return;
+
        sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED);
        if (!sent)
                return;
 
-       if (!status) {
-               if (sent->le) {
-                       hdev->features[1][0] |= LMP_HOST_LE;
-                       set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
-               } else {
-                       hdev->features[1][0] &= ~LMP_HOST_LE;
-                       clear_bit(HCI_LE_ENABLED, &hdev->dev_flags);
-                       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
-               }
-
-               if (sent->simul)
-                       hdev->features[1][0] |= LMP_HOST_LE_BREDR;
-               else
-                       hdev->features[1][0] &= ~LMP_HOST_LE_BREDR;
+       if (sent->le) {
+               hdev->features[1][0] |= LMP_HOST_LE;
+               set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+       } else {
+               hdev->features[1][0] &= ~LMP_HOST_LE;
+               clear_bit(HCI_LE_ENABLED, &hdev->dev_flags);
+               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
        }
+
+       if (sent->simul)
+               hdev->features[1][0] |= LMP_HOST_LE_BREDR;
+       else
+               hdev->features[1][0] &= ~LMP_HOST_LE_BREDR;
 }
 
 static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1342,11 +1414,9 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
                }
        } else {
                if (!conn) {
-                       conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr);
-                       if (conn) {
-                               conn->out = true;
-                               conn->link_mode |= HCI_LM_MASTER;
-                       } else
+                       conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr,
+                                           HCI_ROLE_MASTER);
+                       if (!conn)
                                BT_ERR("No memory for new connection");
                }
        }
@@ -1575,6 +1645,8 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
        if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
                struct hci_cp_auth_requested auth_cp;
 
+               set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
+
                auth_cp.handle = __cpu_to_le16(conn->handle);
                hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
                             sizeof(auth_cp), &auth_cp);
@@ -1835,7 +1907,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status)
        if (cp->filter_policy == HCI_LE_USE_PEER_ADDR)
                queue_delayed_work(conn->hdev->workqueue,
                                   &conn->le_conn_timeout,
-                                  HCI_LE_CONN_TIMEOUT);
+                                  conn->conn_timeout);
 
 unlock:
        hci_dev_unlock(hdev);
@@ -1929,7 +2001,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_lock(hdev);
 
        for (; num_rsp; num_rsp--, info++) {
-               bool name_known, ssp;
+               u32 flags;
 
                bacpy(&data.bdaddr, &info->bdaddr);
                data.pscan_rep_mode     = info->pscan_rep_mode;
@@ -1940,10 +2012,10 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
                data.rssi               = 0x00;
                data.ssp_mode           = 0x00;
 
-               name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp);
+               flags = hci_inquiry_cache_update(hdev, &data, false);
+
                mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
-                                 info->dev_class, 0, !name_known, ssp, NULL,
-                                 0, NULL, 0);
+                                 info->dev_class, 0, flags, NULL, 0, NULL, 0);
        }
 
        hci_dev_unlock(hdev);
@@ -1988,10 +2060,10 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_conn_add_sysfs(conn);
 
                if (test_bit(HCI_AUTH, &hdev->flags))
-                       conn->link_mode |= HCI_LM_AUTH;
+                       set_bit(HCI_CONN_AUTH, &conn->flags);
 
                if (test_bit(HCI_ENCRYPT, &hdev->flags))
-                       conn->link_mode |= HCI_LM_ENCRYPT;
+                       set_bit(HCI_CONN_ENCRYPT, &conn->flags);
 
                /* Get remote features */
                if (conn->type == ACL_LINK) {
@@ -2031,10 +2103,21 @@ unlock:
        hci_conn_check_pending(hdev);
 }
 
+static void hci_reject_conn(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+       struct hci_cp_reject_conn_req cp;
+
+       bacpy(&cp.bdaddr, bdaddr);
+       cp.reason = HCI_ERROR_REJ_BAD_ADDR;
+       hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp);
+}
+
 static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_conn_request *ev = (void *) skb->data;
        int mask = hdev->link_mode;
+       struct inquiry_entry *ie;
+       struct hci_conn *conn;
        __u8 flags = 0;
 
        BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
@@ -2043,73 +2126,79 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type,
                                      &flags);
 
-       if ((mask & HCI_LM_ACCEPT) &&
-           !hci_blacklist_lookup(hdev, &ev->bdaddr, BDADDR_BREDR)) {
-               /* Connection accepted */
-               struct inquiry_entry *ie;
-               struct hci_conn *conn;
+       if (!(mask & HCI_LM_ACCEPT)) {
+               hci_reject_conn(hdev, &ev->bdaddr);
+               return;
+       }
 
-               hci_dev_lock(hdev);
+       if (hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr,
+                                  BDADDR_BREDR)) {
+               hci_reject_conn(hdev, &ev->bdaddr);
+               return;
+       }
 
-               ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
-               if (ie)
-                       memcpy(ie->data.dev_class, ev->dev_class, 3);
+       if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags) &&
+           !hci_bdaddr_list_lookup(&hdev->whitelist, &ev->bdaddr,
+                                   BDADDR_BREDR)) {
+                   hci_reject_conn(hdev, &ev->bdaddr);
+                   return;
+       }
 
-               conn = hci_conn_hash_lookup_ba(hdev, ev->link_type,
-                                              &ev->bdaddr);
+       /* Connection accepted */
+
+       hci_dev_lock(hdev);
+
+       ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
+       if (ie)
+               memcpy(ie->data.dev_class, ev->dev_class, 3);
+
+       conn = hci_conn_hash_lookup_ba(hdev, ev->link_type,
+                       &ev->bdaddr);
+       if (!conn) {
+               conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr,
+                                   HCI_ROLE_SLAVE);
                if (!conn) {
-                       conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr);
-                       if (!conn) {
-                               BT_ERR("No memory for new connection");
-                               hci_dev_unlock(hdev);
-                               return;
-                       }
+                       BT_ERR("No memory for new connection");
+                       hci_dev_unlock(hdev);
+                       return;
                }
+       }
 
-               memcpy(conn->dev_class, ev->dev_class, 3);
+       memcpy(conn->dev_class, ev->dev_class, 3);
 
-               hci_dev_unlock(hdev);
+       hci_dev_unlock(hdev);
 
-               if (ev->link_type == ACL_LINK ||
-                   (!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) {
-                       struct hci_cp_accept_conn_req cp;
-                       conn->state = BT_CONNECT;
+       if (ev->link_type == ACL_LINK ||
+           (!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) {
+               struct hci_cp_accept_conn_req cp;
+               conn->state = BT_CONNECT;
 
-                       bacpy(&cp.bdaddr, &ev->bdaddr);
+               bacpy(&cp.bdaddr, &ev->bdaddr);
 
-                       if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
-                               cp.role = 0x00; /* Become master */
-                       else
-                               cp.role = 0x01; /* Remain slave */
+               if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
+                       cp.role = 0x00; /* Become master */
+               else
+                       cp.role = 0x01; /* Remain slave */
 
-                       hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
-                                    &cp);
-               } else if (!(flags & HCI_PROTO_DEFER)) {
-                       struct hci_cp_accept_sync_conn_req cp;
-                       conn->state = BT_CONNECT;
+               hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp);
+       } else if (!(flags & HCI_PROTO_DEFER)) {
+               struct hci_cp_accept_sync_conn_req cp;
+               conn->state = BT_CONNECT;
 
-                       bacpy(&cp.bdaddr, &ev->bdaddr);
-                       cp.pkt_type = cpu_to_le16(conn->pkt_type);
+               bacpy(&cp.bdaddr, &ev->bdaddr);
+               cp.pkt_type = cpu_to_le16(conn->pkt_type);
 
-                       cp.tx_bandwidth   = cpu_to_le32(0x00001f40);
-                       cp.rx_bandwidth   = cpu_to_le32(0x00001f40);
-                       cp.max_latency    = cpu_to_le16(0xffff);
-                       cp.content_format = cpu_to_le16(hdev->voice_setting);
-                       cp.retrans_effort = 0xff;
+               cp.tx_bandwidth   = cpu_to_le32(0x00001f40);
+               cp.rx_bandwidth   = cpu_to_le32(0x00001f40);
+               cp.max_latency    = cpu_to_le16(0xffff);
+               cp.content_format = cpu_to_le16(hdev->voice_setting);
+               cp.retrans_effort = 0xff;
 
-                       hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
-                                    sizeof(cp), &cp);
-               } else {
-                       conn->state = BT_CONNECT2;
-                       hci_proto_connect_cfm(conn, 0);
-               }
+               hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(cp),
+                            &cp);
        } else {
-               /* Connection rejected */
-               struct hci_cp_reject_conn_req cp;
-
-               bacpy(&cp.bdaddr, &ev->bdaddr);
-               cp.reason = HCI_ERROR_REJ_BAD_ADDR;
-               hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp);
+               conn->state = BT_CONNECT2;
+               hci_proto_connect_cfm(conn, 0);
        }
 }
 
@@ -2158,7 +2247,8 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
                                reason, mgmt_connected);
 
-       if (conn->type == ACL_LINK && conn->flush_key)
+       if (conn->type == ACL_LINK &&
+           test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
                hci_remove_link_key(hdev, &conn->dst);
 
        params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
@@ -2170,7 +2260,9 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                        /* Fall through */
 
                case HCI_AUTO_CONN_ALWAYS:
-                       hci_pend_le_conn_add(hdev, &conn->dst, conn->dst_type);
+                       list_del_init(&params->action);
+                       list_add(&params->action, &hdev->pend_le_conns);
+                       hci_update_background_scan(hdev);
                        break;
 
                default:
@@ -2218,7 +2310,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                    test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) {
                        BT_INFO("re-auth of legacy device is not possible.");
                } else {
-                       conn->link_mode |= HCI_LM_AUTH;
+                       set_bit(HCI_CONN_AUTH, &conn->flags);
                        conn->sec_level = conn->pending_sec_level;
                }
        } else {
@@ -2297,6 +2389,9 @@ check_auth:
 
        if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) {
                struct hci_cp_auth_requested cp;
+
+               set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
+
                cp.handle = __cpu_to_le16(conn->handle);
                hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
        }
@@ -2321,19 +2416,19 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (!ev->status) {
                if (ev->encrypt) {
                        /* Encryption implies authentication */
-                       conn->link_mode |= HCI_LM_AUTH;
-                       conn->link_mode |= HCI_LM_ENCRYPT;
+                       set_bit(HCI_CONN_AUTH, &conn->flags);
+                       set_bit(HCI_CONN_ENCRYPT, &conn->flags);
                        conn->sec_level = conn->pending_sec_level;
 
                        /* P-256 authentication key implies FIPS */
                        if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256)
-                               conn->link_mode |= HCI_LM_FIPS;
+                               set_bit(HCI_CONN_FIPS, &conn->flags);
 
                        if ((conn->type == ACL_LINK && ev->encrypt == 0x02) ||
                            conn->type == LE_LINK)
                                set_bit(HCI_CONN_AES_CCM, &conn->flags);
                } else {
-                       conn->link_mode &= ~HCI_LM_ENCRYPT;
+                       clear_bit(HCI_CONN_ENCRYPT, &conn->flags);
                        clear_bit(HCI_CONN_AES_CCM, &conn->flags);
                }
        }
@@ -2384,7 +2479,7 @@ static void hci_change_link_key_complete_evt(struct hci_dev *hdev,
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
        if (conn) {
                if (!ev->status)
-                       conn->link_mode |= HCI_LM_SECURE;
+                       set_bit(HCI_CONN_SECURE, &conn->flags);
 
                clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
 
@@ -2595,6 +2690,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_read_local_amp_info(hdev, skb);
                break;
 
+       case HCI_OP_READ_CLOCK:
+               hci_cc_read_clock(hdev, skb);
+               break;
+
        case HCI_OP_READ_LOCAL_AMP_ASSOC:
                hci_cc_read_local_amp_assoc(hdev, skb);
                break;
@@ -2709,7 +2808,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        }
 
        if (opcode != HCI_OP_NOP)
-               del_timer(&hdev->cmd_timer);
+               cancel_delayed_work(&hdev->cmd_timer);
 
        hci_req_cmd_complete(hdev, opcode, status);
 
@@ -2800,7 +2899,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
        }
 
        if (opcode != HCI_OP_NOP)
-               del_timer(&hdev->cmd_timer);
+               cancel_delayed_work(&hdev->cmd_timer);
 
        if (ev->status ||
            (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event))
@@ -2824,12 +2923,8 @@ static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
        if (conn) {
-               if (!ev->status) {
-                       if (ev->role)
-                               conn->link_mode &= ~HCI_LM_MASTER;
-                       else
-                               conn->link_mode |= HCI_LM_MASTER;
-               }
+               if (!ev->status)
+                       conn->role = ev->role;
 
                clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
 
@@ -3023,10 +3118,11 @@ static void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_conn_drop(conn);
        }
 
-       if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags))
+       if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags) &&
+           !test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags)) {
                hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
                             sizeof(ev->bdaddr), &ev->bdaddr);
-       else if (test_bit(HCI_MGMT, &hdev->dev_flags)) {
+       else if (test_bit(HCI_MGMT, &hdev->dev_flags)) {
                u8 secure;
 
                if (conn->pending_sec_level == BT_SECURITY_HIGH)
@@ -3065,12 +3161,6 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        BT_DBG("%s found key type %u for %pMR", hdev->name, key->type,
               &ev->bdaddr);
 
-       if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) &&
-           key->type == HCI_LK_DEBUG_COMBINATION) {
-               BT_DBG("%s ignoring debug key", hdev->name);
-               goto not_found;
-       }
-
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
        if (conn) {
                if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
@@ -3110,6 +3200,8 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_link_key_notify *ev = (void *) skb->data;
        struct hci_conn *conn;
+       struct link_key *key;
+       bool persistent;
        u8 pin_len = 0;
 
        BT_DBG("%s", hdev->name);
@@ -3128,10 +3220,33 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_conn_drop(conn);
        }
 
-       if (test_bit(HCI_MGMT, &hdev->dev_flags))
-               hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key,
-                                ev->key_type, pin_len);
+       if (!test_bit(HCI_MGMT, &hdev->dev_flags))
+               goto unlock;
+
+       key = hci_add_link_key(hdev, conn, &ev->bdaddr, ev->link_key,
+                               ev->key_type, pin_len, &persistent);
+       if (!key)
+               goto unlock;
+
+       mgmt_new_link_key(hdev, key, persistent);
+
+       /* Keep debug keys around only if the HCI_KEEP_DEBUG_KEYS flag
+        * is set. If it's not set simply remove the key from the kernel
+        * list (we've still notified user space about it but with
+        * store_hint being 0).
+        */
+       if (key->type == HCI_LK_DEBUG_COMBINATION &&
+           !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
+               list_del(&key->list);
+               kfree(key);
+       } else if (conn) {
+               if (persistent)
+                       clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+               else
+                       set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+       }
 
+unlock:
        hci_dev_unlock(hdev);
 }
 
@@ -3197,7 +3312,6 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev,
 {
        struct inquiry_data data;
        int num_rsp = *((__u8 *) skb->data);
-       bool name_known, ssp;
 
        BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
 
@@ -3214,6 +3328,8 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev,
                info = (void *) (skb->data + 1);
 
                for (; num_rsp; num_rsp--, info++) {
+                       u32 flags;
+
                        bacpy(&data.bdaddr, &info->bdaddr);
                        data.pscan_rep_mode     = info->pscan_rep_mode;
                        data.pscan_period_mode  = info->pscan_period_mode;
@@ -3223,16 +3339,18 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev,
                        data.rssi               = info->rssi;
                        data.ssp_mode           = 0x00;
 
-                       name_known = hci_inquiry_cache_update(hdev, &data,
-                                                             false, &ssp);
+                       flags = hci_inquiry_cache_update(hdev, &data, false);
+
                        mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                          info->dev_class, info->rssi,
-                                         !name_known, ssp, NULL, 0, NULL, 0);
+                                         flags, NULL, 0, NULL, 0);
                }
        } else {
                struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
 
                for (; num_rsp; num_rsp--, info++) {
+                       u32 flags;
+
                        bacpy(&data.bdaddr, &info->bdaddr);
                        data.pscan_rep_mode     = info->pscan_rep_mode;
                        data.pscan_period_mode  = info->pscan_period_mode;
@@ -3241,11 +3359,12 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev,
                        data.clock_offset       = info->clock_offset;
                        data.rssi               = info->rssi;
                        data.ssp_mode           = 0x00;
-                       name_known = hci_inquiry_cache_update(hdev, &data,
-                                                             false, &ssp);
+
+                       flags = hci_inquiry_cache_update(hdev, &data, false);
+
                        mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
                                          info->dev_class, info->rssi,
-                                         !name_known, ssp, NULL, 0, NULL, 0);
+                                         flags, NULL, 0, NULL, 0);
                }
        }
 
@@ -3348,6 +3467,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
                hci_conn_add_sysfs(conn);
                break;
 
+       case 0x10:      /* Connection Accept Timeout */
        case 0x0d:      /* Connection Rejected due to Limited Resources */
        case 0x11:      /* Unsupported Feature or Parameter Value */
        case 0x1c:      /* SCO interval rejected */
@@ -3411,7 +3531,8 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
        hci_dev_lock(hdev);
 
        for (; num_rsp; num_rsp--, info++) {
-               bool name_known, ssp;
+               u32 flags;
+               bool name_known;
 
                bacpy(&data.bdaddr, &info->bdaddr);
                data.pscan_rep_mode     = info->pscan_rep_mode;
@@ -3429,12 +3550,13 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev,
                else
                        name_known = true;
 
-               name_known = hci_inquiry_cache_update(hdev, &data, name_known,
-                                                     &ssp);
+               flags = hci_inquiry_cache_update(hdev, &data, name_known);
+
                eir_len = eir_get_length(info->data, sizeof(info->data));
+
                mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
-                                 info->dev_class, info->rssi, !name_known,
-                                 ssp, info->data, eir_len, NULL, 0);
+                                 info->dev_class, info->rssi,
+                                 flags, info->data, eir_len, NULL, 0);
        }
 
        hci_dev_unlock(hdev);
@@ -3526,7 +3648,11 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (!test_bit(HCI_MGMT, &hdev->dev_flags))
                goto unlock;
 
+       /* Allow pairing if we're pairable, the initiators of the
+        * pairing or if the remote is not requesting bonding.
+        */
        if (test_bit(HCI_PAIRABLE, &hdev->dev_flags) ||
+           test_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags) ||
            (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
                struct hci_cp_io_capability_reply cp;
 
@@ -3538,18 +3664,14 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                /* If we are initiators, there is no remote information yet */
                if (conn->remote_auth == 0xff) {
-                       cp.authentication = conn->auth_type;
-
                        /* Request MITM protection if our IO caps allow it
                         * except for the no-bonding case.
-                        * conn->auth_type is not updated here since
-                        * that might cause the user confirmation to be
-                        * rejected in case the remote doesn't have the
-                        * IO capabilities for MITM.
                         */
                        if (conn->io_capability != HCI_IO_NO_INPUT_OUTPUT &&
-                           cp.authentication != HCI_AT_NO_BONDING)
-                               cp.authentication |= 0x01;
+                           conn->auth_type != HCI_AT_NO_BONDING)
+                               conn->auth_type |= 0x01;
+
+                       cp.authentication = conn->auth_type;
                } else {
                        conn->auth_type = hci_get_auth_req(conn);
                        cp.authentication = conn->auth_type;
@@ -3621,9 +3743,12 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
        rem_mitm = (conn->remote_auth & 0x01);
 
        /* If we require MITM but the remote device can't provide that
-        * (it has NoInputNoOutput) then reject the confirmation request
+        * (it has NoInputNoOutput) then reject the confirmation
+        * request. We check the security level here since it doesn't
+        * necessarily match conn->auth_type.
         */
-       if (loc_mitm && conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
+       if (conn->pending_sec_level > BT_SECURITY_MEDIUM &&
+           conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
                BT_DBG("Rejecting request: remote device can't provide MITM");
                hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY,
                             sizeof(ev->bdaddr), &ev->bdaddr);
@@ -3637,9 +3762,11 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev,
                /* If we're not the initiators request authorization to
                 * proceed from user space (mgmt_user_confirm with
                 * confirm_hint set to 1). The exception is if neither
-                * side had MITM in which case we do auto-accept.
+                * side had MITM or if the local IO capability is
+                * NoInputNoOutput, in which case we do auto-accept
                 */
                if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) &&
+                   conn->io_capability != HCI_IO_NO_INPUT_OUTPUT &&
                    (loc_mitm || rem_mitm)) {
                        BT_DBG("Confirming auto-accept as acceptor");
                        confirm_hint = 1;
@@ -3753,6 +3880,9 @@ static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
        if (!conn)
                goto unlock;
 
+       /* Reset the authentication requirement to unknown */
+       conn->remote_auth = 0xff;
+
        /* To avoid duplicate auth_failed events to user space we check
         * the HCI_CONN_AUTH_PEND flag which will be set if we
         * initiated the authentication. A traditional auth_complete
@@ -3967,16 +4097,23 @@ static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev,
 static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_le_conn_complete *ev = (void *) skb->data;
+       struct hci_conn_params *params;
        struct hci_conn *conn;
        struct smp_irk *irk;
+       u8 addr_type;
 
        BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
 
        hci_dev_lock(hdev);
 
+       /* All controllers implicitly stop advertising in the event of a
+        * connection, so ensure that the state bit is cleared.
+        */
+       clear_bit(HCI_LE_ADV, &hdev->dev_flags);
+
        conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
        if (!conn) {
-               conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr);
+               conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr, ev->role);
                if (!conn) {
                        BT_ERR("No memory for new connection");
                        goto unlock;
@@ -3984,11 +4121,6 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                conn->dst_type = ev->bdaddr_type;
 
-               if (ev->role == LE_CONN_ROLE_MASTER) {
-                       conn->out = true;
-                       conn->link_mode |= HCI_LM_MASTER;
-               }
-
                /* If we didn't have a hci_conn object previously
                 * but we're in master role this must be something
                 * initiated using a white list. Since white list based
@@ -4025,6 +4157,14 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
                conn->init_addr_type = ev->bdaddr_type;
                bacpy(&conn->init_addr, &ev->bdaddr);
+
+               /* For incoming connections, set the default minimum
+                * and maximum connection interval. They will be used
+                * to check if the parameters are in range and if not
+                * trigger the connection update procedure.
+                */
+               conn->le_conn_min_interval = hdev->le_conn_min_interval;
+               conn->le_conn_max_interval = hdev->le_conn_max_interval;
        }
 
        /* Lookup the identity address from the stored connection
@@ -4042,11 +4182,22 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                conn->dst_type = irk->addr_type;
        }
 
+       if (conn->dst_type == ADDR_LE_DEV_PUBLIC)
+               addr_type = BDADDR_LE_PUBLIC;
+       else
+               addr_type = BDADDR_LE_RANDOM;
+
        if (ev->status) {
                hci_le_conn_failed(conn, ev->status);
                goto unlock;
        }
 
+       /* Drop the connection if the device is blocked */
+       if (hci_bdaddr_list_lookup(&hdev->blacklist, &conn->dst, addr_type)) {
+               hci_conn_drop(conn);
+               goto unlock;
+       }
+
        if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
                mgmt_device_connected(hdev, &conn->dst, conn->type,
                                      conn->dst_type, 0, NULL, 0, NULL);
@@ -4055,40 +4206,80 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        conn->handle = __le16_to_cpu(ev->handle);
        conn->state = BT_CONNECTED;
 
-       if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
-               set_bit(HCI_CONN_6LOWPAN, &conn->flags);
+       conn->le_conn_interval = le16_to_cpu(ev->interval);
+       conn->le_conn_latency = le16_to_cpu(ev->latency);
+       conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout);
 
        hci_conn_add_sysfs(conn);
 
        hci_proto_connect_cfm(conn, ev->status);
 
-       hci_pend_le_conn_del(hdev, &conn->dst, conn->dst_type);
+       params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+       if (params)
+               list_del_init(&params->action);
 
 unlock:
+       hci_update_background_scan(hdev);
+       hci_dev_unlock(hdev);
+}
+
+static void hci_le_conn_update_complete_evt(struct hci_dev *hdev,
+                                           struct sk_buff *skb)
+{
+       struct hci_ev_le_conn_update_complete *ev = (void *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
+
+       if (ev->status)
+               return;
+
+       hci_dev_lock(hdev);
+
+       conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
+       if (conn) {
+               conn->le_conn_interval = le16_to_cpu(ev->interval);
+               conn->le_conn_latency = le16_to_cpu(ev->latency);
+               conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout);
+       }
+
        hci_dev_unlock(hdev);
 }
 
 /* This function requires the caller holds hdev->lock */
 static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
-                                 u8 addr_type)
+                                 u8 addr_type, u8 adv_type)
 {
        struct hci_conn *conn;
-       struct smp_irk *irk;
 
-       /* If this is a resolvable address, we should resolve it and then
-        * update address and address type variables.
+       /* If the event is not connectable don't proceed further */
+       if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND)
+               return;
+
+       /* Ignore if the device is blocked */
+       if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type))
+               return;
+
+       /* Most controller will fail if we try to create new connections
+        * while we have an existing one in slave role.
         */
-       irk = hci_get_irk(hdev, addr, addr_type);
-       if (irk) {
-               addr = &irk->bdaddr;
-               addr_type = irk->addr_type;
-       }
+       if (hdev->conn_hash.le_num_slave > 0)
+               return;
+
+       /* If we're connectable, always connect any ADV_DIRECT_IND event */
+       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) &&
+           adv_type == LE_ADV_DIRECT_IND)
+               goto connect;
 
-       if (!hci_pend_le_conn_lookup(hdev, addr, addr_type))
+       /* If we're not connectable only connect devices that we have in
+        * our pend_le_conns list.
+        */
+       if (!hci_pend_le_action_lookup(&hdev->pend_le_conns, addr, addr_type))
                return;
 
+connect:
        conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
-                             HCI_AT_NO_BONDING);
+                             HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER);
        if (!IS_ERR(conn))
                return;
 
@@ -4109,15 +4300,62 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
                               u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
 {
        struct discovery_state *d = &hdev->discovery;
+       struct smp_irk *irk;
        bool match;
+       u32 flags;
 
-       /* Passive scanning shouldn't trigger any device found events */
+       /* Check if we need to convert to identity address */
+       irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
+       if (irk) {
+               bdaddr = &irk->bdaddr;
+               bdaddr_type = irk->addr_type;
+       }
+
+       /* Check if we have been requested to connect to this device */
+       check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+
+       /* Passive scanning shouldn't trigger any device found events,
+        * except for devices marked as CONN_REPORT for which we do send
+        * device found events.
+        */
        if (hdev->le_scan_type == LE_SCAN_PASSIVE) {
-               if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND)
-                       check_pending_le_conn(hdev, bdaddr, bdaddr_type);
+               if (type == LE_ADV_DIRECT_IND)
+                       return;
+
+               if (!hci_pend_le_action_lookup(&hdev->pend_le_reports,
+                                              bdaddr, bdaddr_type))
+                       return;
+
+               if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND)
+                       flags = MGMT_DEV_FOUND_NOT_CONNECTABLE;
+               else
+                       flags = 0;
+               mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
+                                 rssi, flags, data, len, NULL, 0);
                return;
        }
 
+       /* When receiving non-connectable or scannable undirected
+        * advertising reports, this means that the remote device is
+        * not connectable and then clearly indicate this in the
+        * device found event.
+        *
+        * When receiving a scan response, then there is no way to
+        * know if the remote device is connectable or not. However
+        * since scan responses are merged with a previously seen
+        * advertising report, the flags field from that report
+        * will be used.
+        *
+        * In the really unlikely case that a controller get confused
+        * and just sends a scan response event, then it is marked as
+        * not connectable as well.
+        */
+       if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND ||
+           type == LE_ADV_SCAN_RSP)
+               flags = MGMT_DEV_FOUND_NOT_CONNECTABLE;
+       else
+               flags = 0;
+
        /* If there's nothing pending either store the data from this
         * event or send an immediate device found event if the data
         * should not be stored for later.
@@ -4128,12 +4366,12 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
                 */
                if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) {
                        store_pending_adv_report(hdev, bdaddr, bdaddr_type,
-                                                rssi, data, len);
+                                                rssi, flags, data, len);
                        return;
                }
 
                mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
-                                 rssi, 0, 1, data, len, NULL, 0);
+                                 rssi, flags, data, len, NULL, 0);
                return;
        }
 
@@ -4150,7 +4388,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
                if (!match)
                        mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
                                          d->last_adv_addr_type, NULL,
-                                         d->last_adv_rssi, 0, 1,
+                                         d->last_adv_rssi, d->last_adv_flags,
                                          d->last_adv_data,
                                          d->last_adv_data_len, NULL, 0);
 
@@ -4159,7 +4397,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
                 */
                if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) {
                        store_pending_adv_report(hdev, bdaddr, bdaddr_type,
-                                                rssi, data, len);
+                                                rssi, flags, data, len);
                        return;
                }
 
@@ -4168,7 +4406,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
                 */
                clear_pending_adv_report(hdev);
                mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
-                                 rssi, 0, 1, data, len, NULL, 0);
+                                 rssi, flags, data, len, NULL, 0);
                return;
        }
 
@@ -4177,8 +4415,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
         * sending a merged device found event.
         */
        mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
-                         d->last_adv_addr_type, NULL, rssi, 0, 1, data, len,
-                         d->last_adv_data, d->last_adv_data_len);
+                         d->last_adv_addr_type, NULL, rssi, d->last_adv_flags,
+                         d->last_adv_data, d->last_adv_data_len, data, len);
        clear_pending_adv_report(hdev);
 }
 
@@ -4219,7 +4457,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (conn == NULL)
                goto not_found;
 
-       ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->out);
+       ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role);
        if (ltk == NULL)
                goto not_found;
 
@@ -4241,9 +4479,12 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
         * distribute the keys. Later, security can be re-established
         * using a distributed LTK.
         */
-       if (ltk->type == HCI_SMP_STK_SLAVE) {
+       if (ltk->type == SMP_STK) {
+               set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
                list_del(&ltk->list);
                kfree(ltk);
+       } else {
+               clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
        }
 
        hci_dev_unlock(hdev);
@@ -4256,6 +4497,76 @@ not_found:
        hci_dev_unlock(hdev);
 }
 
+static void send_conn_param_neg_reply(struct hci_dev *hdev, u16 handle,
+                                     u8 reason)
+{
+       struct hci_cp_le_conn_param_req_neg_reply cp;
+
+       cp.handle = cpu_to_le16(handle);
+       cp.reason = reason;
+
+       hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY, sizeof(cp),
+                    &cp);
+}
+
+static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev,
+                                            struct sk_buff *skb)
+{
+       struct hci_ev_le_remote_conn_param_req *ev = (void *) skb->data;
+       struct hci_cp_le_conn_param_req_reply cp;
+       struct hci_conn *hcon;
+       u16 handle, min, max, latency, timeout;
+
+       handle = le16_to_cpu(ev->handle);
+       min = le16_to_cpu(ev->interval_min);
+       max = le16_to_cpu(ev->interval_max);
+       latency = le16_to_cpu(ev->latency);
+       timeout = le16_to_cpu(ev->timeout);
+
+       hcon = hci_conn_hash_lookup_handle(hdev, handle);
+       if (!hcon || hcon->state != BT_CONNECTED)
+               return send_conn_param_neg_reply(hdev, handle,
+                                                HCI_ERROR_UNKNOWN_CONN_ID);
+
+       if (hci_check_conn_params(min, max, latency, timeout))
+               return send_conn_param_neg_reply(hdev, handle,
+                                                HCI_ERROR_INVALID_LL_PARAMS);
+
+       if (hcon->role == HCI_ROLE_MASTER) {
+               struct hci_conn_params *params;
+               u8 store_hint;
+
+               hci_dev_lock(hdev);
+
+               params = hci_conn_params_lookup(hdev, &hcon->dst,
+                                               hcon->dst_type);
+               if (params) {
+                       params->conn_min_interval = min;
+                       params->conn_max_interval = max;
+                       params->conn_latency = latency;
+                       params->supervision_timeout = timeout;
+                       store_hint = 0x01;
+               } else{
+                       store_hint = 0x00;
+               }
+
+               hci_dev_unlock(hdev);
+
+               mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type,
+                                   store_hint, min, max, latency, timeout);
+       }
+
+       cp.handle = ev->handle;
+       cp.interval_min = ev->interval_min;
+       cp.interval_max = ev->interval_max;
+       cp.latency = ev->latency;
+       cp.timeout = ev->timeout;
+       cp.min_ce_len = 0;
+       cp.max_ce_len = 0;
+
+       hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp);
+}
+
 static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_le_meta *le_ev = (void *) skb->data;
@@ -4267,6 +4578,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_le_conn_complete_evt(hdev, skb);
                break;
 
+       case HCI_EV_LE_CONN_UPDATE_COMPLETE:
+               hci_le_conn_update_complete_evt(hdev, skb);
+               break;
+
        case HCI_EV_LE_ADVERTISING_REPORT:
                hci_le_adv_report_evt(hdev, skb);
                break;
@@ -4275,6 +4590,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_le_ltk_request_evt(hdev, skb);
                break;
 
+       case HCI_EV_LE_REMOTE_CONN_PARAM_REQ:
+               hci_le_remote_conn_param_req_evt(hdev, skb);
+               break;
+
        default:
                break;
        }
@@ -4306,7 +4625,7 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
        /* Received events are (currently) only needed when a request is
         * ongoing so avoid unnecessary memory allocation.
         */
-       if (hdev->req_status == HCI_REQ_PEND) {
+       if (hci_req_pending(hdev)) {
                kfree_skb(hdev->recv_evt);
                hdev->recv_evt = skb_clone(skb, GFP_KERNEL);
        }
index 80d25c150a653b57977c5b56c227908e20f4fa32..115f149362ba84d4b24b0cb217b49de3a5cf03bb 100644 (file)
@@ -35,13 +35,32 @@ static atomic_t monitor_promisc = ATOMIC_INIT(0);
 
 /* ----- HCI socket interface ----- */
 
+/* Socket info */
+#define hci_pi(sk) ((struct hci_pinfo *) sk)
+
+struct hci_pinfo {
+       struct bt_sock    bt;
+       struct hci_dev    *hdev;
+       struct hci_filter filter;
+       __u32             cmsg_mask;
+       unsigned short    channel;
+};
+
 static inline int hci_test_bit(int nr, void *addr)
 {
        return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
 }
 
 /* Security filter */
-static struct hci_sec_filter hci_sec_filter = {
+#define HCI_SFLT_MAX_OGF  5
+
+struct hci_sec_filter {
+       __u32 type_mask;
+       __u32 event_mask[2];
+       __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
+};
+
+static const struct hci_sec_filter hci_sec_filter = {
        /* Packet types */
        0x10,
        /* Events */
@@ -481,7 +500,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR);
+       err = hci_bdaddr_list_add(&hdev->blacklist, &bdaddr, BDADDR_BREDR);
 
        hci_dev_unlock(hdev);
 
@@ -498,7 +517,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR);
+       err = hci_bdaddr_list_del(&hdev->blacklist, &bdaddr, BDADDR_BREDR);
 
        hci_dev_unlock(hdev);
 
@@ -517,6 +536,9 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
        if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags))
                return -EBUSY;
 
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+               return -EOPNOTSUPP;
+
        if (hdev->dev_type != HCI_BREDR)
                return -EOPNOTSUPP;
 
@@ -690,7 +712,8 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
 
                if (test_bit(HCI_UP, &hdev->flags) ||
                    test_bit(HCI_INIT, &hdev->flags) ||
-                   test_bit(HCI_SETUP, &hdev->dev_flags)) {
+                   test_bit(HCI_SETUP, &hdev->dev_flags) ||
+                   test_bit(HCI_CONFIG, &hdev->dev_flags)) {
                        err = -EBUSY;
                        hci_dev_put(hdev);
                        goto done;
@@ -960,7 +983,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                        goto drop;
                }
 
-               if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) {
+               if (ogf == 0x3f) {
                        skb_queue_tail(&hdev->raw_q, skb);
                        queue_work(hdev->workqueue, &hdev->tx_work);
                } else {
index 323f23cd2c37c4a9b95f3c195c48d170d9e73404..46547b920f88edf7e6df5b6c5d10ae30daa47d67 100644 (file)
 #include "smp.h"
 #include "a2mp.h"
 #include "amp.h"
-#include "6lowpan.h"
 
 #define LE_FLOWCTL_MAX_CREDITS 65535
 
 bool disable_ertm;
 
 static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
-static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
+static u8 l2cap_fixed_chan[8] = { L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS, };
 
 static LIST_HEAD(chan_list);
 static DEFINE_RWLOCK(chan_list_lock);
@@ -205,6 +204,7 @@ done:
        write_unlock(&chan_list_lock);
        return err;
 }
+EXPORT_SYMBOL_GPL(l2cap_add_psm);
 
 int l2cap_add_scid(struct l2cap_chan *chan,  __u16 scid)
 {
@@ -437,6 +437,7 @@ struct l2cap_chan *l2cap_chan_create(void)
 
        return chan;
 }
+EXPORT_SYMBOL_GPL(l2cap_chan_create);
 
 static void l2cap_chan_destroy(struct kref *kref)
 {
@@ -464,6 +465,7 @@ void l2cap_chan_put(struct l2cap_chan *c)
 
        kref_put(&c->kref, l2cap_chan_destroy);
 }
+EXPORT_SYMBOL_GPL(l2cap_chan_put);
 
 void l2cap_chan_set_defaults(struct l2cap_chan *chan)
 {
@@ -482,6 +484,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
 
        set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
 }
+EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults);
 
 static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
 {
@@ -614,6 +617,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
 
        return;
 }
+EXPORT_SYMBOL_GPL(l2cap_chan_del);
 
 void l2cap_conn_update_id_addr(struct hci_conn *hcon)
 {
@@ -717,6 +721,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
                break;
        }
 }
+EXPORT_SYMBOL(l2cap_chan_close);
 
 static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
 {
@@ -770,7 +775,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
 }
 
 /* Service level security */
-int l2cap_chan_check_security(struct l2cap_chan *chan)
+int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
 {
        struct l2cap_conn *conn = chan->conn;
        __u8 auth_type;
@@ -780,7 +785,8 @@ int l2cap_chan_check_security(struct l2cap_chan *chan)
 
        auth_type = l2cap_get_auth_type(chan);
 
-       return hci_conn_security(conn->hcon, chan->sec_level, auth_type);
+       return hci_conn_security(conn->hcon, chan->sec_level, auth_type,
+                                initiator);
 }
 
 static u8 l2cap_get_ident(struct l2cap_conn *conn)
@@ -793,14 +799,14 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn)
         *  200 - 254 are used by utilities like l2ping, etc.
         */
 
-       spin_lock(&conn->lock);
+       mutex_lock(&conn->ident_lock);
 
        if (++conn->tx_ident > 128)
                conn->tx_ident = 1;
 
        id = conn->tx_ident;
 
-       spin_unlock(&conn->lock);
+       mutex_unlock(&conn->ident_lock);
 
        return id;
 }
@@ -1273,7 +1279,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
                if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
                        return;
 
-               if (l2cap_chan_check_security(chan) &&
+               if (l2cap_chan_check_security(chan, true) &&
                    __l2cap_no_conn_pending(chan)) {
                        l2cap_start_connection(chan);
                }
@@ -1352,7 +1358,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                }
 
                if (chan->state == BT_CONNECT) {
-                       if (!l2cap_chan_check_security(chan) ||
+                       if (!l2cap_chan_check_security(chan, true) ||
                            !__l2cap_no_conn_pending(chan)) {
                                l2cap_chan_unlock(chan);
                                continue;
@@ -1374,7 +1380,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
                        rsp.scid = cpu_to_le16(chan->dcid);
                        rsp.dcid = cpu_to_le16(chan->scid);
 
-                       if (l2cap_chan_check_security(chan)) {
+                       if (l2cap_chan_check_security(chan, false)) {
                                if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                                        rsp.result = cpu_to_le16(L2CAP_CR_PEND);
                                        rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
@@ -1455,13 +1461,12 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
 static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 {
        struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
        struct l2cap_chan *chan, *pchan;
        u8 dst_type;
 
        BT_DBG("");
 
-       bt_6lowpan_add_conn(conn);
-
        /* Check if we have socket listening on cid */
        pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
                                          &hcon->src, &hcon->dst);
@@ -1475,9 +1480,28 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
        dst_type = bdaddr_type(hcon, hcon->dst_type);
 
        /* If device is blocked, do not create a channel for it */
-       if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type))
+       if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type))
                return;
 
+       /* For LE slave connections, make sure the connection interval
+        * is in the range of the minium and maximum interval that has
+        * been configured for this connection. If not, then trigger
+        * the connection update procedure.
+        */
+       if (hcon->role == HCI_ROLE_SLAVE &&
+           (hcon->le_conn_interval < hcon->le_conn_min_interval ||
+            hcon->le_conn_interval > hcon->le_conn_max_interval)) {
+               struct l2cap_conn_param_update_req req;
+
+               req.min = cpu_to_le16(hcon->le_conn_min_interval);
+               req.max = cpu_to_le16(hcon->le_conn_max_interval);
+               req.latency = cpu_to_le16(hcon->le_conn_latency);
+               req.to_multiplier = cpu_to_le16(hcon->le_supv_timeout);
+
+               l2cap_send_cmd(conn, l2cap_get_ident(conn),
+                              L2CAP_CONN_PARAM_UPDATE_REQ, sizeof(req), &req);
+       }
+
        l2cap_chan_lock(pchan);
 
        chan = pchan->ops->new_connection(pchan);
@@ -2118,7 +2142,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
        struct sk_buff **frag;
        int sent = 0;
 
-       if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count))
+       if (chan->ops->memcpy_fromiovec(chan, skb_put(skb, count),
+                                       msg->msg_iov, count))
                return -EFAULT;
 
        sent += count;
@@ -2131,18 +2156,17 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
 
                count = min_t(unsigned int, conn->mtu, len);
 
-               tmp = chan->ops->alloc_skb(chan, count,
+               tmp = chan->ops->alloc_skb(chan, 0, count,
                                           msg->msg_flags & MSG_DONTWAIT);
                if (IS_ERR(tmp))
                        return PTR_ERR(tmp);
 
                *frag = tmp;
 
-               if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
+               if (chan->ops->memcpy_fromiovec(chan, skb_put(*frag, count),
+                                               msg->msg_iov, count))
                        return -EFAULT;
 
-               (*frag)->priority = skb->priority;
-
                sent += count;
                len  -= count;
 
@@ -2156,26 +2180,23 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
 }
 
 static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
-                                                struct msghdr *msg, size_t len,
-                                                u32 priority)
+                                                struct msghdr *msg, size_t len)
 {
        struct l2cap_conn *conn = chan->conn;
        struct sk_buff *skb;
        int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
        struct l2cap_hdr *lh;
 
-       BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan,
-              __le16_to_cpu(chan->psm), len, priority);
+       BT_DBG("chan %p psm 0x%2.2x len %zu", chan,
+              __le16_to_cpu(chan->psm), len);
 
        count = min_t(unsigned int, (conn->mtu - hlen), len);
 
-       skb = chan->ops->alloc_skb(chan, count + hlen,
+       skb = chan->ops->alloc_skb(chan, hlen, count,
                                   msg->msg_flags & MSG_DONTWAIT);
        if (IS_ERR(skb))
                return skb;
 
-       skb->priority = priority;
-
        /* Create L2CAP header */
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
@@ -2191,8 +2212,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
 }
 
 static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
-                                             struct msghdr *msg, size_t len,
-                                             u32 priority)
+                                             struct msghdr *msg, size_t len)
 {
        struct l2cap_conn *conn = chan->conn;
        struct sk_buff *skb;
@@ -2203,13 +2223,11 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan,
 
        count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
 
-       skb = chan->ops->alloc_skb(chan, count + L2CAP_HDR_SIZE,
+       skb = chan->ops->alloc_skb(chan, L2CAP_HDR_SIZE, count,
                                   msg->msg_flags & MSG_DONTWAIT);
        if (IS_ERR(skb))
                return skb;
 
-       skb->priority = priority;
-
        /* Create L2CAP header */
        lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
        lh->cid = cpu_to_le16(chan->dcid);
@@ -2247,7 +2265,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
 
        count = min_t(unsigned int, (conn->mtu - hlen), len);
 
-       skb = chan->ops->alloc_skb(chan, count + hlen,
+       skb = chan->ops->alloc_skb(chan, hlen, count,
                                   msg->msg_flags & MSG_DONTWAIT);
        if (IS_ERR(skb))
                return skb;
@@ -2368,7 +2386,7 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
 
        count = min_t(unsigned int, (conn->mtu - hlen), len);
 
-       skb = chan->ops->alloc_skb(chan, count + hlen,
+       skb = chan->ops->alloc_skb(chan, hlen, count,
                                   msg->msg_flags & MSG_DONTWAIT);
        if (IS_ERR(skb))
                return skb;
@@ -2430,8 +2448,7 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
        return 0;
 }
 
-int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
-                   u32 priority)
+int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
 {
        struct sk_buff *skb;
        int err;
@@ -2442,7 +2459,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 
        /* Connectionless channel */
        if (chan->chan_type == L2CAP_CHAN_CONN_LESS) {
-               skb = l2cap_create_connless_pdu(chan, msg, len, priority);
+               skb = l2cap_create_connless_pdu(chan, msg, len);
                if (IS_ERR(skb))
                        return PTR_ERR(skb);
 
@@ -2499,7 +2516,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
                        return -EMSGSIZE;
 
                /* Create a basic PDU */
-               skb = l2cap_create_basic_pdu(chan, msg, len, priority);
+               skb = l2cap_create_basic_pdu(chan, msg, len);
                if (IS_ERR(skb))
                        return PTR_ERR(skb);
 
@@ -2562,6 +2579,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
 
        return err;
 }
+EXPORT_SYMBOL_GPL(l2cap_chan_send);
 
 static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq)
 {
@@ -3217,6 +3235,9 @@ done:
 
        switch (chan->mode) {
        case L2CAP_MODE_BASIC:
+               if (disable_ertm)
+                       break;
+
                if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) &&
                    !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING))
                        break;
@@ -3829,7 +3850,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
        chan->ident = cmd->ident;
 
        if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
-               if (l2cap_chan_check_security(chan)) {
+               if (l2cap_chan_check_security(chan, false)) {
                        if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
                                l2cap_state_change(chan, BT_CONNECT2);
                                result = L2CAP_CR_PEND;
@@ -5197,27 +5218,6 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
        return 0;
 }
 
-static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
-                                        u16 to_multiplier)
-{
-       u16 max_latency;
-
-       if (min > max || min < 6 || max > 3200)
-               return -EINVAL;
-
-       if (to_multiplier < 10 || to_multiplier > 3200)
-               return -EINVAL;
-
-       if (max >= to_multiplier * 8)
-               return -EINVAL;
-
-       max_latency = (to_multiplier * 8 / max) - 1;
-       if (latency > 499 || latency > max_latency)
-               return -EINVAL;
-
-       return 0;
-}
-
 static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
                                              struct l2cap_cmd_hdr *cmd,
                                              u16 cmd_len, u8 *data)
@@ -5228,7 +5228,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
        u16 min, max, latency, to_multiplier;
        int err;
 
-       if (!(hcon->link_mode & HCI_LM_MASTER))
+       if (hcon->role != HCI_ROLE_MASTER)
                return -EINVAL;
 
        if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
@@ -5245,7 +5245,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
 
        memset(&rsp, 0, sizeof(rsp));
 
-       err = l2cap_check_conn_param(min, max, latency, to_multiplier);
+       err = hci_check_conn_params(min, max, latency, to_multiplier);
        if (err)
                rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
        else
@@ -5254,8 +5254,16 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
        l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
                       sizeof(rsp), &rsp);
 
-       if (!err)
-               hci_le_conn_update(hcon, min, max, latency, to_multiplier);
+       if (!err) {
+               u8 store_hint;
+
+               store_hint = hci_le_conn_update(hcon, min, max, latency,
+                                               to_multiplier);
+               mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type,
+                                   store_hint, min, max, latency,
+                                   to_multiplier);
+
+       }
 
        return 0;
 }
@@ -6879,9 +6887,6 @@ static void l2cap_att_channel(struct l2cap_conn *conn,
 
        BT_DBG("chan %p, len %d", chan, skb->len);
 
-       if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
-               goto drop;
-
        if (chan->imtu < skb->len)
                goto drop;
 
@@ -6914,6 +6919,16 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                return;
        }
 
+       /* Since we can't actively block incoming LE connections we must
+        * at least ensure that we ignore incoming data from them.
+        */
+       if (hcon->type == LE_LINK &&
+           hci_bdaddr_list_lookup(&hcon->hdev->blacklist, &hcon->dst,
+                                  bdaddr_type(hcon, hcon->dst_type))) {
+               kfree_skb(skb);
+               return;
+       }
+
        BT_DBG("len %d, cid 0x%4.4x", len, cid);
 
        switch (cid) {
@@ -6940,10 +6955,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                        l2cap_conn_del(conn->hcon, EACCES);
                break;
 
-       case L2CAP_FC_6LOWPAN:
-               bt_6lowpan_recv(conn, skb);
-               break;
-
        default:
                l2cap_data_channel(conn, cid, skb);
                break;
@@ -6974,7 +6985,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
        if (!hchan)
                return NULL;
 
-       conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
+       conn = kzalloc(sizeof(*conn), GFP_KERNEL);
        if (!conn) {
                hci_chan_del(hchan);
                return NULL;
@@ -7006,7 +7017,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
                conn->hs_enabled = test_bit(HCI_HS_ENABLED,
                                            &hcon->hdev->dev_flags);
 
-       spin_lock_init(&conn->lock);
+       mutex_init(&conn->ident_lock);
        mutex_init(&conn->chan_lock);
 
        INIT_LIST_HEAD(&conn->chan_l);
@@ -7042,7 +7053,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        struct l2cap_conn *conn;
        struct hci_conn *hcon;
        struct hci_dev *hdev;
-       __u8 auth_type;
        int err;
 
        BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
@@ -7084,7 +7094,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                        break;
                /* fall through */
        default:
-               err = -ENOTSUPP;
+               err = -EOPNOTSUPP;
                goto done;
        }
 
@@ -7118,9 +7128,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        chan->psm = psm;
        chan->dcid = cid;
 
-       auth_type = l2cap_get_auth_type(chan);
-
        if (bdaddr_type_is_le(dst_type)) {
+               u8 role;
+
                /* Convert from L2CAP channel address type to HCI address type
                 */
                if (dst_type == BDADDR_LE_PUBLIC)
@@ -7128,9 +7138,15 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                else
                        dst_type = ADDR_LE_DEV_RANDOM;
 
+               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+                       role = HCI_ROLE_SLAVE;
+               else
+                       role = HCI_ROLE_MASTER;
+
                hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level,
-                                     auth_type);
+                                     HCI_LE_CONN_TIMEOUT, role);
        } else {
+               u8 auth_type = l2cap_get_auth_type(chan);
                hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type);
        }
 
@@ -7176,7 +7192,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        if (hcon->state == BT_CONNECTED) {
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
                        __clear_chan_timer(chan);
-                       if (l2cap_chan_check_security(chan))
+                       if (l2cap_chan_check_security(chan, true))
                                l2cap_state_change(chan, BT_CONNECTED);
                } else
                        l2cap_do_start(chan);
@@ -7190,6 +7206,7 @@ done:
        hci_dev_put(hdev);
        return err;
 }
+EXPORT_SYMBOL_GPL(l2cap_chan_connect);
 
 /* ---- L2CAP interface with lower layer (HCI) ---- */
 
@@ -7252,8 +7269,6 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
 {
        BT_DBG("hcon %p reason %d", hcon, reason);
 
-       bt_6lowpan_del_conn(hcon->l2cap_data);
-
        l2cap_conn_del(hcon, bt_to_errno(reason));
 }
 
@@ -7536,14 +7551,11 @@ int __init l2cap_init(void)
        debugfs_create_u16("l2cap_le_default_mps", 0644, bt_debugfs,
                           &le_default_mps);
 
-       bt_6lowpan_init();
-
        return 0;
 }
 
 void l2cap_exit(void)
 {
-       bt_6lowpan_cleanup();
        debugfs_remove(l2cap_debugfs);
        l2cap_cleanup_sockets();
 }
index e1378693cc907086d5e90a4f8003c11cb774bda2..1884f72083c2f7de4595e91f2cdb61f02cef99bb 100644 (file)
@@ -279,7 +279,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
                        break;
                /* fall through */
        default:
-               err = -ENOTSUPP;
+               err = -EOPNOTSUPP;
                goto done;
        }
 
@@ -361,7 +361,8 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
        BT_DBG("sock %p, sk %p", sock, sk);
 
        if (peer && sk->sk_state != BT_CONNECTED &&
-           sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2)
+           sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2 &&
+           sk->sk_state != BT_CONFIG)
                return -ENOTCONN;
 
        memset(la, 0, sizeof(struct sockaddr_l2));
@@ -796,7 +797,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                } else if ((sk->sk_state == BT_CONNECT2 &&
                            test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) ||
                           sk->sk_state == BT_CONNECTED) {
-                       if (!l2cap_chan_check_security(chan))
+                       if (!l2cap_chan_check_security(chan, true))
                                set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
                        else
                                sk->sk_state_change(sk);
@@ -964,7 +965,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                return err;
 
        l2cap_chan_lock(chan);
-       err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
+       err = l2cap_chan_send(chan, msg, len);
        l2cap_chan_unlock(chan);
 
        return err;
@@ -1111,7 +1112,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
                l2cap_chan_close(chan, 0);
                lock_sock(sk);
 
-               if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
+               if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+                   !(current->flags & PF_EXITING))
                        err = bt_sock_wait_state(sk, BT_CLOSED,
                                                 sk->sk_lingertime);
        }
@@ -1292,6 +1294,7 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state,
 }
 
 static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
+                                              unsigned long hdr_len,
                                               unsigned long len, int nb)
 {
        struct sock *sk = chan->data;
@@ -1299,17 +1302,26 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
        int err;
 
        l2cap_chan_unlock(chan);
-       skb = bt_skb_send_alloc(sk, len, nb, &err);
+       skb = bt_skb_send_alloc(sk, hdr_len + len, nb, &err);
        l2cap_chan_lock(chan);
 
        if (!skb)
                return ERR_PTR(err);
 
+       skb->priority = sk->sk_priority;
+
        bt_cb(skb)->chan = chan;
 
        return skb;
 }
 
+static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan,
+                                         unsigned char *kdata,
+                                         struct iovec *iov, int len)
+{
+       return memcpy_fromiovec(kdata, iov, len);
+}
+
 static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
 {
        struct sock *sk = chan->data;
@@ -1375,20 +1387,21 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan)
        sk->sk_state_change(sk);
 }
 
-static struct l2cap_ops l2cap_chan_ops = {
-       .name           = "L2CAP Socket Interface",
-       .new_connection = l2cap_sock_new_connection_cb,
-       .recv           = l2cap_sock_recv_cb,
-       .close          = l2cap_sock_close_cb,
-       .teardown       = l2cap_sock_teardown_cb,
-       .state_change   = l2cap_sock_state_change_cb,
-       .ready          = l2cap_sock_ready_cb,
-       .defer          = l2cap_sock_defer_cb,
-       .resume         = l2cap_sock_resume_cb,
-       .suspend        = l2cap_sock_suspend_cb,
-       .set_shutdown   = l2cap_sock_set_shutdown_cb,
-       .get_sndtimeo   = l2cap_sock_get_sndtimeo_cb,
-       .alloc_skb      = l2cap_sock_alloc_skb_cb,
+static const struct l2cap_ops l2cap_chan_ops = {
+       .name                   = "L2CAP Socket Interface",
+       .new_connection         = l2cap_sock_new_connection_cb,
+       .recv                   = l2cap_sock_recv_cb,
+       .close                  = l2cap_sock_close_cb,
+       .teardown               = l2cap_sock_teardown_cb,
+       .state_change           = l2cap_sock_state_change_cb,
+       .ready                  = l2cap_sock_ready_cb,
+       .defer                  = l2cap_sock_defer_cb,
+       .resume                 = l2cap_sock_resume_cb,
+       .suspend                = l2cap_sock_suspend_cb,
+       .set_shutdown           = l2cap_sock_set_shutdown_cb,
+       .get_sndtimeo           = l2cap_sock_get_sndtimeo_cb,
+       .alloc_skb              = l2cap_sock_alloc_skb_cb,
+       .memcpy_fromiovec       = l2cap_sock_memcpy_fromiovec_cb,
 };
 
 static void l2cap_sock_destruct(struct sock *sk)
index af8e0a6243b7520617156f79d7d430ce12ef4be9..190668367e422b7ace0e04946ae59c35d34ba6c6 100644 (file)
@@ -35,7 +35,7 @@
 #include "smp.h"
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  6
+#define MGMT_REVISION  7
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -85,6 +85,14 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_SET_PRIVACY,
        MGMT_OP_LOAD_IRKS,
        MGMT_OP_GET_CONN_INFO,
+       MGMT_OP_GET_CLOCK_INFO,
+       MGMT_OP_ADD_DEVICE,
+       MGMT_OP_REMOVE_DEVICE,
+       MGMT_OP_LOAD_CONN_PARAM,
+       MGMT_OP_READ_UNCONF_INDEX_LIST,
+       MGMT_OP_READ_CONFIG_INFO,
+       MGMT_OP_SET_EXTERNAL_CONFIG,
+       MGMT_OP_SET_PUBLIC_ADDRESS,
 };
 
 static const u16 mgmt_events[] = {
@@ -111,6 +119,12 @@ static const u16 mgmt_events[] = {
        MGMT_EV_PASSKEY_NOTIFY,
        MGMT_EV_NEW_IRK,
        MGMT_EV_NEW_CSRK,
+       MGMT_EV_DEVICE_ADDED,
+       MGMT_EV_DEVICE_REMOVED,
+       MGMT_EV_NEW_CONN_PARAM,
+       MGMT_EV_UNCONF_INDEX_ADDED,
+       MGMT_EV_UNCONF_INDEX_REMOVED,
+       MGMT_EV_NEW_CONFIG_OPTIONS,
 };
 
 #define CACHE_TIMEOUT  msecs_to_jiffies(2 * 1000)
@@ -200,6 +214,36 @@ static u8 mgmt_status(u8 hci_status)
        return MGMT_STATUS_FAILED;
 }
 
+static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
+                     struct sock *skip_sk)
+{
+       struct sk_buff *skb;
+       struct mgmt_hdr *hdr;
+
+       skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       hdr = (void *) skb_put(skb, sizeof(*hdr));
+       hdr->opcode = cpu_to_le16(event);
+       if (hdev)
+               hdr->index = cpu_to_le16(hdev->id);
+       else
+               hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
+       hdr->len = cpu_to_le16(data_len);
+
+       if (data)
+               memcpy(skb_put(skb, data_len), data, data_len);
+
+       /* Time stamp */
+       __net_timestamp(skb);
+
+       hci_send_to_control(skb, skip_sk);
+       kfree_skb(skb);
+
+       return 0;
+}
+
 static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
 {
        struct sk_buff *skb;
@@ -327,7 +371,8 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
 
        count = 0;
        list_for_each_entry(d, &hci_dev_list, list) {
-               if (d->dev_type == HCI_BREDR)
+               if (d->dev_type == HCI_BREDR &&
+                   !test_bit(HCI_UNCONFIGURED, &d->dev_flags))
                        count++;
        }
 
@@ -340,13 +385,19 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
 
        count = 0;
        list_for_each_entry(d, &hci_dev_list, list) {
-               if (test_bit(HCI_SETUP, &d->dev_flags))
+               if (test_bit(HCI_SETUP, &d->dev_flags) ||
+                   test_bit(HCI_CONFIG, &d->dev_flags) ||
+                   test_bit(HCI_USER_CHANNEL, &d->dev_flags))
                        continue;
 
-               if (test_bit(HCI_USER_CHANNEL, &d->dev_flags))
+               /* Devices marked as raw-only are neither configured
+                * nor unconfigured controllers.
+                */
+               if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks))
                        continue;
 
-               if (d->dev_type == HCI_BREDR) {
+               if (d->dev_type == HCI_BREDR &&
+                   !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) {
                        rp->index[count++] = cpu_to_le16(d->id);
                        BT_DBG("Added hci%u", d->id);
                }
@@ -365,6 +416,138 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
        return err;
 }
 
+static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev,
+                                 void *data, u16 data_len)
+{
+       struct mgmt_rp_read_unconf_index_list *rp;
+       struct hci_dev *d;
+       size_t rp_len;
+       u16 count;
+       int err;
+
+       BT_DBG("sock %p", sk);
+
+       read_lock(&hci_dev_list_lock);
+
+       count = 0;
+       list_for_each_entry(d, &hci_dev_list, list) {
+               if (d->dev_type == HCI_BREDR &&
+                   test_bit(HCI_UNCONFIGURED, &d->dev_flags))
+                       count++;
+       }
+
+       rp_len = sizeof(*rp) + (2 * count);
+       rp = kmalloc(rp_len, GFP_ATOMIC);
+       if (!rp) {
+               read_unlock(&hci_dev_list_lock);
+               return -ENOMEM;
+       }
+
+       count = 0;
+       list_for_each_entry(d, &hci_dev_list, list) {
+               if (test_bit(HCI_SETUP, &d->dev_flags) ||
+                   test_bit(HCI_CONFIG, &d->dev_flags) ||
+                   test_bit(HCI_USER_CHANNEL, &d->dev_flags))
+                       continue;
+
+               /* Devices marked as raw-only are neither configured
+                * nor unconfigured controllers.
+                */
+               if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks))
+                       continue;
+
+               if (d->dev_type == HCI_BREDR &&
+                   test_bit(HCI_UNCONFIGURED, &d->dev_flags)) {
+                       rp->index[count++] = cpu_to_le16(d->id);
+                       BT_DBG("Added hci%u", d->id);
+               }
+       }
+
+       rp->num_controllers = cpu_to_le16(count);
+       rp_len = sizeof(*rp) + (2 * count);
+
+       read_unlock(&hci_dev_list_lock);
+
+       err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_UNCONF_INDEX_LIST,
+                          0, rp, rp_len);
+
+       kfree(rp);
+
+       return err;
+}
+
+static bool is_configured(struct hci_dev *hdev)
+{
+       if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) &&
+           !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags))
+               return false;
+
+       if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) &&
+           !bacmp(&hdev->public_addr, BDADDR_ANY))
+               return false;
+
+       return true;
+}
+
+static __le32 get_missing_options(struct hci_dev *hdev)
+{
+       u32 options = 0;
+
+       if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) &&
+           !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags))
+               options |= MGMT_OPTION_EXTERNAL_CONFIG;
+
+       if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) &&
+           !bacmp(&hdev->public_addr, BDADDR_ANY))
+               options |= MGMT_OPTION_PUBLIC_ADDRESS;
+
+       return cpu_to_le32(options);
+}
+
+static int new_options(struct hci_dev *hdev, struct sock *skip)
+{
+       __le32 options = get_missing_options(hdev);
+
+       return mgmt_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options,
+                         sizeof(options), skip);
+}
+
+static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
+{
+       __le32 options = get_missing_options(hdev);
+
+       return cmd_complete(sk, hdev->id, opcode, 0, &options,
+                           sizeof(options));
+}
+
+static int read_config_info(struct sock *sk, struct hci_dev *hdev,
+                           void *data, u16 data_len)
+{
+       struct mgmt_rp_read_config_info rp;
+       u32 options = 0;
+
+       BT_DBG("sock %p %s", sk, hdev->name);
+
+       hci_dev_lock(hdev);
+
+       memset(&rp, 0, sizeof(rp));
+       rp.manufacturer = cpu_to_le16(hdev->manufacturer);
+
+       if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks))
+               options |= MGMT_OPTION_EXTERNAL_CONFIG;
+
+       if (hdev->set_bdaddr)
+               options |= MGMT_OPTION_PUBLIC_ADDRESS;
+
+       rp.supported_options = cpu_to_le32(options);
+       rp.missing_options = get_missing_options(hdev);
+
+       hci_dev_unlock(hdev);
+
+       return cmd_complete(sk, hdev->id, MGMT_OP_READ_CONFIG_INFO, 0, &rp,
+                           sizeof(rp));
+}
+
 static u32 get_supported_settings(struct hci_dev *hdev)
 {
        u32 settings = 0;
@@ -372,12 +555,12 @@ static u32 get_supported_settings(struct hci_dev *hdev)
        settings |= MGMT_SETTING_POWERED;
        settings |= MGMT_SETTING_PAIRABLE;
        settings |= MGMT_SETTING_DEBUG_KEYS;
+       settings |= MGMT_SETTING_CONNECTABLE;
+       settings |= MGMT_SETTING_DISCOVERABLE;
 
        if (lmp_bredr_capable(hdev)) {
-               settings |= MGMT_SETTING_CONNECTABLE;
                if (hdev->hci_ver >= BLUETOOTH_VER_1_2)
                        settings |= MGMT_SETTING_FAST_CONNECTABLE;
-               settings |= MGMT_SETTING_DISCOVERABLE;
                settings |= MGMT_SETTING_BREDR;
                settings |= MGMT_SETTING_LINK_SECURITY;
 
@@ -387,7 +570,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
                }
 
                if (lmp_sc_capable(hdev) ||
-                   test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+                   test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
                        settings |= MGMT_SETTING_SECURE_CONN;
        }
 
@@ -397,6 +580,10 @@ static u32 get_supported_settings(struct hci_dev *hdev)
                settings |= MGMT_SETTING_PRIVACY;
        }
 
+       if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
+           hdev->set_bdaddr)
+               settings |= MGMT_SETTING_CONFIGURATION;
+
        return settings;
 }
 
@@ -440,7 +627,7 @@ static u32 get_current_settings(struct hci_dev *hdev)
        if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
                settings |= MGMT_SETTING_SECURE_CONN;
 
-       if (test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags))
+       if (test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags))
                settings |= MGMT_SETTING_DEBUG_KEYS;
 
        if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
@@ -571,6 +758,22 @@ static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
        return NULL;
 }
 
+static struct pending_cmd *mgmt_pending_find_data(u16 opcode,
+                                                 struct hci_dev *hdev,
+                                                 const void *data)
+{
+       struct pending_cmd *cmd;
+
+       list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
+               if (cmd->user_data != data)
+                       continue;
+               if (cmd->opcode == opcode)
+                       return cmd;
+       }
+
+       return NULL;
+}
+
 static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
 {
        u8 ad_len = 0;
@@ -703,6 +906,16 @@ static void update_adv_data(struct hci_request *req)
        hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
 }
 
+int mgmt_update_adv_data(struct hci_dev *hdev)
+{
+       struct hci_request req;
+
+       hci_req_init(&req, hdev);
+       update_adv_data(&req);
+
+       return hci_req_run(&req, NULL);
+}
+
 static void create_eir(struct hci_dev *hdev, u8 *data)
 {
        u8 *ptr = data;
@@ -836,6 +1049,13 @@ static bool get_connectable(struct hci_dev *hdev)
        return test_bit(HCI_CONNECTABLE, &hdev->dev_flags);
 }
 
+static void disable_advertising(struct hci_request *req)
+{
+       u8 enable = 0x00;
+
+       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
+}
+
 static void enable_advertising(struct hci_request *req)
 {
        struct hci_dev *hdev = req->hdev;
@@ -843,12 +1063,18 @@ static void enable_advertising(struct hci_request *req)
        u8 own_addr_type, enable = 0x01;
        bool connectable;
 
-       /* Clear the HCI_ADVERTISING bit temporarily so that the
+       if (hci_conn_num(hdev, LE_LINK) > 0)
+               return;
+
+       if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
+               disable_advertising(req);
+
+       /* Clear the HCI_LE_ADV bit temporarily so that the
         * hci_update_random_address knows that it's safe to go ahead
         * and write a new random address. The flag will be set back on
         * as soon as the SET_ADV_ENABLE HCI command completes.
         */
-       clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+       clear_bit(HCI_LE_ADV, &hdev->dev_flags);
 
        connectable = get_connectable(hdev);
 
@@ -871,13 +1097,6 @@ static void enable_advertising(struct hci_request *req)
        hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
 }
 
-static void disable_advertising(struct hci_request *req)
-{
-       u8 enable = 0x00;
-
-       hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable);
-}
-
 static void service_cache_off(struct work_struct *work)
 {
        struct hci_dev *hdev = container_of(work, struct hci_dev,
@@ -909,19 +1128,14 @@ static void rpa_expired(struct work_struct *work)
 
        set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
 
-       if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags) ||
-           hci_conn_num(hdev, LE_LINK) > 0)
+       if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
                return;
 
        /* The generation of a new RPA and programming it into the
         * controller happens in the enable_advertising() function.
         */
-
        hci_req_init(&req, hdev);
-
-       disable_advertising(&req);
        enable_advertising(&req);
-
        hci_req_run(&req, NULL);
 }
 
@@ -984,7 +1198,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
 {
        struct pending_cmd *cmd;
 
-       cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (!cmd)
                return NULL;
 
@@ -1047,7 +1261,7 @@ static void clean_up_hci_complete(struct hci_dev *hdev, u8 status)
        }
 }
 
-static void hci_stop_discovery(struct hci_request *req)
+static bool hci_stop_discovery(struct hci_request *req)
 {
        struct hci_dev *hdev = req->hdev;
        struct hci_cp_remote_name_req_cancel cp;
@@ -1062,32 +1276,39 @@ static void hci_stop_discovery(struct hci_request *req)
                        hci_req_add_le_scan_disable(req);
                }
 
-               break;
+               return true;
 
        case DISCOVERY_RESOLVING:
                e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
                                                     NAME_PENDING);
                if (!e)
-                       return;
+                       break;
 
                bacpy(&cp.bdaddr, &e->data.bdaddr);
                hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
                            &cp);
 
-               break;
+               return true;
 
        default:
                /* Passive scanning */
-               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+               if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
                        hci_req_add_le_scan_disable(req);
+                       return true;
+               }
+
                break;
        }
+
+       return false;
 }
 
 static int clean_up_hci_state(struct hci_dev *hdev)
 {
        struct hci_request req;
        struct hci_conn *conn;
+       bool discov_stopped;
+       int err;
 
        hci_req_init(&req, hdev);
 
@@ -1097,10 +1318,10 @@ static int clean_up_hci_state(struct hci_dev *hdev)
                hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
        }
 
-       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+       if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
                disable_advertising(&req);
 
-       hci_stop_discovery(&req);
+       discov_stopped = hci_stop_discovery(&req);
 
        list_for_each_entry(conn, &hdev->conn_hash.list, list) {
                struct hci_cp_disconnect dc;
@@ -1134,7 +1355,11 @@ static int clean_up_hci_state(struct hci_dev *hdev)
                }
        }
 
-       return hci_req_run(&req, clean_up_hci_complete);
+       err = hci_req_run(&req, clean_up_hci_complete);
+       if (!err && discov_stopped)
+               hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
+
+       return err;
 }
 
 static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -1203,36 +1428,6 @@ failed:
        return err;
 }
 
-static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
-                     struct sock *skip_sk)
-{
-       struct sk_buff *skb;
-       struct mgmt_hdr *hdr;
-
-       skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
-       if (!skb)
-               return -ENOMEM;
-
-       hdr = (void *) skb_put(skb, sizeof(*hdr));
-       hdr->opcode = cpu_to_le16(event);
-       if (hdev)
-               hdr->index = cpu_to_le16(hdev->id);
-       else
-               hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
-       hdr->len = cpu_to_le16(data_len);
-
-       if (data)
-               memcpy(skb_put(skb, data_len), data, data_len);
-
-       /* Time stamp */
-       __net_timestamp(skb);
-
-       hci_send_to_control(skb, skip_sk);
-       kfree_skb(skb);
-
-       return 0;
-}
-
 static int new_settings(struct hci_dev *hdev, struct sock *skip)
 {
        __le32 ev;
@@ -1242,6 +1437,11 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip)
        return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
 }
 
+int mgmt_new_settings(struct hci_dev *hdev)
+{
+       return new_settings(hdev, NULL);
+}
+
 struct cmd_lookup {
        struct sock *sk;
        struct hci_dev *hdev;
@@ -1553,7 +1753,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
 {
        struct pending_cmd *cmd;
        struct mgmt_mode *cp;
-       bool changed;
+       bool conn_changed, discov_changed;
 
        BT_DBG("status 0x%02x", status);
 
@@ -1570,15 +1770,25 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
        }
 
        cp = cmd->param;
-       if (cp->val)
-               changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-       else
-               changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
+       if (cp->val) {
+               conn_changed = !test_and_set_bit(HCI_CONNECTABLE,
+                                                &hdev->dev_flags);
+               discov_changed = false;
+       } else {
+               conn_changed = test_and_clear_bit(HCI_CONNECTABLE,
+                                                 &hdev->dev_flags);
+               discov_changed = test_and_clear_bit(HCI_DISCOVERABLE,
+                                                   &hdev->dev_flags);
+       }
 
        send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev);
 
-       if (changed)
+       if (conn_changed || discov_changed) {
                new_settings(hdev, cmd->sk);
+               if (discov_changed)
+                       mgmt_update_adv_data(hdev);
+               hci_update_background_scan(hdev);
+       }
 
 remove_cmd:
        mgmt_pending_remove(cmd);
@@ -1607,8 +1817,10 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
        if (err < 0)
                return err;
 
-       if (changed)
+       if (changed) {
+               hci_update_background_scan(hdev);
                return new_settings(hdev, sk);
+       }
 
        return 0;
 }
@@ -1688,11 +1900,9 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
        if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags))
                write_fast_connectable(&req, false);
 
-       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) &&
-           hci_conn_num(hdev, LE_LINK) == 0) {
-               disable_advertising(&req);
+       /* Update the advertising parameters if necessary */
+       if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
                enable_advertising(&req);
-       }
 
        err = hci_req_run(&req, set_connectable_complete);
        if (err < 0) {
@@ -1877,6 +2087,10 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                goto failed;
        }
 
+       if (!cp->val && test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags))
+               hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE,
+                            sizeof(cp->val), &cp->val);
+
        err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &cp->val);
        if (err < 0) {
                mgmt_pending_remove(cmd);
@@ -1973,6 +2187,8 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
                update_scan_rsp_data(&req);
                hci_req_run(&req, NULL);
 
+               hci_update_background_scan(hdev);
+
                hci_dev_unlock(hdev);
        }
 }
@@ -2050,7 +2266,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
                hci_cp.le = val;
                hci_cp.simul = lmp_le_br_capable(hdev);
        } else {
-               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
+               if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
                        disable_advertising(&req);
        }
 
@@ -2373,6 +2589,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
                          u16 len)
 {
        struct mgmt_cp_load_link_keys *cp = data;
+       const u16 max_key_count = ((U16_MAX - sizeof(*cp)) /
+                                  sizeof(struct mgmt_link_key_info));
        u16 key_count, expected_len;
        bool changed;
        int i;
@@ -2384,6 +2602,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
        key_count = __le16_to_cpu(cp->key_count);
+       if (key_count > max_key_count) {
+               BT_ERR("load_link_keys: too big key_count value %u",
+                      key_count);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
 
        expected_len = sizeof(*cp) + key_count *
                                        sizeof(struct mgmt_link_key_info);
@@ -2414,9 +2638,11 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
        hci_link_keys_clear(hdev);
 
        if (cp->debug_keys)
-               changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+               changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS,
+                                           &hdev->dev_flags);
        else
-               changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+               changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS,
+                                            &hdev->dev_flags);
 
        if (changed)
                new_settings(hdev, NULL);
@@ -2424,8 +2650,14 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
        for (i = 0; i < key_count; i++) {
                struct mgmt_link_key_info *key = &cp->keys[i];
 
-               hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val,
-                                key->type, key->pin_len);
+               /* Always ignore debug keys and require a new pairing if
+                * the user wants to use them.
+                */
+               if (key->type == HCI_LK_DEBUG_COMBINATION)
+                       continue;
+
+               hci_add_link_key(hdev, NULL, &key->addr.bdaddr, key->val,
+                                key->type, key->pin_len, NULL);
        }
 
        cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
@@ -2766,6 +2998,10 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data,
 
        BT_DBG("");
 
+       if (cp->io_capability > SMP_IO_KEYBOARD_DISPLAY)
+               return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY,
+                                   MGMT_STATUS_INVALID_PARAMS, NULL, 0);
+
        hci_dev_lock(hdev);
 
        hdev->io_capability = cp->io_capability;
@@ -2878,6 +3114,11 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                                    MGMT_STATUS_INVALID_PARAMS,
                                    &rp, sizeof(rp));
 
+       if (cp->io_cap > SMP_IO_KEYBOARD_DISPLAY)
+               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)) {
@@ -2902,8 +3143,20 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                else
                        addr_type = ADDR_LE_DEV_RANDOM;
 
+               /* When pairing a new device, it is expected to remember
+                * this device for future connections. Adding the connection
+                * parameter information ahead of time allows tracking
+                * of the slave preferred values and will speed up any
+                * further connection establishment.
+                *
+                * If connection parameters already exist, then they
+                * will be kept and this function does nothing.
+                */
+               hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type);
+
                conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type,
-                                     sec_level, auth_type);
+                                     sec_level, HCI_LE_CONN_TIMEOUT,
+                                     HCI_ROLE_MASTER);
        }
 
        if (IS_ERR(conn)) {
@@ -2949,7 +3202,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        cmd->user_data = conn;
 
        if (conn->state == BT_CONNECTED &&
-           hci_conn_security(conn, sec_level, auth_type))
+           hci_conn_security(conn, sec_level, auth_type, true))
                pairing_complete(cmd, 0);
 
        err = 0;
@@ -3031,14 +3284,7 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
        }
 
        if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) {
-               /* Continue with pairing via SMP. The hdev lock must be
-                * released as SMP may try to recquire it for crypto
-                * purposes.
-                */
-               hci_dev_unlock(hdev);
                err = smp_user_confirm_reply(conn, mgmt_op, passkey);
-               hci_dev_lock(hdev);
-
                if (!err)
                        err = cmd_complete(sk, hdev->id, mgmt_op,
                                           MGMT_STATUS_SUCCESS, addr,
@@ -3516,11 +3762,21 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
                        goto failed;
                }
 
-               if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
-                       err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
-                                        MGMT_STATUS_REJECTED);
-                       mgmt_pending_remove(cmd);
-                       goto failed;
+               if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
+                       /* Don't let discovery abort an outgoing
+                        * connection attempt that's using directed
+                        * advertising.
+                        */
+                       if (hci_conn_hash_lookup_state(hdev, LE_LINK,
+                                                      BT_CONNECT)) {
+                               err = cmd_status(sk, hdev->id,
+                                                MGMT_OP_START_DISCOVERY,
+                                                MGMT_STATUS_REJECTED);
+                               mgmt_pending_remove(cmd);
+                               goto failed;
+                       }
+
+                       disable_advertising(&req);
                }
 
                /* If controller is scanning, it means the background scanning
@@ -3723,12 +3979,18 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
-       if (err < 0)
+       err = hci_bdaddr_list_add(&hdev->blacklist, &cp->addr.bdaddr,
+                                 cp->addr.type);
+       if (err < 0) {
                status = MGMT_STATUS_FAILED;
-       else
-               status = MGMT_STATUS_SUCCESS;
+               goto done;
+       }
+
+       mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &cp->addr, sizeof(cp->addr),
+                  sk);
+       status = MGMT_STATUS_SUCCESS;
 
+done:
        err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
                           &cp->addr, sizeof(cp->addr));
 
@@ -3753,12 +4015,18 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data,
 
        hci_dev_lock(hdev);
 
-       err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
-       if (err < 0)
+       err = hci_bdaddr_list_del(&hdev->blacklist, &cp->addr.bdaddr,
+                                 cp->addr.type);
+       if (err < 0) {
                status = MGMT_STATUS_INVALID_PARAMS;
-       else
-               status = MGMT_STATUS_SUCCESS;
+               goto done;
+       }
+
+       mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &cp->addr, sizeof(cp->addr),
+                  sk);
+       status = MGMT_STATUS_SUCCESS;
 
+done:
        err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
                           &cp->addr, sizeof(cp->addr));
 
@@ -3813,6 +4081,11 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status)
                return;
        }
 
+       if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
+               set_bit(HCI_ADVERTISING, &hdev->dev_flags);
+       else
+               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
+
        mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp,
                             &match);
 
@@ -3853,7 +4126,9 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
         * necessary).
         */
        if (!hdev_is_powered(hdev) || val == enabled ||
-           hci_conn_num(hdev, LE_LINK) > 0) {
+           hci_conn_num(hdev, LE_LINK) > 0 ||
+           (test_bit(HCI_LE_SCAN, &hdev->dev_flags) &&
+            hdev->le_scan_type == LE_SCAN_ACTIVE)) {
                bool changed = false;
 
                if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
@@ -4105,7 +4380,8 @@ static void set_bredr_scan(struct hci_request *req)
         */
        write_fast_connectable(req, false);
 
-       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) ||
+           !list_empty(&hdev->whitelist))
                scan |= SCAN_PAGE;
        if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
                scan |= SCAN_INQUIRY;
@@ -4219,7 +4495,8 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
 
        hci_req_init(&req, hdev);
 
-       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) ||
+           !list_empty(&hdev->whitelist))
                set_bredr_scan(&req);
 
        /* Since only the advertising data flags will change, there
@@ -4252,7 +4529,7 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
                                  status);
 
        if (!lmp_sc_capable(hdev) &&
-           !test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+           !test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
                return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
@@ -4328,21 +4605,37 @@ static int set_debug_keys(struct sock *sk, struct hci_dev *hdev,
                          void *data, u16 len)
 {
        struct mgmt_mode *cp = data;
-       bool changed;
+       bool changed, use_changed;
        int err;
 
        BT_DBG("request for %s", hdev->name);
 
-       if (cp->val != 0x00 && cp->val != 0x01)
+       if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
                return cmd_status(sk, hdev->id, MGMT_OP_SET_DEBUG_KEYS,
                                  MGMT_STATUS_INVALID_PARAMS);
 
        hci_dev_lock(hdev);
 
        if (cp->val)
-               changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+               changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS,
+                                           &hdev->dev_flags);
        else
-               changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+               changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS,
+                                            &hdev->dev_flags);
+
+       if (cp->val == 0x02)
+               use_changed = !test_and_set_bit(HCI_USE_DEBUG_KEYS,
+                                               &hdev->dev_flags);
+       else
+               use_changed = test_and_clear_bit(HCI_USE_DEBUG_KEYS,
+                                                &hdev->dev_flags);
+
+       if (hdev_is_powered(hdev) && use_changed &&
+           test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+               u8 mode = (cp->val == 0x02) ? 0x01 : 0x00;
+               hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE,
+                            sizeof(mode), &mode);
+       }
 
        err = send_settings_rsp(sk, MGMT_OP_SET_DEBUG_KEYS, hdev);
        if (err < 0)
@@ -4426,6 +4719,8 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
                     u16 len)
 {
        struct mgmt_cp_load_irks *cp = cp_data;
+       const u16 max_irk_count = ((U16_MAX - sizeof(*cp)) /
+                                  sizeof(struct mgmt_irk_info));
        u16 irk_count, expected_len;
        int i, err;
 
@@ -4436,6 +4731,11 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
        irk_count = __le16_to_cpu(cp->irk_count);
+       if (irk_count > max_irk_count) {
+               BT_ERR("load_irks: too big irk_count value %u", irk_count);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
 
        expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info);
        if (expected_len != len) {
@@ -4505,6 +4805,8 @@ 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;
+       const u16 max_key_count = ((U16_MAX - sizeof(*cp)) /
+                                  sizeof(struct mgmt_ltk_info));
        u16 key_count, expected_len;
        int i, err;
 
@@ -4515,6 +4817,11 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                                  MGMT_STATUS_NOT_SUPPORTED);
 
        key_count = __le16_to_cpu(cp->key_count);
+       if (key_count > max_key_count) {
+               BT_ERR("load_ltks: too big key_count value %u", key_count);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
 
        expected_len = sizeof(*cp) + key_count *
                                        sizeof(struct mgmt_ltk_info);
@@ -4550,9 +4857,9 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                        addr_type = ADDR_LE_DEV_RANDOM;
 
                if (key->master)
-                       type = HCI_SMP_LTK;
+                       type = SMP_LTK;
                else
-                       type = HCI_SMP_LTK_SLAVE;
+                       type = SMP_LTK_SLAVE;
 
                switch (key->type) {
                case MGMT_LTK_UNAUTHENTICATED:
@@ -4790,6 +5097,559 @@ unlock:
        return err;
 }
 
+static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
+{
+       struct mgmt_cp_get_clock_info *cp;
+       struct mgmt_rp_get_clock_info rp;
+       struct hci_cp_read_clock *hci_cp;
+       struct pending_cmd *cmd;
+       struct hci_conn *conn;
+
+       BT_DBG("%s status %u", hdev->name, status);
+
+       hci_dev_lock(hdev);
+
+       hci_cp = hci_sent_cmd_data(hdev, HCI_OP_READ_CLOCK);
+       if (!hci_cp)
+               goto unlock;
+
+       if (hci_cp->which) {
+               u16 handle = __le16_to_cpu(hci_cp->handle);
+               conn = hci_conn_hash_lookup_handle(hdev, handle);
+       } else {
+               conn = NULL;
+       }
+
+       cmd = mgmt_pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn);
+       if (!cmd)
+               goto unlock;
+
+       cp = cmd->param;
+
+       memset(&rp, 0, sizeof(rp));
+       memcpy(&rp.addr, &cp->addr, sizeof(rp.addr));
+
+       if (status)
+               goto send_rsp;
+
+       rp.local_clock = cpu_to_le32(hdev->clock);
+
+       if (conn) {
+               rp.piconet_clock = cpu_to_le32(conn->clock);
+               rp.accuracy = cpu_to_le16(conn->clock_accuracy);
+       }
+
+send_rsp:
+       cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status),
+                    &rp, sizeof(rp));
+       mgmt_pending_remove(cmd);
+       if (conn)
+               hci_conn_drop(conn);
+
+unlock:
+       hci_dev_unlock(hdev);
+}
+
+static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
+                        u16 len)
+{
+       struct mgmt_cp_get_clock_info *cp = data;
+       struct mgmt_rp_get_clock_info rp;
+       struct hci_cp_read_clock hci_cp;
+       struct pending_cmd *cmd;
+       struct hci_request req;
+       struct hci_conn *conn;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       memset(&rp, 0, sizeof(rp));
+       bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+       rp.addr.type = cp->addr.type;
+
+       if (cp->addr.type != BDADDR_BREDR)
+               return cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &rp, sizeof(rp));
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO,
+                                  MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp));
+               goto unlock;
+       }
+
+       if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
+               conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+                                              &cp->addr.bdaddr);
+               if (!conn || conn->state != BT_CONNECTED) {
+                       err = cmd_complete(sk, hdev->id,
+                                          MGMT_OP_GET_CLOCK_INFO,
+                                          MGMT_STATUS_NOT_CONNECTED,
+                                          &rp, sizeof(rp));
+                       goto unlock;
+               }
+       } else {
+               conn = NULL;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_GET_CLOCK_INFO, hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+
+       hci_req_init(&req, hdev);
+
+       memset(&hci_cp, 0, sizeof(hci_cp));
+       hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp);
+
+       if (conn) {
+               hci_conn_hold(conn);
+               cmd->user_data = conn;
+
+               hci_cp.handle = cpu_to_le16(conn->handle);
+               hci_cp.which = 0x01; /* Piconet clock */
+               hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp);
+       }
+
+       err = hci_req_run(&req, get_clock_info_complete);
+       if (err < 0)
+               mgmt_pending_remove(cmd);
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+/* Helper for Add/Remove Device commands */
+static void update_page_scan(struct hci_dev *hdev, u8 scan)
+{
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+               return;
+
+       if (!hdev_is_powered(hdev))
+               return;
+
+       /* If HCI_CONNECTABLE is set then Add/Remove Device should not
+        * make any changes to page scanning.
+        */
+       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+               return;
+
+       if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+               scan |= SCAN_INQUIRY;
+
+       hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+}
+
+static void device_added(struct sock *sk, struct hci_dev *hdev,
+                        bdaddr_t *bdaddr, u8 type, u8 action)
+{
+       struct mgmt_ev_device_added ev;
+
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = type;
+       ev.action = action;
+
+       mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk);
+}
+
+static int add_device(struct sock *sk, struct hci_dev *hdev,
+                     void *data, u16 len)
+{
+       struct mgmt_cp_add_device *cp = data;
+       u8 auto_conn, addr_type;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (!bdaddr_type_is_valid(cp->addr.type) ||
+           !bacmp(&cp->addr.bdaddr, BDADDR_ANY))
+               return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &cp->addr, sizeof(cp->addr));
+
+       if (cp->action != 0x00 && cp->action != 0x01)
+               return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+                                   MGMT_STATUS_INVALID_PARAMS,
+                                   &cp->addr, sizeof(cp->addr));
+
+       hci_dev_lock(hdev);
+
+       if (cp->addr.type == BDADDR_BREDR) {
+               bool update_scan;
+
+               /* Only "connect" action supported for now */
+               if (cp->action != 0x01) {
+                       err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+                                          MGMT_STATUS_INVALID_PARAMS,
+                                          &cp->addr, sizeof(cp->addr));
+                       goto unlock;
+               }
+
+               update_scan = list_empty(&hdev->whitelist);
+
+               err = hci_bdaddr_list_add(&hdev->whitelist, &cp->addr.bdaddr,
+                                         cp->addr.type);
+               if (err)
+                       goto unlock;
+
+               if (update_scan)
+                       update_page_scan(hdev, SCAN_PAGE);
+
+               goto added;
+       }
+
+       if (cp->addr.type == BDADDR_LE_PUBLIC)
+               addr_type = ADDR_LE_DEV_PUBLIC;
+       else
+               addr_type = ADDR_LE_DEV_RANDOM;
+
+       if (cp->action)
+               auto_conn = HCI_AUTO_CONN_ALWAYS;
+       else
+               auto_conn = HCI_AUTO_CONN_REPORT;
+
+       /* If the connection parameters don't exist for this device,
+        * they will be created and configured with defaults.
+        */
+       if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
+                               auto_conn) < 0) {
+               err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+                                  MGMT_STATUS_FAILED,
+                                  &cp->addr, sizeof(cp->addr));
+               goto unlock;
+       }
+
+added:
+       device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
+
+       err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
+                          MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static void device_removed(struct sock *sk, struct hci_dev *hdev,
+                          bdaddr_t *bdaddr, u8 type)
+{
+       struct mgmt_ev_device_removed ev;
+
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = type;
+
+       mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk);
+}
+
+static int remove_device(struct sock *sk, struct hci_dev *hdev,
+                        void *data, u16 len)
+{
+       struct mgmt_cp_remove_device *cp = data;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
+               struct hci_conn_params *params;
+               u8 addr_type;
+
+               if (!bdaddr_type_is_valid(cp->addr.type)) {
+                       err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
+                                          MGMT_STATUS_INVALID_PARAMS,
+                                          &cp->addr, sizeof(cp->addr));
+                       goto unlock;
+               }
+
+               if (cp->addr.type == BDADDR_BREDR) {
+                       err = hci_bdaddr_list_del(&hdev->whitelist,
+                                                 &cp->addr.bdaddr,
+                                                 cp->addr.type);
+                       if (err) {
+                               err = cmd_complete(sk, hdev->id,
+                                                  MGMT_OP_REMOVE_DEVICE,
+                                                  MGMT_STATUS_INVALID_PARAMS,
+                                                  &cp->addr, sizeof(cp->addr));
+                               goto unlock;
+                       }
+
+                       if (list_empty(&hdev->whitelist))
+                               update_page_scan(hdev, SCAN_DISABLED);
+
+                       device_removed(sk, hdev, &cp->addr.bdaddr,
+                                      cp->addr.type);
+                       goto complete;
+               }
+
+               if (cp->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
+
+               params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
+                                               addr_type);
+               if (!params) {
+                       err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
+                                          MGMT_STATUS_INVALID_PARAMS,
+                                          &cp->addr, sizeof(cp->addr));
+                       goto unlock;
+               }
+
+               if (params->auto_connect == HCI_AUTO_CONN_DISABLED) {
+                       err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
+                                          MGMT_STATUS_INVALID_PARAMS,
+                                          &cp->addr, sizeof(cp->addr));
+                       goto unlock;
+               }
+
+               list_del(&params->action);
+               list_del(&params->list);
+               kfree(params);
+               hci_update_background_scan(hdev);
+
+               device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
+       } else {
+               struct hci_conn_params *p, *tmp;
+               struct bdaddr_list *b, *btmp;
+
+               if (cp->addr.type) {
+                       err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
+                                          MGMT_STATUS_INVALID_PARAMS,
+                                          &cp->addr, sizeof(cp->addr));
+                       goto unlock;
+               }
+
+               list_for_each_entry_safe(b, btmp, &hdev->whitelist, list) {
+                       device_removed(sk, hdev, &b->bdaddr, b->bdaddr_type);
+                       list_del(&b->list);
+                       kfree(b);
+               }
+
+               update_page_scan(hdev, SCAN_DISABLED);
+
+               list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
+                       if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
+                               continue;
+                       device_removed(sk, hdev, &p->addr, p->addr_type);
+                       list_del(&p->action);
+                       list_del(&p->list);
+                       kfree(p);
+               }
+
+               BT_DBG("All LE connection parameters were removed");
+
+               hci_update_background_scan(hdev);
+       }
+
+complete:
+       err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
+                          MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
+                          u16 len)
+{
+       struct mgmt_cp_load_conn_param *cp = data;
+       const u16 max_param_count = ((U16_MAX - sizeof(*cp)) /
+                                    sizeof(struct mgmt_conn_param));
+       u16 param_count, expected_len;
+       int i;
+
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       param_count = __le16_to_cpu(cp->param_count);
+       if (param_count > max_param_count) {
+               BT_ERR("load_conn_param: too big param_count value %u",
+                      param_count);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       expected_len = sizeof(*cp) + param_count *
+                                       sizeof(struct mgmt_conn_param);
+       if (expected_len != len) {
+               BT_ERR("load_conn_param: expected %u bytes, got %u bytes",
+                      expected_len, len);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       BT_DBG("%s param_count %u", hdev->name, param_count);
+
+       hci_dev_lock(hdev);
+
+       hci_conn_params_clear_disabled(hdev);
+
+       for (i = 0; i < param_count; i++) {
+               struct mgmt_conn_param *param = &cp->params[i];
+               struct hci_conn_params *hci_param;
+               u16 min, max, latency, timeout;
+               u8 addr_type;
+
+               BT_DBG("Adding %pMR (type %u)", &param->addr.bdaddr,
+                      param->addr.type);
+
+               if (param->addr.type == BDADDR_LE_PUBLIC) {
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               } else if (param->addr.type == BDADDR_LE_RANDOM) {
+                       addr_type = ADDR_LE_DEV_RANDOM;
+               } else {
+                       BT_ERR("Ignoring invalid connection parameters");
+                       continue;
+               }
+
+               min = le16_to_cpu(param->min_interval);
+               max = le16_to_cpu(param->max_interval);
+               latency = le16_to_cpu(param->latency);
+               timeout = le16_to_cpu(param->timeout);
+
+               BT_DBG("min 0x%04x max 0x%04x latency 0x%04x timeout 0x%04x",
+                      min, max, latency, timeout);
+
+               if (hci_check_conn_params(min, max, latency, timeout) < 0) {
+                       BT_ERR("Ignoring invalid connection parameters");
+                       continue;
+               }
+
+               hci_param = hci_conn_params_add(hdev, &param->addr.bdaddr,
+                                               addr_type);
+               if (!hci_param) {
+                       BT_ERR("Failed to add connection parameters");
+                       continue;
+               }
+
+               hci_param->conn_min_interval = min;
+               hci_param->conn_max_interval = max;
+               hci_param->conn_latency = latency;
+               hci_param->supervision_timeout = timeout;
+       }
+
+       hci_dev_unlock(hdev);
+
+       return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0);
+}
+
+static int set_external_config(struct sock *sk, struct hci_dev *hdev,
+                              void *data, u16 len)
+{
+       struct mgmt_cp_set_external_config *cp = data;
+       bool changed;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (hdev_is_powered(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+                                 MGMT_STATUS_REJECTED);
+
+       if (cp->config != 0x00 && cp->config != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+                                   MGMT_STATUS_INVALID_PARAMS);
+
+       if (!test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       hci_dev_lock(hdev);
+
+       if (cp->config)
+               changed = !test_and_set_bit(HCI_EXT_CONFIGURED,
+                                           &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_EXT_CONFIGURED,
+                                            &hdev->dev_flags);
+
+       err = send_options_rsp(sk, MGMT_OP_SET_EXTERNAL_CONFIG, hdev);
+       if (err < 0)
+               goto unlock;
+
+       if (!changed)
+               goto unlock;
+
+       err = new_options(hdev, sk);
+
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) {
+               mgmt_index_removed(hdev);
+
+               if (test_and_change_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) {
+                       set_bit(HCI_CONFIG, &hdev->dev_flags);
+                       set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+
+                       queue_work(hdev->req_workqueue, &hdev->power_on);
+               } else {
+                       set_bit(HCI_RAW, &hdev->flags);
+                       mgmt_index_added(hdev);
+               }
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int set_public_address(struct sock *sk, struct hci_dev *hdev,
+                             void *data, u16 len)
+{
+       struct mgmt_cp_set_public_address *cp = data;
+       bool changed;
+       int err;
+
+       BT_DBG("%s", hdev->name);
+
+       if (hdev_is_powered(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+                                 MGMT_STATUS_REJECTED);
+
+       if (!bacmp(&cp->bdaddr, BDADDR_ANY))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       if (!hdev->set_bdaddr)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       hci_dev_lock(hdev);
+
+       changed = !!bacmp(&hdev->public_addr, &cp->bdaddr);
+       bacpy(&hdev->public_addr, &cp->bdaddr);
+
+       err = send_options_rsp(sk, MGMT_OP_SET_PUBLIC_ADDRESS, hdev);
+       if (err < 0)
+               goto unlock;
+
+       if (!changed)
+               goto unlock;
+
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+               err = new_options(hdev, sk);
+
+       if (is_configured(hdev)) {
+               mgmt_index_removed(hdev);
+
+               clear_bit(HCI_UNCONFIGURED, &hdev->dev_flags);
+
+               set_bit(HCI_CONFIG, &hdev->dev_flags);
+               set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
+
+               queue_work(hdev->req_workqueue, &hdev->power_on);
+       }
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
 static const struct mgmt_handler {
        int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
                     u16 data_len);
@@ -4846,9 +5706,16 @@ static const struct mgmt_handler {
        { set_privacy,            false, MGMT_SET_PRIVACY_SIZE },
        { load_irks,              true,  MGMT_LOAD_IRKS_SIZE },
        { get_conn_info,          false, MGMT_GET_CONN_INFO_SIZE },
+       { get_clock_info,         false, MGMT_GET_CLOCK_INFO_SIZE },
+       { add_device,             false, MGMT_ADD_DEVICE_SIZE },
+       { remove_device,          false, MGMT_REMOVE_DEVICE_SIZE },
+       { load_conn_param,        true,  MGMT_LOAD_CONN_PARAM_SIZE },
+       { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE },
+       { read_config_info,       false, MGMT_READ_CONFIG_INFO_SIZE },
+       { set_external_config,    false, MGMT_SET_EXTERNAL_CONFIG_SIZE },
+       { set_public_address,     false, MGMT_SET_PUBLIC_ADDRESS_SIZE },
 };
 
-
 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
 {
        void *buf;
@@ -4892,11 +5759,21 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                }
 
                if (test_bit(HCI_SETUP, &hdev->dev_flags) ||
+                   test_bit(HCI_CONFIG, &hdev->dev_flags) ||
                    test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) {
                        err = cmd_status(sk, index, opcode,
                                         MGMT_STATUS_INVALID_INDEX);
                        goto done;
                }
+
+               if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) &&
+                   opcode != MGMT_OP_READ_CONFIG_INFO &&
+                   opcode != MGMT_OP_SET_EXTERNAL_CONFIG &&
+                   opcode != MGMT_OP_SET_PUBLIC_ADDRESS) {
+                       err = cmd_status(sk, index, opcode,
+                                        MGMT_STATUS_INVALID_INDEX);
+                       goto done;
+               }
        }
 
        if (opcode >= ARRAY_SIZE(mgmt_handlers) ||
@@ -4907,8 +5784,15 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
                goto done;
        }
 
-       if ((hdev && opcode < MGMT_OP_READ_INFO) ||
-           (!hdev && opcode >= MGMT_OP_READ_INFO)) {
+       if (hdev && (opcode <= MGMT_OP_READ_INDEX_LIST ||
+                    opcode == MGMT_OP_READ_UNCONF_INDEX_LIST)) {
+               err = cmd_status(sk, index, opcode,
+                                MGMT_STATUS_INVALID_INDEX);
+               goto done;
+       }
+
+       if (!hdev && (opcode > MGMT_OP_READ_INDEX_LIST &&
+                     opcode != MGMT_OP_READ_UNCONF_INDEX_LIST)) {
                err = cmd_status(sk, index, opcode,
                                 MGMT_STATUS_INVALID_INDEX);
                goto done;
@@ -4947,7 +5831,13 @@ void mgmt_index_added(struct hci_dev *hdev)
        if (hdev->dev_type != HCI_BREDR)
                return;
 
-       mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
+       if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
+               return;
+
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+               mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL);
+       else
+               mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
 }
 
 void mgmt_index_removed(struct hci_dev *hdev)
@@ -4957,20 +5847,41 @@ void mgmt_index_removed(struct hci_dev *hdev)
        if (hdev->dev_type != HCI_BREDR)
                return;
 
+       if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
+               return;
+
        mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
 
-       mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
+       if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
+               mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL);
+       else
+               mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
 }
 
 /* This function requires the caller holds hdev->lock */
-static void restart_le_auto_conns(struct hci_dev *hdev)
+static void restart_le_actions(struct hci_dev *hdev)
 {
        struct hci_conn_params *p;
 
        list_for_each_entry(p, &hdev->le_conn_params, list) {
-               if (p->auto_connect == HCI_AUTO_CONN_ALWAYS)
-                       hci_pend_le_conn_add(hdev, &p->addr, p->addr_type);
+               /* Needed for AUTO_OFF case where might not "really"
+                * have been powered off.
+                */
+               list_del_init(&p->action);
+
+               switch (p->auto_connect) {
+               case HCI_AUTO_CONN_ALWAYS:
+                       list_add(&p->action, &hdev->pend_le_conns);
+                       break;
+               case HCI_AUTO_CONN_REPORT:
+                       list_add(&p->action, &hdev->pend_le_reports);
+                       break;
+               default:
+                       break;
+               }
        }
+
+       hci_update_background_scan(hdev);
 }
 
 static void powered_complete(struct hci_dev *hdev, u8 status)
@@ -4981,7 +5892,7 @@ static void powered_complete(struct hci_dev *hdev, u8 status)
 
        hci_dev_lock(hdev);
 
-       restart_le_auto_conns(hdev);
+       restart_le_actions(hdev);
 
        mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
 
@@ -5138,92 +6049,6 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev)
        hci_dev_unlock(hdev);
 }
 
-void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
-{
-       bool changed;
-
-       /* Nothing needed here if there's a pending command since that
-        * commands request completion callback takes care of everything
-        * necessary.
-        */
-       if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev))
-               return;
-
-       /* Powering off may clear the scan mode - don't let that interfere */
-       if (!discoverable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
-               return;
-
-       if (discoverable) {
-               changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
-       } else {
-               clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags);
-               changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
-       }
-
-       if (changed) {
-               struct hci_request req;
-
-               /* In case this change in discoverable was triggered by
-                * a disabling of connectable there could be a need to
-                * update the advertising flags.
-                */
-               hci_req_init(&req, hdev);
-               update_adv_data(&req);
-               hci_req_run(&req, NULL);
-
-               new_settings(hdev, NULL);
-       }
-}
-
-void mgmt_connectable(struct hci_dev *hdev, u8 connectable)
-{
-       bool changed;
-
-       /* Nothing needed here if there's a pending command since that
-        * commands request completion callback takes care of everything
-        * necessary.
-        */
-       if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev))
-               return;
-
-       /* Powering off may clear the scan mode - don't let that interfere */
-       if (!connectable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
-               return;
-
-       if (connectable)
-               changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-       else
-               changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
-
-       if (changed)
-               new_settings(hdev, NULL);
-}
-
-void mgmt_advertising(struct hci_dev *hdev, u8 advertising)
-{
-       /* Powering off may stop advertising - don't let that interfere */
-       if (!advertising && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev))
-               return;
-
-       if (advertising)
-               set_bit(HCI_ADVERTISING, &hdev->dev_flags);
-       else
-               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
-}
-
-void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
-{
-       u8 mgmt_err = mgmt_status(status);
-
-       if (scan & SCAN_PAGE)
-               mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
-                                    cmd_status_rsp, &mgmt_err);
-
-       if (scan & SCAN_INQUIRY)
-               mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
-                                    cmd_status_rsp, &mgmt_err);
-}
-
 void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
                       bool persistent)
 {
@@ -5279,7 +6104,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
        ev.key.ediv = key->ediv;
        ev.key.rand = key->rand;
 
-       if (key->type == HCI_SMP_LTK)
+       if (key->type == SMP_LTK)
                ev.key.master = 1;
 
        memcpy(ev.key.val, key->val, sizeof(key->val));
@@ -5347,6 +6172,27 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk,
        mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL);
 }
 
+void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                        u8 bdaddr_type, u8 store_hint, u16 min_interval,
+                        u16 max_interval, u16 latency, u16 timeout)
+{
+       struct mgmt_ev_new_conn_param ev;
+
+       if (!hci_is_identity_address(bdaddr, bdaddr_type))
+               return;
+
+       memset(&ev, 0, sizeof(ev));
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = link_to_bdaddr(LE_LINK, bdaddr_type);
+       ev.store_hint = store_hint;
+       ev.min_interval = cpu_to_le16(min_interval);
+       ev.max_interval = cpu_to_le16(max_interval);
+       ev.latency = cpu_to_le16(latency);
+       ev.timeout = cpu_to_le16(timeout);
+
+       mgmt_event(MGMT_EV_NEW_CONN_PARAM, hdev, &ev, sizeof(ev), NULL);
+}
+
 static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
                                  u8 data_len)
 {
@@ -5765,10 +6611,14 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
 
        hci_req_init(&req, hdev);
 
-       if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
+       if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+               if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags))
+                       hci_req_add(&req, HCI_OP_WRITE_SSP_DEBUG_MODE,
+                                   sizeof(enable), &enable);
                update_eir(&req);
-       else
+       } else {
                clear_eir(&req);
+       }
 
        hci_req_run(&req, NULL);
 }
@@ -5912,17 +6762,23 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
 }
 
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
-                      u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
-                      u8 ssp, u8 *eir, u16 eir_len, u8 *scan_rsp,
-                      u8 scan_rsp_len)
+                      u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
+                      u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
 {
        char buf[512];
        struct mgmt_ev_device_found *ev = (void *) buf;
-       struct smp_irk *irk;
        size_t ev_size;
 
-       if (!hci_discovery_active(hdev))
-               return;
+       /* Don't send events for a non-kernel initiated discovery. With
+        * LE one exception is if we have pend_le_reports > 0 in which
+        * case we're doing passive scanning and want these events.
+        */
+       if (!hci_discovery_active(hdev)) {
+               if (link_type == ACL_LINK)
+                       return;
+               if (link_type == LE_LINK && list_empty(&hdev->pend_le_reports))
+                       return;
+       }
 
        /* Make sure that the buffer is big enough. The 5 extra bytes
         * are for the potential CoD field.
@@ -5932,20 +6788,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        memset(buf, 0, sizeof(buf));
 
-       irk = hci_get_irk(hdev, bdaddr, addr_type);
-       if (irk) {
-               bacpy(&ev->addr.bdaddr, &irk->bdaddr);
-               ev->addr.type = link_to_bdaddr(link_type, irk->addr_type);
-       } else {
-               bacpy(&ev->addr.bdaddr, bdaddr);
-               ev->addr.type = link_to_bdaddr(link_type, addr_type);
-       }
-
+       bacpy(&ev->addr.bdaddr, bdaddr);
+       ev->addr.type = link_to_bdaddr(link_type, addr_type);
        ev->rssi = rssi;
-       if (cfm_name)
-               ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
-       if (!ssp)
-               ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
+       ev->flags = cpu_to_le32(flags);
 
        if (eir_len > 0)
                memcpy(ev->eir, eir, eir_len);
@@ -6013,63 +6859,19 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
        mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
 }
 
-int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
-{
-       struct pending_cmd *cmd;
-       struct mgmt_ev_device_blocked ev;
-
-       cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
-
-       bacpy(&ev.addr.bdaddr, bdaddr);
-       ev.addr.type = type;
-
-       return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
-                         cmd ? cmd->sk : NULL);
-}
-
-int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
-{
-       struct pending_cmd *cmd;
-       struct mgmt_ev_device_unblocked ev;
-
-       cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
-
-       bacpy(&ev.addr.bdaddr, bdaddr);
-       ev.addr.type = type;
-
-       return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
-                         cmd ? cmd->sk : NULL);
-}
-
 static void adv_enable_complete(struct hci_dev *hdev, u8 status)
 {
        BT_DBG("%s status %u", hdev->name, status);
-
-       /* Clear the advertising mgmt setting if we failed to re-enable it */
-       if (status) {
-               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
-               new_settings(hdev, NULL);
-       }
 }
 
 void mgmt_reenable_advertising(struct hci_dev *hdev)
 {
        struct hci_request req;
 
-       if (hci_conn_num(hdev, LE_LINK) > 0)
-               return;
-
        if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags))
                return;
 
        hci_req_init(&req, hdev);
        enable_advertising(&req);
-
-       /* If this fails we have no option but to let user space know
-        * that we've disabled advertising.
-        */
-       if (hci_req_run(&req, adv_enable_complete) < 0) {
-               clear_bit(HCI_ADVERTISING, &hdev->dev_flags);
-               new_settings(hdev, NULL);
-       }
+       hci_req_run(&req, adv_enable_complete);
 }
index 754b6fe4f742af8ce4662a25065c6caba242295b..a0690a84f3e9c2346b8bd19bb74387f650ba3e0a 100644 (file)
@@ -227,7 +227,8 @@ static int rfcomm_check_security(struct rfcomm_dlc *d)
                break;
        }
 
-       return hci_conn_security(conn->hcon, d->sec_level, auth_type);
+       return hci_conn_security(conn->hcon, d->sec_level, auth_type,
+                                d->out);
 }
 
 static void rfcomm_session_timeout(unsigned long arg)
index c603a5eb47204ffae6eb4890da75e7e3c0394df9..8bbbb5ec468c373f86c6caafe9187f3684775915 100644 (file)
@@ -918,7 +918,8 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)
                sk->sk_shutdown = SHUTDOWN_MASK;
                __rfcomm_sock_close(sk);
 
-               if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
+               if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+                   !(current->flags & PF_EXITING))
                        err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
        }
        release_sock(sk);
index c06dbd3938e881e45d575b586830d2bea9d580a0..7ee9e4ab00f882f827f7b63361dc82a24ca72a23 100644 (file)
@@ -40,13 +40,38 @@ static struct bt_sock_list sco_sk_list = {
        .lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock)
 };
 
-static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
-static void sco_chan_del(struct sock *sk, int err);
+/* ---- SCO connections ---- */
+struct sco_conn {
+       struct hci_conn *hcon;
+
+       spinlock_t      lock;
+       struct sock     *sk;
+
+       unsigned int    mtu;
+};
+
+#define sco_conn_lock(c)       spin_lock(&c->lock);
+#define sco_conn_unlock(c)     spin_unlock(&c->lock);
 
 static void sco_sock_close(struct sock *sk);
 static void sco_sock_kill(struct sock *sk);
 
+/* ----- SCO socket info ----- */
+#define sco_pi(sk) ((struct sco_pinfo *) sk)
+
+struct sco_pinfo {
+       struct bt_sock  bt;
+       bdaddr_t        src;
+       bdaddr_t        dst;
+       __u32           flags;
+       __u16           setting;
+       struct sco_conn *conn;
+};
+
 /* ---- SCO timers ---- */
+#define SCO_CONN_TIMEOUT       (HZ * 40)
+#define SCO_DISCONN_TIMEOUT    (HZ * 2)
+
 static void sco_sock_timeout(unsigned long arg)
 {
        struct sock *sk = (struct sock *) arg;
@@ -102,13 +127,31 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon)
        return conn;
 }
 
-static struct sock *sco_chan_get(struct sco_conn *conn)
+/* Delete channel.
+ * Must be called on the locked socket. */
+static void sco_chan_del(struct sock *sk, int err)
 {
-       struct sock *sk = NULL;
-       sco_conn_lock(conn);
-       sk = conn->sk;
-       sco_conn_unlock(conn);
-       return sk;
+       struct sco_conn *conn;
+
+       conn = sco_pi(sk)->conn;
+
+       BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
+
+       if (conn) {
+               sco_conn_lock(conn);
+               conn->sk = NULL;
+               sco_pi(sk)->conn = NULL;
+               sco_conn_unlock(conn);
+
+               if (conn->hcon)
+                       hci_conn_drop(conn->hcon);
+       }
+
+       sk->sk_state = BT_CLOSED;
+       sk->sk_err   = err;
+       sk->sk_state_change(sk);
+
+       sock_set_flag(sk, SOCK_ZAPPED);
 }
 
 static int sco_conn_del(struct hci_conn *hcon, int err)
@@ -122,7 +165,10 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
        BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
 
        /* Kill socket */
-       sk = sco_chan_get(conn);
+       sco_conn_lock(conn);
+       sk = conn->sk;
+       sco_conn_unlock(conn);
+
        if (sk) {
                bh_lock_sock(sk);
                sco_sock_clear_timer(sk);
@@ -136,6 +182,17 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
        return 0;
 }
 
+static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+{
+       BT_DBG("conn %p", conn);
+
+       sco_pi(sk)->conn = conn;
+       conn->sk = sk;
+
+       if (parent)
+               bt_accept_enqueue(parent, sk);
+}
+
 static int sco_chan_add(struct sco_conn *conn, struct sock *sk,
                        struct sock *parent)
 {
@@ -240,7 +297,11 @@ static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
 
 static void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
 {
-       struct sock *sk = sco_chan_get(conn);
+       struct sock *sk;
+
+       sco_conn_lock(conn);
+       sk = conn->sk;
+       sco_conn_unlock(conn);
 
        if (!sk)
                goto drop;
@@ -909,7 +970,8 @@ static int sco_sock_shutdown(struct socket *sock, int how)
                sco_sock_clear_timer(sk);
                __sco_sock_close(sk);
 
-               if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
+               if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+                   !(current->flags & PF_EXITING))
                        err = bt_sock_wait_state(sk, BT_CLOSED,
                                                 sk->sk_lingertime);
        }
@@ -929,7 +991,8 @@ static int sco_sock_release(struct socket *sock)
 
        sco_sock_close(sk);
 
-       if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) {
+       if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
+           !(current->flags & PF_EXITING)) {
                lock_sock(sk);
                err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime);
                release_sock(sk);
@@ -940,44 +1003,6 @@ static int sco_sock_release(struct socket *sock)
        return err;
 }
 
-static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
-{
-       BT_DBG("conn %p", conn);
-
-       sco_pi(sk)->conn = conn;
-       conn->sk = sk;
-
-       if (parent)
-               bt_accept_enqueue(parent, sk);
-}
-
-/* Delete channel.
- * Must be called on the locked socket. */
-static void sco_chan_del(struct sock *sk, int err)
-{
-       struct sco_conn *conn;
-
-       conn = sco_pi(sk)->conn;
-
-       BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
-
-       if (conn) {
-               sco_conn_lock(conn);
-               conn->sk = NULL;
-               sco_pi(sk)->conn = NULL;
-               sco_conn_unlock(conn);
-
-               if (conn->hcon)
-                       hci_conn_drop(conn->hcon);
-       }
-
-       sk->sk_state = BT_CLOSED;
-       sk->sk_err   = err;
-       sk->sk_state_change(sk);
-
-       sock_set_flag(sk, SOCK_ZAPPED);
-}
-
 static void sco_conn_ready(struct sco_conn *conn)
 {
        struct sock *parent;
index e33a982161c1db063b5cb96f06a10babb5f0b436..e49c83d8b95709d7ce602869dffde715df730b58 100644 (file)
 
 #define AUTH_REQ_MASK   0x07
 
-#define SMP_FLAG_TK_VALID      1
-#define SMP_FLAG_CFM_PENDING   2
-#define SMP_FLAG_MITM_AUTH     3
-#define SMP_FLAG_COMPLETE      4
-#define SMP_FLAG_INITIATOR     5
+enum {
+       SMP_FLAG_TK_VALID,
+       SMP_FLAG_CFM_PENDING,
+       SMP_FLAG_MITM_AUTH,
+       SMP_FLAG_COMPLETE,
+       SMP_FLAG_INITIATOR,
+};
 
 struct smp_chan {
        struct l2cap_conn *conn;
@@ -60,20 +62,16 @@ struct smp_chan {
        struct smp_ltk  *slave_ltk;
        struct smp_irk  *remote_irk;
        unsigned long   flags;
+
+       struct crypto_blkcipher *tfm_aes;
 };
 
-static inline void swap128(const u8 src[16], u8 dst[16])
+static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
 {
-       int i;
-       for (i = 0; i < 16; i++)
-               dst[15 - i] = src[i];
-}
+       size_t i;
 
-static inline void swap56(const u8 src[7], u8 dst[7])
-{
-       int i;
-       for (i = 0; i < 7; i++)
-               dst[6 - i] = src[i];
+       for (i = 0; i < len; i++)
+               dst[len - 1 - i] = src[i];
 }
 
 static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
@@ -92,7 +90,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
        desc.flags = 0;
 
        /* The most significant octet of key corresponds to k[0] */
-       swap128(k, tmp);
+       swap_buf(k, tmp, 16);
 
        err = crypto_blkcipher_setkey(tfm, tmp, 16);
        if (err) {
@@ -101,7 +99,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
        }
 
        /* Most significant octet of plaintextData corresponds to data[0] */
-       swap128(r, data);
+       swap_buf(r, data, 16);
 
        sg_init_one(&sg, data, 16);
 
@@ -110,7 +108,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
                BT_ERR("Encrypt data error %d", err);
 
        /* Most significant octet of encryptedData corresponds to data[0] */
-       swap128(data, r);
+       swap_buf(data, r, 16);
 
        return err;
 }
@@ -174,13 +172,16 @@ int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa)
        return 0;
 }
 
-static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
-                 u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
-                 u8 _rat, bdaddr_t *ra, u8 res[16])
+static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
+                 u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra,
+                 u8 res[16])
 {
+       struct hci_dev *hdev = smp->conn->hcon->hdev;
        u8 p1[16], p2[16];
        int err;
 
+       BT_DBG("%s", hdev->name);
+
        memset(p1, 0, 16);
 
        /* p1 = pres || preq || _rat || _iat */
@@ -198,7 +199,7 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
        u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
 
        /* res = e(k, res) */
-       err = smp_e(tfm, k, res);
+       err = smp_e(smp->tfm_aes, k, res);
        if (err) {
                BT_ERR("Encrypt data error");
                return err;
@@ -208,23 +209,26 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
        u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
 
        /* res = e(k, res) */
-       err = smp_e(tfm, k, res);
+       err = smp_e(smp->tfm_aes, k, res);
        if (err)
                BT_ERR("Encrypt data error");
 
        return err;
 }
 
-static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16],
-                 u8 r2[16], u8 _r[16])
+static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16],
+                 u8 _r[16])
 {
+       struct hci_dev *hdev = smp->conn->hcon->hdev;
        int err;
 
+       BT_DBG("%s", hdev->name);
+
        /* Just least significant octets from r1 and r2 are considered */
        memcpy(_r, r2, 8);
        memcpy(_r + 8, r1, 8);
 
-       err = smp_e(tfm, k, _r);
+       err = smp_e(smp->tfm_aes, k, _r);
        if (err)
                BT_ERR("Encrypt data error");
 
@@ -387,10 +391,12 @@ static const u8 gen_method[5][5] = {
 
 static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
 {
-       /* If either side has unknown io_caps, use JUST WORKS */
+       /* If either side has unknown io_caps, use JUST_CFM (which gets
+        * converted later to JUST_WORKS if we're initiators.
+        */
        if (local_io > SMP_IO_KEYBOARD_DISPLAY ||
            remote_io > SMP_IO_KEYBOARD_DISPLAY)
-               return JUST_WORKS;
+               return JUST_CFM;
 
        return gen_method[remote_io][local_io];
 }
@@ -410,21 +416,25 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
 
        BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io);
 
-       /* If neither side wants MITM, use JUST WORKS */
-       /* Otherwise, look up method from the table */
+       /* If neither side wants MITM, either "just" confirm an incoming
+        * request or use just-works for outgoing ones. The JUST_CFM
+        * will be converted to JUST_WORKS if necessary later in this
+        * function. If either side has MITM look up the method from the
+        * table.
+        */
        if (!(auth & SMP_AUTH_MITM))
-               method = JUST_WORKS;
+               method = JUST_CFM;
        else
                method = get_auth_method(smp, local_io, remote_io);
 
-       /* If not bonding, don't ask user to confirm a Zero TK */
-       if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM)
-               method = JUST_WORKS;
-
        /* Don't confirm locally initiated pairing attempts */
        if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
                method = JUST_WORKS;
 
+       /* Don't bother user space with no IO capabilities */
+       if (method == JUST_CFM && hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
+               method = JUST_WORKS;
+
        /* If Just Works, Continue with Zero TK */
        if (method == JUST_WORKS) {
                set_bit(SMP_FLAG_TK_VALID, &smp->flags);
@@ -439,7 +449,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
         * Confirms and the slave Enters the passkey.
         */
        if (method == OVERLAP) {
-               if (hcon->link_mode & HCI_LM_MASTER)
+               if (hcon->role == HCI_ROLE_MASTER)
                        method = CFM_PASSKEY;
                else
                        method = REQ_PASSKEY;
@@ -477,23 +487,15 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
 static u8 smp_confirm(struct smp_chan *smp)
 {
        struct l2cap_conn *conn = smp->conn;
-       struct hci_dev *hdev = conn->hcon->hdev;
-       struct crypto_blkcipher *tfm = hdev->tfm_aes;
        struct smp_cmd_pairing_confirm cp;
        int ret;
 
        BT_DBG("conn %p", conn);
 
-       /* Prevent mutual access to hdev->tfm_aes */
-       hci_dev_lock(hdev);
-
-       ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
+       ret = smp_c1(smp, smp->tk, smp->prnd, smp->preq, smp->prsp,
                     conn->hcon->init_addr_type, &conn->hcon->init_addr,
                     conn->hcon->resp_addr_type, &conn->hcon->resp_addr,
                     cp.confirm_val);
-
-       hci_dev_unlock(hdev);
-
        if (ret)
                return SMP_UNSPECIFIED;
 
@@ -508,25 +510,17 @@ static u8 smp_random(struct smp_chan *smp)
 {
        struct l2cap_conn *conn = smp->conn;
        struct hci_conn *hcon = conn->hcon;
-       struct hci_dev *hdev = hcon->hdev;
-       struct crypto_blkcipher *tfm = hdev->tfm_aes;
        u8 confirm[16];
        int ret;
 
-       if (IS_ERR_OR_NULL(tfm))
+       if (IS_ERR_OR_NULL(smp->tfm_aes))
                return SMP_UNSPECIFIED;
 
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
-       /* Prevent mutual access to hdev->tfm_aes */
-       hci_dev_lock(hdev);
-
-       ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+       ret = smp_c1(smp, smp->tk, smp->rrnd, smp->preq, smp->prsp,
                     hcon->init_addr_type, &hcon->init_addr,
                     hcon->resp_addr_type, &hcon->resp_addr, confirm);
-
-       hci_dev_unlock(hdev);
-
        if (ret)
                return SMP_UNSPECIFIED;
 
@@ -540,7 +534,7 @@ static u8 smp_random(struct smp_chan *smp)
                __le64 rand = 0;
                __le16 ediv = 0;
 
-               smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, stk);
+               smp_s1(smp, smp->tk, smp->rrnd, smp->prnd, stk);
 
                memset(stk + smp->enc_key_size, 0,
                       SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -550,6 +544,7 @@ static u8 smp_random(struct smp_chan *smp)
 
                hci_le_start_enc(hcon, ediv, rand, stk);
                hcon->enc_key_size = smp->enc_key_size;
+               set_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
        } else {
                u8 stk[16], auth;
                __le64 rand = 0;
@@ -558,7 +553,7 @@ static u8 smp_random(struct smp_chan *smp)
                smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
                             smp->prnd);
 
-               smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, stk);
+               smp_s1(smp, smp->tk, smp->prnd, smp->rrnd, stk);
 
                memset(stk + smp->enc_key_size, 0,
                       SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -568,9 +563,12 @@ static u8 smp_random(struct smp_chan *smp)
                else
                        auth = 0;
 
+               /* Even though there's no _SLAVE suffix this is the
+                * slave STK we're adding for later lookup (the master
+                * STK never needs to be stored).
+                */
                hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
-                           HCI_SMP_STK_SLAVE, auth, stk, smp->enc_key_size,
-                           ediv, rand);
+                           SMP_STK, auth, stk, smp->enc_key_size, ediv, rand);
        }
 
        return 0;
@@ -584,9 +582,15 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
        if (!smp)
                return NULL;
 
+       smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(smp->tfm_aes)) {
+               BT_ERR("Unable to create ECB crypto context");
+               kfree(smp);
+               return NULL;
+       }
+
        smp->conn = conn;
        conn->smp_chan = smp;
-       conn->hcon->smp_conn = conn;
 
        hci_conn_hold(conn->hcon);
 
@@ -606,6 +610,8 @@ void smp_chan_destroy(struct l2cap_conn *conn)
        kfree(smp->csrk);
        kfree(smp->slave_csrk);
 
+       crypto_free_blkcipher(smp->tfm_aes);
+
        /* If pairing failed clean up any keys we might have */
        if (!complete) {
                if (smp->ltk) {
@@ -626,19 +632,18 @@ void smp_chan_destroy(struct l2cap_conn *conn)
 
        kfree(smp);
        conn->smp_chan = NULL;
-       conn->hcon->smp_conn = NULL;
        hci_conn_drop(conn->hcon);
 }
 
 int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
 {
-       struct l2cap_conn *conn = hcon->smp_conn;
+       struct l2cap_conn *conn = hcon->l2cap_data;
        struct smp_chan *smp;
        u32 value;
 
        BT_DBG("");
 
-       if (!conn)
+       if (!conn || !test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
                return -ENOTCONN;
 
        smp = conn->smp_chan;
@@ -675,6 +680,7 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
 static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct smp_cmd_pairing rsp, *req = (void *) skb->data;
+       struct hci_dev *hdev = conn->hcon->hdev;
        struct smp_chan *smp;
        u8 key_size, auth, sec_level;
        int ret;
@@ -684,7 +690,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (skb->len < sizeof(*req))
                return SMP_INVALID_PARAMS;
 
-       if (conn->hcon->link_mode & HCI_LM_MASTER)
+       if (conn->hcon->role != HCI_ROLE_SLAVE)
                return SMP_CMD_NOTSUPP;
 
        if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
@@ -695,6 +701,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (!smp)
                return SMP_UNSPECIFIED;
 
+       if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags) &&
+           (req->auth_req & SMP_AUTH_BONDING))
+               return SMP_PAIRING_NOTSUPP;
+
        smp->preq[0] = SMP_CMD_PAIRING_REQ;
        memcpy(&smp->preq[1], req, sizeof(*req));
        skb_pull(skb, sizeof(*req));
@@ -734,8 +744,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (ret)
                return SMP_UNSPECIFIED;
 
-       clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
-
        return 0;
 }
 
@@ -751,7 +759,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
        if (skb->len < sizeof(*rsp))
                return SMP_INVALID_PARAMS;
 
-       if (!(conn->hcon->link_mode & HCI_LM_MASTER))
+       if (conn->hcon->role != HCI_ROLE_MASTER)
                return SMP_CMD_NOTSUPP;
 
        skb_pull(skb, sizeof(*rsp));
@@ -839,26 +847,51 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
        return smp_random(smp);
 }
 
-static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
+static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
 {
        struct smp_ltk *key;
        struct hci_conn *hcon = conn->hcon;
 
        key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
-                                  hcon->out);
+                                  hcon->role);
        if (!key)
-               return 0;
+               return false;
 
        if (sec_level > BT_SECURITY_MEDIUM && !key->authenticated)
-               return 0;
+               return false;
 
        if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
-               return 1;
+               return true;
 
        hci_le_start_enc(hcon, key->ediv, key->rand, key->val);
        hcon->enc_key_size = key->enc_size;
 
-       return 1;
+       /* We never store STKs for master role, so clear this flag */
+       clear_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
+
+       return true;
+}
+
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
+{
+       if (sec_level == BT_SECURITY_LOW)
+               return true;
+
+       /* If we're encrypted with an STK always claim insufficient
+        * security. This way we allow the connection to be re-encrypted
+        * with an LTK, even if the LTK provides the same level of
+        * security. Only exception is if we don't have an LTK (e.g.
+        * because of key distribution bits).
+        */
+       if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
+           hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
+                                hcon->role))
+               return false;
+
+       if (hcon->sec_level >= sec_level)
+               return true;
+
+       return false;
 }
 
 static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
@@ -874,10 +907,13 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (skb->len < sizeof(*rp))
                return SMP_INVALID_PARAMS;
 
-       if (!(conn->hcon->link_mode & HCI_LM_MASTER))
+       if (hcon->role != HCI_ROLE_MASTER)
                return SMP_CMD_NOTSUPP;
 
        sec_level = authreq_to_seclevel(rp->auth_req);
+       if (smp_sufficient_security(hcon, sec_level))
+               return 0;
+
        if (sec_level > hcon->pending_sec_level)
                hcon->pending_sec_level = sec_level;
 
@@ -887,7 +923,13 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
                return 0;
 
+       if (!test_bit(HCI_PAIRABLE, &hcon->hdev->dev_flags) &&
+           (rp->auth_req & SMP_AUTH_BONDING))
+               return SMP_PAIRING_NOTSUPP;
+
        smp = smp_chan_create(conn);
+       if (!smp)
+               return SMP_UNSPECIFIED;
 
        skb_pull(skb, sizeof(*rp));
 
@@ -899,22 +941,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 
        smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
 
-       clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
-
        return 0;
 }
 
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
-{
-       if (sec_level == BT_SECURITY_LOW)
-               return true;
-
-       if (hcon->sec_level >= sec_level)
-               return true;
-
-       return false;
-}
-
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
 {
        struct l2cap_conn *conn = hcon->l2cap_data;
@@ -936,7 +965,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
        if (sec_level > hcon->pending_sec_level)
                hcon->pending_sec_level = sec_level;
 
-       if (hcon->link_mode & HCI_LM_MASTER)
+       if (hcon->role == HCI_ROLE_MASTER)
                if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
                        return 0;
 
@@ -956,7 +985,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
            hcon->pending_sec_level > BT_SECURITY_MEDIUM)
                authreq |= SMP_AUTH_MITM;
 
-       if (hcon->link_mode & HCI_LM_MASTER) {
+       if (hcon->role == HCI_ROLE_MASTER) {
                struct smp_cmd_pairing cp;
 
                build_pairing_cmd(conn, &cp, NULL, authreq);
@@ -1021,7 +1050,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
 
        hci_dev_lock(hdev);
        authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
-       ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK,
+       ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK,
                          authenticated, smp->tk, smp->enc_key_size,
                          rp->ediv, rp->rand);
        smp->ltk = ltk;
@@ -1075,6 +1104,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
 
        skb_pull(skb, sizeof(*info));
 
+       hci_dev_lock(hcon->hdev);
+
        /* Strictly speaking the Core Specification (4.1) allows sending
         * an empty address which would force us to rely on just the IRK
         * as "identity information". However, since such
@@ -1084,8 +1115,7 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
         */
        if (!bacmp(&info->bdaddr, BDADDR_ANY)) {
                BT_ERR("Ignoring IRK with no identity address");
-               smp_distribute_keys(conn);
-               return 0;
+               goto distribute;
        }
 
        bacpy(&smp->id_addr, &info->bdaddr);
@@ -1099,8 +1129,11 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
        smp->remote_irk = hci_add_irk(conn->hcon->hdev, &smp->id_addr,
                                      smp->id_addr_type, smp->irk, &rpa);
 
+distribute:
        smp_distribute_keys(conn);
 
+       hci_dev_unlock(hcon->hdev);
+
        return 0;
 }
 
@@ -1156,7 +1189,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
        }
 
        if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
-               err = -ENOTSUPP;
+               err = -EOPNOTSUPP;
                reason = SMP_PAIRING_NOTSUPP;
                goto done;
        }
@@ -1174,7 +1207,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
            !conn->smp_chan) {
                BT_ERR("Unexpected SMP command 0x%02x. Disconnecting.", code);
                kfree_skb(skb);
-               return -ENOTSUPP;
+               return -EOPNOTSUPP;
        }
 
        switch (code) {
@@ -1337,7 +1370,7 @@ int smp_distribute_keys(struct l2cap_conn *conn)
 
                authenticated = hcon->sec_level == BT_SECURITY_HIGH;
                ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type,
-                                 HCI_SMP_LTK_SLAVE, authenticated, enc.ltk,
+                                 SMP_LTK_SLAVE, authenticated, enc.ltk,
                                  smp->enc_key_size, ediv, rand);
                smp->slave_ltk = ltk;
 
index 5a8dc36460a1b7e3a61b08d18407708673a7d1a5..796f4f45f92f67e43e32d75bfb015b5175f2aba0 100644 (file)
@@ -116,6 +116,13 @@ struct smp_cmd_security_req {
 #define SMP_MIN_ENC_KEY_SIZE           7
 #define SMP_MAX_ENC_KEY_SIZE           16
 
+/* LTK types used in internal storage (struct smp_ltk) */
+enum {
+       SMP_STK,
+       SMP_LTK,
+       SMP_LTK_SLAVE,
+};
+
 /* SMP Commands */
 bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
 int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
diff --git a/net/ieee802154/6lowpan_iphc.c b/net/ieee802154/6lowpan_iphc.c
deleted file mode 100644 (file)
index 211b568..0000000
+++ /dev/null
@@ -1,801 +0,0 @@
-/*
- * Copyright 2011, Siemens AG
- * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- */
-
-/*
- * Based on patches from Jon Smirl <jonsmirl@gmail.com>
- * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
- *
- * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/* Jon's code is based on 6lowpan implementation for Contiki which is:
- * Copyright (c) 2008, Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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/bitops.h>
-#include <linux/if_arp.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <net/6lowpan.h>
-#include <net/ipv6.h>
-#include <net/af_ieee802154.h>
-
-/*
- * Uncompress address function for source and
- * destination address(non-multicast).
- *
- * address_mode is sam value or dam value.
- */
-static int uncompress_addr(struct sk_buff *skb,
-                               struct in6_addr *ipaddr, const u8 address_mode,
-                               const u8 *lladdr, const u8 addr_type,
-                               const u8 addr_len)
-{
-       bool fail;
-
-       switch (address_mode) {
-       case LOWPAN_IPHC_ADDR_00:
-               /* for global link addresses */
-               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
-               break;
-       case LOWPAN_IPHC_ADDR_01:
-               /* fe:80::XXXX:XXXX:XXXX:XXXX */
-               ipaddr->s6_addr[0] = 0xFE;
-               ipaddr->s6_addr[1] = 0x80;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
-               break;
-       case LOWPAN_IPHC_ADDR_02:
-               /* fe:80::ff:fe00:XXXX */
-               ipaddr->s6_addr[0] = 0xFE;
-               ipaddr->s6_addr[1] = 0x80;
-               ipaddr->s6_addr[11] = 0xFF;
-               ipaddr->s6_addr[12] = 0xFE;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
-               break;
-       case LOWPAN_IPHC_ADDR_03:
-               fail = false;
-               switch (addr_type) {
-               case IEEE802154_ADDR_LONG:
-                       /* fe:80::XXXX:XXXX:XXXX:XXXX
-                        *        \_________________/
-                        *              hwaddr
-                        */
-                       ipaddr->s6_addr[0] = 0xFE;
-                       ipaddr->s6_addr[1] = 0x80;
-                       memcpy(&ipaddr->s6_addr[8], lladdr, addr_len);
-                       /* second bit-flip (Universe/Local)
-                        * is done according RFC2464
-                        */
-                       ipaddr->s6_addr[8] ^= 0x02;
-                       break;
-               case IEEE802154_ADDR_SHORT:
-                       /* fe:80::ff:fe00:XXXX
-                        *                \__/
-                        *             short_addr
-                        *
-                        * Universe/Local bit is zero.
-                        */
-                       ipaddr->s6_addr[0] = 0xFE;
-                       ipaddr->s6_addr[1] = 0x80;
-                       ipaddr->s6_addr[11] = 0xFF;
-                       ipaddr->s6_addr[12] = 0xFE;
-                       ipaddr->s6_addr16[7] = htons(*((u16 *)lladdr));
-                       break;
-               default:
-                       pr_debug("Invalid addr_type set\n");
-                       return -EINVAL;
-               }
-               break;
-       default:
-               pr_debug("Invalid address mode value: 0x%x\n", address_mode);
-               return -EINVAL;
-       }
-
-       if (fail) {
-               pr_debug("Failed to fetch skb data\n");
-               return -EIO;
-       }
-
-       raw_dump_inline(NULL, "Reconstructed ipv6 addr is",
-                       ipaddr->s6_addr, 16);
-
-       return 0;
-}
-
-/*
- * Uncompress address function for source context
- * based address(non-multicast).
- */
-static int uncompress_context_based_src_addr(struct sk_buff *skb,
-                                               struct in6_addr *ipaddr,
-                                               const u8 sam)
-{
-       switch (sam) {
-       case LOWPAN_IPHC_ADDR_00:
-               /* unspec address ::
-                * Do nothing, address is already ::
-                */
-               break;
-       case LOWPAN_IPHC_ADDR_01:
-               /* TODO */
-       case LOWPAN_IPHC_ADDR_02:
-               /* TODO */
-       case LOWPAN_IPHC_ADDR_03:
-               /* TODO */
-               netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
-               return -EINVAL;
-       default:
-               pr_debug("Invalid sam value: 0x%x\n", sam);
-               return -EINVAL;
-       }
-
-       raw_dump_inline(NULL,
-                       "Reconstructed context based ipv6 src addr is",
-                       ipaddr->s6_addr, 16);
-
-       return 0;
-}
-
-static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
-               struct net_device *dev, skb_delivery_cb deliver_skb)
-{
-       struct sk_buff *new;
-       int stat;
-
-       new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
-                                                               GFP_ATOMIC);
-       kfree_skb(skb);
-
-       if (!new)
-               return -ENOMEM;
-
-       skb_push(new, sizeof(struct ipv6hdr));
-       skb_reset_network_header(new);
-       skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
-
-       new->protocol = htons(ETH_P_IPV6);
-       new->pkt_type = PACKET_HOST;
-       new->dev = dev;
-
-       raw_dump_table(__func__, "raw skb data dump before receiving",
-                       new->data, new->len);
-
-       stat = deliver_skb(new, dev);
-
-       kfree_skb(new);
-
-       return stat;
-}
-
-/* Uncompress function for multicast destination address,
- * when M bit is set.
- */
-static int
-lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
-               struct in6_addr *ipaddr,
-               const u8 dam)
-{
-       bool fail;
-
-       switch (dam) {
-       case LOWPAN_IPHC_DAM_00:
-               /* 00:  128 bits.  The full address
-                * is carried in-line.
-                */
-               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
-               break;
-       case LOWPAN_IPHC_DAM_01:
-               /* 01:  48 bits.  The address takes
-                * the form ffXX::00XX:XXXX:XXXX.
-                */
-               ipaddr->s6_addr[0] = 0xFF;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
-               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[11], 5);
-               break;
-       case LOWPAN_IPHC_DAM_10:
-               /* 10:  32 bits.  The address takes
-                * the form ffXX::00XX:XXXX.
-                */
-               ipaddr->s6_addr[0] = 0xFF;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
-               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[13], 3);
-               break;
-       case LOWPAN_IPHC_DAM_11:
-               /* 11:  8 bits.  The address takes
-                * the form ff02::00XX.
-                */
-               ipaddr->s6_addr[0] = 0xFF;
-               ipaddr->s6_addr[1] = 0x02;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[15], 1);
-               break;
-       default:
-               pr_debug("DAM value has a wrong value: 0x%x\n", dam);
-               return -EINVAL;
-       }
-
-       if (fail) {
-               pr_debug("Failed to fetch skb data\n");
-               return -EIO;
-       }
-
-       raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is",
-                               ipaddr->s6_addr, 16);
-
-       return 0;
-}
-
-static int
-uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
-{
-       bool fail;
-       u8 tmp = 0, val = 0;
-
-       if (!uh)
-               goto err;
-
-       fail = lowpan_fetch_skb(skb, &tmp, 1);
-
-       if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
-               pr_debug("UDP header uncompression\n");
-               switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
-               case LOWPAN_NHC_UDP_CS_P_00:
-                       fail |= lowpan_fetch_skb(skb, &uh->source, 2);
-                       fail |= lowpan_fetch_skb(skb, &uh->dest, 2);
-                       break;
-               case LOWPAN_NHC_UDP_CS_P_01:
-                       fail |= lowpan_fetch_skb(skb, &uh->source, 2);
-                       fail |= lowpan_fetch_skb(skb, &val, 1);
-                       uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
-                       break;
-               case LOWPAN_NHC_UDP_CS_P_10:
-                       fail |= lowpan_fetch_skb(skb, &val, 1);
-                       uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
-                       fail |= lowpan_fetch_skb(skb, &uh->dest, 2);
-                       break;
-               case LOWPAN_NHC_UDP_CS_P_11:
-                       fail |= lowpan_fetch_skb(skb, &val, 1);
-                       uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT +
-                                          (val >> 4));
-                       uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT +
-                                        (val & 0x0f));
-                       break;
-               default:
-                       pr_debug("ERROR: unknown UDP format\n");
-                       goto err;
-                       break;
-               }
-
-               pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
-                        ntohs(uh->source), ntohs(uh->dest));
-
-               /* checksum */
-               if (tmp & LOWPAN_NHC_UDP_CS_C) {
-                       pr_debug_ratelimited("checksum elided currently not supported\n");
-                       goto err;
-               } else {
-                       fail |= lowpan_fetch_skb(skb, &uh->check, 2);
-               }
-
-               /*
-                * UDP lenght needs to be infered from the lower layers
-                * here, we obtain the hint from the remaining size of the
-                * frame
-                */
-               uh->len = htons(skb->len + sizeof(struct udphdr));
-               pr_debug("uncompressed UDP length: src = %d", ntohs(uh->len));
-       } else {
-               pr_debug("ERROR: unsupported NH format\n");
-               goto err;
-       }
-
-       if (fail)
-               goto err;
-
-       return 0;
-err:
-       return -EINVAL;
-}
-
-/* TTL uncompression values */
-static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
-
-int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
-               const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
-               const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
-               u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb)
-{
-       struct ipv6hdr hdr = {};
-       u8 tmp, num_context = 0;
-       int err;
-
-       raw_dump_table(__func__, "raw skb data dump uncompressed",
-                               skb->data, skb->len);
-
-       /* another if the CID flag is set */
-       if (iphc1 & LOWPAN_IPHC_CID) {
-               pr_debug("CID flag is set, increase header with one\n");
-               if (lowpan_fetch_skb_u8(skb, &num_context))
-                       goto drop;
-       }
-
-       hdr.version = 6;
-
-       /* Traffic Class and Flow Label */
-       switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
-       /*
-        * Traffic Class and FLow Label carried in-line
-        * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
-        */
-       case 0: /* 00b */
-               if (lowpan_fetch_skb_u8(skb, &tmp))
-                       goto drop;
-
-               memcpy(&hdr.flow_lbl, &skb->data[0], 3);
-               skb_pull(skb, 3);
-               hdr.priority = ((tmp >> 2) & 0x0f);
-               hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
-                                       (hdr.flow_lbl[0] & 0x0f);
-               break;
-       /*
-        * Traffic class carried in-line
-        * ECN + DSCP (1 byte), Flow Label is elided
-        */
-       case 2: /* 10b */
-               if (lowpan_fetch_skb_u8(skb, &tmp))
-                       goto drop;
-
-               hdr.priority = ((tmp >> 2) & 0x0f);
-               hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
-               break;
-       /*
-        * Flow Label carried in-line
-        * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
-        */
-       case 1: /* 01b */
-               if (lowpan_fetch_skb_u8(skb, &tmp))
-                       goto drop;
-
-               hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
-               memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
-               skb_pull(skb, 2);
-               break;
-       /* Traffic Class and Flow Label are elided */
-       case 3: /* 11b */
-               break;
-       default:
-               break;
-       }
-
-       /* Next Header */
-       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
-               /* Next header is carried inline */
-               if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr)))
-                       goto drop;
-
-               pr_debug("NH flag is set, next header carried inline: %02x\n",
-                        hdr.nexthdr);
-       }
-
-       /* Hop Limit */
-       if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
-               hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
-       else {
-               if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit)))
-                       goto drop;
-       }
-
-       /* Extract SAM to the tmp variable */
-       tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
-
-       if (iphc1 & LOWPAN_IPHC_SAC) {
-               /* Source address context based uncompression */
-               pr_debug("SAC bit is set. Handle context based source address.\n");
-               err = uncompress_context_based_src_addr(
-                               skb, &hdr.saddr, tmp);
-       } else {
-               /* Source address uncompression */
-               pr_debug("source address stateless compression\n");
-               err = uncompress_addr(skb, &hdr.saddr, tmp, saddr,
-                                       saddr_type, saddr_len);
-       }
-
-       /* Check on error of previous branch */
-       if (err)
-               goto drop;
-
-       /* Extract DAM to the tmp variable */
-       tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
-
-       /* check for Multicast Compression */
-       if (iphc1 & LOWPAN_IPHC_M) {
-               if (iphc1 & LOWPAN_IPHC_DAC) {
-                       pr_debug("dest: context-based mcast compression\n");
-                       /* TODO: implement this */
-               } else {
-                       err = lowpan_uncompress_multicast_daddr(
-                                               skb, &hdr.daddr, tmp);
-                       if (err)
-                               goto drop;
-               }
-       } else {
-               err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
-                                       daddr_type, daddr_len);
-               pr_debug("dest: stateless compression mode %d dest %pI6c\n",
-                       tmp, &hdr.daddr);
-               if (err)
-                       goto drop;
-       }
-
-       /* UDP data uncompression */
-       if (iphc0 & LOWPAN_IPHC_NH_C) {
-               struct udphdr uh;
-               struct sk_buff *new;
-               if (uncompress_udp_header(skb, &uh))
-                       goto drop;
-
-               /*
-                * replace the compressed UDP head by the uncompressed UDP
-                * header
-                */
-               new = skb_copy_expand(skb, sizeof(struct udphdr),
-                                     skb_tailroom(skb), GFP_ATOMIC);
-               kfree_skb(skb);
-
-               if (!new)
-                       return -ENOMEM;
-
-               skb = new;
-
-               skb_push(skb, sizeof(struct udphdr));
-               skb_reset_transport_header(skb);
-               skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
-
-               raw_dump_table(__func__, "raw UDP header dump",
-                                     (u8 *)&uh, sizeof(uh));
-
-               hdr.nexthdr = UIP_PROTO_UDP;
-       }
-
-       hdr.payload_len = htons(skb->len);
-
-       pr_debug("skb headroom size = %d, data length = %d\n",
-                skb_headroom(skb), skb->len);
-
-       pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n\t"
-                "nexthdr = 0x%02x\n\thop_lim = %d\n\tdest    = %pI6c\n",
-               hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
-               hdr.hop_limit, &hdr.daddr);
-
-       raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
-                                                       sizeof(hdr));
-
-       return skb_deliver(skb, &hdr, dev, deliver_skb);
-
-drop:
-       kfree_skb(skb);
-       return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(lowpan_process_data);
-
-static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift,
-                               const struct in6_addr *ipaddr,
-                               const unsigned char *lladdr)
-{
-       u8 val = 0;
-
-       if (is_addr_mac_addr_based(ipaddr, lladdr)) {
-               val = 3; /* 0-bits */
-               pr_debug("address compression 0 bits\n");
-       } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
-               /* compress IID to 16 bits xxxx::XXXX */
-               memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2);
-               *hc06_ptr += 2;
-               val = 2; /* 16-bits */
-               raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
-                       *hc06_ptr - 2, 2);
-       } else {
-               /* do not compress IID => xxxx::IID */
-               memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
-               *hc06_ptr += 8;
-               val = 1; /* 64-bits */
-               raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
-                       *hc06_ptr - 8, 8);
-       }
-
-       return rol8(val, shift);
-}
-
-static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
-{
-       struct udphdr *uh = udp_hdr(skb);
-       u8 tmp;
-
-       if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
-            LOWPAN_NHC_UDP_4BIT_PORT) &&
-           ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
-            LOWPAN_NHC_UDP_4BIT_PORT)) {
-               pr_debug("UDP header: both ports compression to 4 bits\n");
-               /* compression value */
-               tmp = LOWPAN_NHC_UDP_CS_P_11;
-               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
-               /* source and destination port */
-               tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
-                     ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
-               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
-       } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
-                       LOWPAN_NHC_UDP_8BIT_PORT) {
-               pr_debug("UDP header: remove 8 bits of dest\n");
-               /* compression value */
-               tmp = LOWPAN_NHC_UDP_CS_P_01;
-               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
-               /* source port */
-               lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source));
-               /* destination port */
-               tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
-               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
-       } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
-                       LOWPAN_NHC_UDP_8BIT_PORT) {
-               pr_debug("UDP header: remove 8 bits of source\n");
-               /* compression value */
-               tmp = LOWPAN_NHC_UDP_CS_P_10;
-               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
-               /* source port */
-               tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
-               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
-               /* destination port */
-               lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest));
-       } else {
-               pr_debug("UDP header: can't compress\n");
-               /* compression value */
-               tmp = LOWPAN_NHC_UDP_CS_P_00;
-               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
-               /* source port */
-               lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source));
-               /* destination port */
-               lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest));
-       }
-
-       /* checksum is always inline */
-       lowpan_push_hc_data(hc06_ptr, &uh->check, sizeof(uh->check));
-
-       /* skip the UDP header */
-       skb_pull(skb, sizeof(struct udphdr));
-}
-
-int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
-                       unsigned short type, const void *_daddr,
-                       const void *_saddr, unsigned int len)
-{
-       u8 tmp, iphc0, iphc1, *hc06_ptr;
-       struct ipv6hdr *hdr;
-       u8 head[100] = {};
-
-       if (type != ETH_P_IPV6)
-               return -EINVAL;
-
-       hdr = ipv6_hdr(skb);
-       hc06_ptr = head + 2;
-
-       pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n"
-                "\tnexthdr = 0x%02x\n\thop_lim = %d\n\tdest    = %pI6c\n",
-               hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
-               hdr->hop_limit, &hdr->daddr);
-
-       raw_dump_table(__func__, "raw skb network header dump",
-               skb_network_header(skb), sizeof(struct ipv6hdr));
-
-       /*
-        * As we copy some bit-length fields, in the IPHC encoding bytes,
-        * we sometimes use |=
-        * If the field is 0, and the current bit value in memory is 1,
-        * this does not work. We therefore reset the IPHC encoding here
-        */
-       iphc0 = LOWPAN_DISPATCH_IPHC;
-       iphc1 = 0;
-
-       /* TODO: context lookup */
-
-       raw_dump_inline(__func__, "saddr",
-                       (unsigned char *)_saddr, IEEE802154_ADDR_LEN);
-       raw_dump_inline(__func__, "daddr",
-                       (unsigned char *)_daddr, IEEE802154_ADDR_LEN);
-
-       raw_dump_table(__func__,
-                       "sending raw skb network uncompressed packet",
-                       skb->data, skb->len);
-
-       /*
-        * Traffic class, flow label
-        * If flow label is 0, compress it. If traffic class is 0, compress it
-        * We have to process both in the same time as the offset of traffic
-        * class depends on the presence of version and flow label
-        */
-
-       /* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */
-       tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
-       tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
-
-       if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
-            (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
-               /* flow label can be compressed */
-               iphc0 |= LOWPAN_IPHC_FL_C;
-               if ((hdr->priority == 0) &&
-                  ((hdr->flow_lbl[0] & 0xF0) == 0)) {
-                       /* compress (elide) all */
-                       iphc0 |= LOWPAN_IPHC_TC_C;
-               } else {
-                       /* compress only the flow label */
-                       *hc06_ptr = tmp;
-                       hc06_ptr += 1;
-               }
-       } else {
-               /* Flow label cannot be compressed */
-               if ((hdr->priority == 0) &&
-                  ((hdr->flow_lbl[0] & 0xF0) == 0)) {
-                       /* compress only traffic class */
-                       iphc0 |= LOWPAN_IPHC_TC_C;
-                       *hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
-                       memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2);
-                       hc06_ptr += 3;
-               } else {
-                       /* compress nothing */
-                       memcpy(hc06_ptr, hdr, 4);
-                       /* replace the top byte with new ECN | DSCP format */
-                       *hc06_ptr = tmp;
-                       hc06_ptr += 4;
-               }
-       }
-
-       /* NOTE: payload length is always compressed */
-
-       /* Next Header is compress if UDP */
-       if (hdr->nexthdr == UIP_PROTO_UDP)
-               iphc0 |= LOWPAN_IPHC_NH_C;
-
-       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
-               *hc06_ptr = hdr->nexthdr;
-               hc06_ptr += 1;
-       }
-
-       /*
-        * Hop limit
-        * if 1:   compress, encoding is 01
-        * if 64:  compress, encoding is 10
-        * if 255: compress, encoding is 11
-        * else do not compress
-        */
-       switch (hdr->hop_limit) {
-       case 1:
-               iphc0 |= LOWPAN_IPHC_TTL_1;
-               break;
-       case 64:
-               iphc0 |= LOWPAN_IPHC_TTL_64;
-               break;
-       case 255:
-               iphc0 |= LOWPAN_IPHC_TTL_255;
-               break;
-       default:
-               *hc06_ptr = hdr->hop_limit;
-               hc06_ptr += 1;
-               break;
-       }
-
-       /* source address compression */
-       if (is_addr_unspecified(&hdr->saddr)) {
-               pr_debug("source address is unspecified, setting SAC\n");
-               iphc1 |= LOWPAN_IPHC_SAC;
-       /* TODO: context lookup */
-       } else if (is_addr_link_local(&hdr->saddr)) {
-               iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
-                               LOWPAN_IPHC_SAM_BIT, &hdr->saddr, _saddr);
-               pr_debug("source address unicast link-local %pI6c "
-                       "iphc1 0x%02x\n", &hdr->saddr, iphc1);
-       } else {
-               pr_debug("send the full source address\n");
-               memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16);
-               hc06_ptr += 16;
-       }
-
-       /* destination address compression */
-       if (is_addr_mcast(&hdr->daddr)) {
-               pr_debug("destination address is multicast: ");
-               iphc1 |= LOWPAN_IPHC_M;
-               if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
-                       pr_debug("compressed to 1 octet\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_11;
-                       /* use last byte */
-                       *hc06_ptr = hdr->daddr.s6_addr[15];
-                       hc06_ptr += 1;
-               } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
-                       pr_debug("compressed to 4 octets\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_10;
-                       /* second byte + the last three */
-                       *hc06_ptr = hdr->daddr.s6_addr[1];
-                       memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3);
-                       hc06_ptr += 4;
-               } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
-                       pr_debug("compressed to 6 octets\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_01;
-                       /* second byte + the last five */
-                       *hc06_ptr = hdr->daddr.s6_addr[1];
-                       memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5);
-                       hc06_ptr += 6;
-               } else {
-                       pr_debug("using full address\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_00;
-                       memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16);
-                       hc06_ptr += 16;
-               }
-       } else {
-               /* TODO: context lookup */
-               if (is_addr_link_local(&hdr->daddr)) {
-                       iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
-                               LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr);
-                       pr_debug("dest address unicast link-local %pI6c "
-                               "iphc1 0x%02x\n", &hdr->daddr, iphc1);
-               } else {
-                       pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
-                       memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16);
-                       hc06_ptr += 16;
-               }
-       }
-
-       /* UDP header compression */
-       if (hdr->nexthdr == UIP_PROTO_UDP)
-               compress_udp_header(&hc06_ptr, skb);
-
-       head[0] = iphc0;
-       head[1] = iphc1;
-
-       skb_pull(skb, sizeof(struct ipv6hdr));
-       skb_reset_transport_header(skb);
-       memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
-       skb_reset_network_header(skb);
-
-       pr_debug("header len %d skb %u\n", (int)(hc06_ptr - head), skb->len);
-
-       raw_dump_table(__func__, "raw skb data dump compressed",
-                               skb->data, skb->len);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(lowpan_header_compress);
-
-MODULE_LICENSE("GPL");
index 8af1330b3137b0282d316190770d77150f336d56..c0d4154d144f1eeac5418a048a0ccded798c3419 100644 (file)
@@ -12,13 +12,6 @@ config IEEE802154
 
 config IEEE802154_6LOWPAN
        tristate "6lowpan support over IEEE 802.15.4"
-       depends on IEEE802154 && IPV6
-       select 6LOWPAN_IPHC
+       depends on IEEE802154 && 6LOWPAN
        ---help---
          IPv6 compression over IEEE 802.15.4.
-
-config 6LOWPAN_IPHC
-       tristate
-       ---help---
-         6lowpan compression code which is shared between IEEE 802.15.4 and Bluetooth
-         stacks.
index bf1b51497a41048442640cf02d8de1f62c18ef6a..3914b1ed42743c50aaa4fe0a40927133cf25dae1 100644 (file)
@@ -1,8 +1,7 @@
 obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
-obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o
-obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o
+obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
 
-6lowpan-y := 6lowpan_rtnl.o reassembly.o
+ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
 ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \
                 header_ops.o
 af_802154-y := af_ieee802154.o raw.o dgram.o
index 97b5dcad50250d22bb5c5ad7ac41c18558d967ce..aeb6a483b3bc881e4b45c6dad7aacc9ae57bde37 100644 (file)
@@ -19,14 +19,6 @@ if MAC80211 != n
 config MAC80211_HAS_RC
        bool
 
-config MAC80211_RC_PID
-       bool "PID controller based rate control algorithm" if EXPERT
-       select MAC80211_HAS_RC
-       ---help---
-         This option enables a TX rate control algorithm for
-         mac80211 that uses a PID controller to select the TX
-         rate.
-
 config MAC80211_RC_MINSTREL
        bool "Minstrel" if EXPERT
        select MAC80211_HAS_RC
@@ -51,14 +43,6 @@ choice
          overridden through the ieee80211_default_rc_algo module
          parameter if different algorithms are available.
 
-config MAC80211_RC_DEFAULT_PID
-       bool "PID controller based rate control algorithm"
-       depends on MAC80211_RC_PID
-       ---help---
-         Select the PID controller based rate control as the
-         default rate control algorithm. You should choose
-         this unless you know what you are doing.
-
 config MAC80211_RC_DEFAULT_MINSTREL
        bool "Minstrel"
        depends on MAC80211_RC_MINSTREL
@@ -72,7 +56,6 @@ config MAC80211_RC_DEFAULT
        string
        default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT
        default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
-       default "pid" if MAC80211_RC_DEFAULT_PID
        default ""
 
 endif
index 1e46ffa69167973921b795f8757f903234a61b94..7273d2796dd1a79e4a529bb38a69a7722a7dec3e 100644 (file)
@@ -17,6 +17,7 @@ mac80211-y := \
        aes_ccm.o \
        aes_cmac.o \
        cfg.o \
+       ethtool.o \
        rx.o \
        spectmgmt.o \
        tx.o \
@@ -47,17 +48,12 @@ mac80211-$(CONFIG_PM) += pm.o
 
 CFLAGS_trace.o := -I$(src)
 
-# objects for PID algorithm
-rc80211_pid-y := rc80211_pid_algo.o
-rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
-
 rc80211_minstrel-y := rc80211_minstrel.o
 rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
 
 rc80211_minstrel_ht-y := rc80211_minstrel_ht.o
 rc80211_minstrel_ht-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o
 
-mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y)
 mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
 mac80211-$(CONFIG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y)
 
index 31bf2586fb84a59a0b8ce964c1717e2555a7ea65..f0e84bc48038932b022a0623ab1e2bf11a9fdfaf 100644 (file)
@@ -52,7 +52,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
        del_timer_sync(&tid_rx->reorder_timer);
 
        for (i = 0; i < tid_rx->buf_size; i++)
-               dev_kfree_skb(tid_rx->reorder_buf[i]);
+               __skb_queue_purge(&tid_rx->reorder_buf[i]);
        kfree(tid_rx->reorder_buf);
        kfree(tid_rx->reorder_time);
        kfree(tid_rx);
@@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
        ieee80211_tx_skb(sdata, skb);
 }
 
-void ieee80211_process_addba_request(struct ieee80211_local *local,
-                                    struct sta_info *sta,
-                                    struct ieee80211_mgmt *mgmt,
-                                    size_t len)
+void __ieee80211_start_rx_ba_session(struct sta_info *sta,
+                                    u8 dialog_token, u16 timeout,
+                                    u16 start_seq_num, u16 ba_policy, u16 tid,
+                                    u16 buf_size, bool tx)
 {
+       struct ieee80211_local *local = sta->sdata->local;
        struct tid_ampdu_rx *tid_agg_rx;
-       u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
-       u8 dialog_token;
-       int ret = -EOPNOTSUPP;
-
-       /* extract session parameters from addba request frame */
-       dialog_token = mgmt->u.action.u.addba_req.dialog_token;
-       timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
-       start_seq_num =
-               le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
-
-       capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
-       ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
-       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
-       buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
-
-       status = WLAN_STATUS_REQUEST_DECLINED;
+       int i, ret = -EOPNOTSUPP;
+       u16 status = WLAN_STATUS_REQUEST_DECLINED;
 
        if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) {
                ht_dbg(sta->sdata,
@@ -264,7 +251,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                status = WLAN_STATUS_INVALID_QOS_PARAM;
                ht_dbg_ratelimited(sta->sdata,
                                   "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
-                                  mgmt->sa, tid, ba_policy, buf_size);
+                                  sta->sta.addr, tid, ba_policy, buf_size);
                goto end_no_lock;
        }
        /* determine default buffer size */
@@ -281,7 +268,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
        if (sta->ampdu_mlme.tid_rx[tid]) {
                ht_dbg_ratelimited(sta->sdata,
                                   "unexpected AddBA Req from %pM on tid %u\n",
-                                  mgmt->sa, tid);
+                                  sta->sta.addr, tid);
 
                /* delete existing Rx BA session on the same tid */
                ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT,
@@ -308,7 +295,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
 
        /* prepare reordering buffer */
        tid_agg_rx->reorder_buf =
-               kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL);
+               kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL);
        tid_agg_rx->reorder_time =
                kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
        if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
@@ -318,6 +305,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
                goto end;
        }
 
+       for (i = 0; i < buf_size; i++)
+               __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
+
        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 %pM tid %d result %d\n",
@@ -350,6 +340,74 @@ end:
        mutex_unlock(&sta->ampdu_mlme.mtx);
 
 end_no_lock:
-       ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
-                                 dialog_token, status, 1, buf_size, timeout);
+       if (tx)
+               ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
+                                         dialog_token, status, 1, buf_size,
+                                         timeout);
+}
+
+void ieee80211_process_addba_request(struct ieee80211_local *local,
+                                    struct sta_info *sta,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len)
+{
+       u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
+       u8 dialog_token;
+
+       /* extract session parameters from addba request frame */
+       dialog_token = mgmt->u.action.u.addba_req.dialog_token;
+       timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout);
+       start_seq_num =
+               le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4;
+
+       capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+       ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1;
+       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+       buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
+
+       __ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
+                                       start_seq_num, ba_policy, tid,
+                                       buf_size, true);
+}
+
+void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                       const u8 *addr, u16 tid)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_rx_agg *rx_agg;
+       struct sk_buff *skb = dev_alloc_skb(0);
+
+       if (unlikely(!skb))
+               return;
+
+       rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
+       memcpy(&rx_agg->addr, addr, ETH_ALEN);
+       rx_agg->tid = tid;
+
+       skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START;
+       skb_queue_tail(&sdata->skb_queue, skb);
+       ieee80211_queue_work(&local->hw, &sdata->work);
+}
+EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl);
+
+void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif,
+                                      const u8 *addr, u16 tid)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_rx_agg *rx_agg;
+       struct sk_buff *skb = dev_alloc_skb(0);
+
+       if (unlikely(!skb))
+               return;
+
+       rx_agg = (struct ieee80211_rx_agg *) &skb->cb;
+       memcpy(&rx_agg->addr, addr, ETH_ALEN);
+       rx_agg->tid = tid;
+
+       skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP;
+       skb_queue_tail(&sdata->skb_queue, skb);
+       ieee80211_queue_work(&local->hw, &sdata->work);
 }
+EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl);
index ce9633a3cfb0c54abe7aa87746f2b843cf33e65c..d6986f3aa5c469fa15dfbc585d72ec0b5cc53af4 100644 (file)
@@ -170,10 +170,13 @@ ieee80211_stop_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
 {
        int queue = sdata->vif.hw_queue[ieee80211_ac_from_tid(tid)];
 
+       /* we do refcounting here, so don't use the queue reason refcounting */
+
        if (atomic_inc_return(&sdata->local->agg_queue_stop[queue]) == 1)
                ieee80211_stop_queue_by_reason(
                        &sdata->local->hw, queue,
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
+                       false);
        __acquire(agg_queue);
 }
 
@@ -185,7 +188,8 @@ ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid)
        if (atomic_dec_return(&sdata->local->agg_queue_stop[queue]) == 0)
                ieee80211_wake_queue_by_reason(
                        &sdata->local->hw, queue,
-                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+                       IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
+                       false);
        __release(agg_queue);
 }
 
index 592f4b152ba8a51e4d1c3b3bc3360286959ff1e0..927b4ea0128bbc365a9692302ba299d5da16b2c4 100644 (file)
@@ -468,330 +468,6 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
 }
 
-static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
-{
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
-       struct ieee80211_local *local = sdata->local;
-       struct rate_control_ref *ref = NULL;
-       struct timespec uptime;
-       u64 packets = 0;
-       u32 thr = 0;
-       int i, ac;
-
-       if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
-               ref = local->rate_ctrl;
-
-       sinfo->generation = sdata->local->sta_generation;
-
-       sinfo->filled = STATION_INFO_INACTIVE_TIME |
-                       STATION_INFO_RX_BYTES64 |
-                       STATION_INFO_TX_BYTES64 |
-                       STATION_INFO_RX_PACKETS |
-                       STATION_INFO_TX_PACKETS |
-                       STATION_INFO_TX_RETRIES |
-                       STATION_INFO_TX_FAILED |
-                       STATION_INFO_TX_BITRATE |
-                       STATION_INFO_RX_BITRATE |
-                       STATION_INFO_RX_DROP_MISC |
-                       STATION_INFO_BSS_PARAM |
-                       STATION_INFO_CONNECTED_TIME |
-                       STATION_INFO_STA_FLAGS |
-                       STATION_INFO_BEACON_LOSS_COUNT;
-
-       do_posix_clock_monotonic_gettime(&uptime);
-       sinfo->connected_time = uptime.tv_sec - sta->last_connected;
-
-       sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
-       sinfo->tx_bytes = 0;
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-               sinfo->tx_bytes += sta->tx_bytes[ac];
-               packets += sta->tx_packets[ac];
-       }
-       sinfo->tx_packets = packets;
-       sinfo->rx_bytes = sta->rx_bytes;
-       sinfo->rx_packets = sta->rx_packets;
-       sinfo->tx_retries = sta->tx_retry_count;
-       sinfo->tx_failed = sta->tx_retry_failed;
-       sinfo->rx_dropped_misc = sta->rx_dropped;
-       sinfo->beacon_loss_count = sta->beacon_loss_count;
-
-       if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
-           (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
-               sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
-               if (!local->ops->get_rssi ||
-                   drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
-                       sinfo->signal = (s8)sta->last_signal;
-               sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
-       }
-       if (sta->chains) {
-               sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
-                                STATION_INFO_CHAIN_SIGNAL_AVG;
-
-               sinfo->chains = sta->chains;
-               for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
-                       sinfo->chain_signal[i] = sta->chain_signal_last[i];
-                       sinfo->chain_signal_avg[i] =
-                               (s8) -ewma_read(&sta->chain_signal_avg[i]);
-               }
-       }
-
-       sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
-       sta_set_rate_info_rx(sta, &sinfo->rxrate);
-
-       if (ieee80211_vif_is_mesh(&sdata->vif)) {
-#ifdef CONFIG_MAC80211_MESH
-               sinfo->filled |= STATION_INFO_LLID |
-                                STATION_INFO_PLID |
-                                STATION_INFO_PLINK_STATE |
-                                STATION_INFO_LOCAL_PM |
-                                STATION_INFO_PEER_PM |
-                                STATION_INFO_NONPEER_PM;
-
-               sinfo->llid = sta->llid;
-               sinfo->plid = sta->plid;
-               sinfo->plink_state = sta->plink_state;
-               if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
-                       sinfo->filled |= STATION_INFO_T_OFFSET;
-                       sinfo->t_offset = sta->t_offset;
-               }
-               sinfo->local_pm = sta->local_pm;
-               sinfo->peer_pm = sta->peer_pm;
-               sinfo->nonpeer_pm = sta->nonpeer_pm;
-#endif
-       }
-
-       sinfo->bss_param.flags = 0;
-       if (sdata->vif.bss_conf.use_cts_prot)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
-       if (sdata->vif.bss_conf.use_short_preamble)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
-       if (sdata->vif.bss_conf.use_short_slot)
-               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
-       sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
-       sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
-
-       sinfo->sta_flags.set = 0;
-       sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
-                               BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
-                               BIT(NL80211_STA_FLAG_WME) |
-                               BIT(NL80211_STA_FLAG_MFP) |
-                               BIT(NL80211_STA_FLAG_AUTHENTICATED) |
-                               BIT(NL80211_STA_FLAG_ASSOCIATED) |
-                               BIT(NL80211_STA_FLAG_TDLS_PEER);
-       if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
-       if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
-       if (test_sta_flag(sta, WLAN_STA_WME))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
-       if (test_sta_flag(sta, WLAN_STA_MFP))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
-       if (test_sta_flag(sta, WLAN_STA_AUTH))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
-       if (test_sta_flag(sta, WLAN_STA_ASSOC))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
-       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
-               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
-
-       /* check if the driver has a SW RC implementation */
-       if (ref && ref->ops->get_expected_throughput)
-               thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
-       else
-               thr = drv_get_expected_throughput(local, &sta->sta);
-
-       if (thr != 0) {
-               sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
-               sinfo->expected_throughput = thr;
-       }
-}
-
-static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
-       "rx_packets", "rx_bytes", "wep_weak_iv_count",
-       "rx_duplicates", "rx_fragments", "rx_dropped",
-       "tx_packets", "tx_bytes", "tx_fragments",
-       "tx_filtered", "tx_retry_failed", "tx_retries",
-       "beacon_loss", "sta_state", "txrate", "rxrate", "signal",
-       "channel", "noise", "ch_time", "ch_time_busy",
-       "ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
-};
-#define STA_STATS_LEN  ARRAY_SIZE(ieee80211_gstrings_sta_stats)
-
-static int ieee80211_get_et_sset_count(struct wiphy *wiphy,
-                                      struct net_device *dev,
-                                      int sset)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       int rv = 0;
-
-       if (sset == ETH_SS_STATS)
-               rv += STA_STATS_LEN;
-
-       rv += drv_get_et_sset_count(sdata, sset);
-
-       if (rv == 0)
-               return -EOPNOTSUPP;
-       return rv;
-}
-
-static void ieee80211_get_et_stats(struct wiphy *wiphy,
-                                  struct net_device *dev,
-                                  struct ethtool_stats *stats,
-                                  u64 *data)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       struct ieee80211_channel *channel;
-       struct sta_info *sta;
-       struct ieee80211_local *local = sdata->local;
-       struct station_info sinfo;
-       struct survey_info survey;
-       int i, q;
-#define STA_STATS_SURVEY_LEN 7
-
-       memset(data, 0, sizeof(u64) * STA_STATS_LEN);
-
-#define ADD_STA_STATS(sta)                             \
-       do {                                            \
-               data[i++] += sta->rx_packets;           \
-               data[i++] += sta->rx_bytes;             \
-               data[i++] += sta->wep_weak_iv_count;    \
-               data[i++] += sta->num_duplicates;       \
-               data[i++] += sta->rx_fragments;         \
-               data[i++] += sta->rx_dropped;           \
-                                                       \
-               data[i++] += sinfo.tx_packets;          \
-               data[i++] += sinfo.tx_bytes;            \
-               data[i++] += sta->tx_fragments;         \
-               data[i++] += sta->tx_filtered_count;    \
-               data[i++] += sta->tx_retry_failed;      \
-               data[i++] += sta->tx_retry_count;       \
-               data[i++] += sta->beacon_loss_count;    \
-       } while (0)
-
-       /* For Managed stations, find the single station based on BSSID
-        * and use that.  For interface types, iterate through all available
-        * stations and add stats for any station that is assigned to this
-        * network device.
-        */
-
-       mutex_lock(&local->sta_mtx);
-
-       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid);
-
-               if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
-                       goto do_survey;
-
-               sinfo.filled = 0;
-               sta_set_sinfo(sta, &sinfo);
-
-               i = 0;
-               ADD_STA_STATS(sta);
-
-               data[i++] = sta->sta_state;
-
-
-               if (sinfo.filled & STATION_INFO_TX_BITRATE)
-                       data[i] = 100000 *
-                               cfg80211_calculate_bitrate(&sinfo.txrate);
-               i++;
-               if (sinfo.filled & STATION_INFO_RX_BITRATE)
-                       data[i] = 100000 *
-                               cfg80211_calculate_bitrate(&sinfo.rxrate);
-               i++;
-
-               if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
-                       data[i] = (u8)sinfo.signal_avg;
-               i++;
-       } else {
-               list_for_each_entry(sta, &local->sta_list, list) {
-                       /* Make sure this station belongs to the proper dev */
-                       if (sta->sdata->dev != dev)
-                               continue;
-
-                       sinfo.filled = 0;
-                       sta_set_sinfo(sta, &sinfo);
-                       i = 0;
-                       ADD_STA_STATS(sta);
-               }
-       }
-
-do_survey:
-       i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
-       /* Get survey stats for current channel */
-       survey.filled = 0;
-
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-       if (chanctx_conf)
-               channel = chanctx_conf->def.chan;
-       else
-               channel = NULL;
-       rcu_read_unlock();
-
-       if (channel) {
-               q = 0;
-               do {
-                       survey.filled = 0;
-                       if (drv_get_survey(local, q, &survey) != 0) {
-                               survey.filled = 0;
-                               break;
-                       }
-                       q++;
-               } while (channel != survey.channel);
-       }
-
-       if (survey.filled)
-               data[i++] = survey.channel->center_freq;
-       else
-               data[i++] = 0;
-       if (survey.filled & SURVEY_INFO_NOISE_DBM)
-               data[i++] = (u8)survey.noise;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
-               data[i++] = survey.channel_time;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
-               data[i++] = survey.channel_time_busy;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
-               data[i++] = survey.channel_time_ext_busy;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
-               data[i++] = survey.channel_time_rx;
-       else
-               data[i++] = -1LL;
-       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
-               data[i++] = survey.channel_time_tx;
-       else
-               data[i++] = -1LL;
-
-       mutex_unlock(&local->sta_mtx);
-
-       if (WARN_ON(i != STA_STATS_LEN))
-               return;
-
-       drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
-}
-
-static void ieee80211_get_et_strings(struct wiphy *wiphy,
-                                    struct net_device *dev,
-                                    u32 sset, u8 *data)
-{
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       int sz_sta_stats = 0;
-
-       if (sset == ETH_SS_STATS) {
-               sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
-               memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
-       }
-       drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
-}
-
 static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
                                  int idx, u8 *mac, struct station_info *sinfo)
 {
@@ -878,7 +554,8 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
 }
 
 static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
-                                   const u8 *resp, size_t resp_len)
+                                   const u8 *resp, size_t resp_len,
+                                   const struct ieee80211_csa_settings *csa)
 {
        struct probe_resp *new, *old;
 
@@ -894,6 +571,11 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
        new->len = resp_len;
        memcpy(new->data, resp, resp_len);
 
+       if (csa)
+               memcpy(new->csa_counter_offsets, csa->counter_offsets_presp,
+                      csa->n_counter_offsets_presp *
+                      sizeof(new->csa_counter_offsets[0]));
+
        rcu_assign_pointer(sdata->u.ap.probe_resp, new);
        if (old)
                kfree_rcu(old, rcu_head);
@@ -902,7 +584,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
 }
 
 static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
-                                  struct cfg80211_beacon_data *params)
+                                  struct cfg80211_beacon_data *params,
+                                  const struct ieee80211_csa_settings *csa)
 {
        struct beacon_data *new, *old;
        int new_head_len, new_tail_len;
@@ -946,6 +629,13 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
        new->head_len = new_head_len;
        new->tail_len = new_tail_len;
 
+       if (csa) {
+               new->csa_current_counter = csa->count;
+               memcpy(new->csa_counter_offsets, csa->counter_offsets_beacon,
+                      csa->n_counter_offsets_beacon *
+                      sizeof(new->csa_counter_offsets[0]));
+       }
+
        /* copy in head */
        if (params->head)
                memcpy(new->head, params->head, new_head_len);
@@ -960,7 +650,7 @@ static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
                        memcpy(new->tail, old->tail, new_tail_len);
 
        err = ieee80211_set_probe_resp(sdata, params->probe_resp,
-                                      params->probe_resp_len);
+                                      params->probe_resp_len, csa);
        if (err < 0)
                return err;
        if (err == 0)
@@ -1045,7 +735,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                sdata->vif.bss_conf.p2p_noa_attr.oppps_ctwindow |=
                                        IEEE80211_P2P_OPPPS_ENABLE_BIT;
 
-       err = ieee80211_assign_beacon(sdata, &params->beacon);
+       err = ieee80211_assign_beacon(sdata, &params->beacon, NULL);
        if (err < 0) {
                ieee80211_vif_release_channel(sdata);
                return err;
@@ -1093,38 +783,13 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
        if (!old)
                return -ENOENT;
 
-       err = ieee80211_assign_beacon(sdata, params);
+       err = ieee80211_assign_beacon(sdata, params, NULL);
        if (err < 0)
                return err;
        ieee80211_bss_info_change_notify(sdata, err);
        return 0;
 }
 
-bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local)
-{
-       struct ieee80211_sub_if_data *sdata;
-
-       lockdep_assert_held(&local->mtx);
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
-               if (!ieee80211_sdata_running(sdata))
-                       continue;
-
-               if (!sdata->vif.csa_active)
-                       continue;
-
-               if (!sdata->csa_block_tx)
-                       continue;
-
-               rcu_read_unlock();
-               return true;
-       }
-       rcu_read_unlock();
-
-       return false;
-}
-
 static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1144,10 +809,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        /* abort any running channel switch */
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
+
        mutex_unlock(&local->mtx);
 
        kfree(sdata->u.ap.next_beacon);
@@ -1330,9 +997,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                }
        }
 
-       ret = sta_apply_auth_flags(local, sta, mask, set);
-       if (ret)
-               return ret;
+       /* auth flags will be set later for TDLS stations */
+       if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               ret = sta_apply_auth_flags(local, sta, mask, set);
+               if (ret)
+                       return ret;
+       }
 
        if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
                if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
@@ -1469,6 +1139,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
 #endif
        }
 
+       /* set the STA state after all sta info from usermode has been set */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
+               ret = sta_apply_auth_flags(local, sta, mask, set);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -3076,7 +2753,8 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
-               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+               err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon,
+                                             NULL);
                kfree(sdata->u.ap.next_beacon);
                sdata->u.ap.next_beacon = NULL;
 
@@ -3114,17 +2792,35 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
 
        sdata_assert_lock(sdata);
        lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
 
-       sdata->radar_required = sdata->csa_radar_required;
-       err = ieee80211_vif_change_channel(sdata, &changed);
-       if (err < 0)
-               return err;
+       /*
+        * using reservation isn't immediate as it may be deferred until later
+        * with multi-vif. once reservation is complete it will re-schedule the
+        * work with no reserved_chanctx so verify chandef to check if it
+        * completed successfully
+        */
 
-       if (!local->use_chanctx) {
-               local->_oper_chandef = sdata->csa_chandef;
-               ieee80211_hw_config(local, 0);
+       if (sdata->reserved_chanctx) {
+               /*
+                * with multi-vif csa driver may call ieee80211_csa_finish()
+                * many times while waiting for other interfaces to use their
+                * reservations
+                */
+               if (sdata->reserved_ready)
+                       return 0;
+
+               err = ieee80211_vif_use_reserved_context(sdata);
+               if (err)
+                       return err;
+
+               return 0;
        }
 
+       if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
+                                       &sdata->csa_chandef))
+               return -EINVAL;
+
        sdata->vif.csa_active = false;
 
        err = ieee80211_set_after_csa_beacon(sdata, &changed);
@@ -3134,10 +2830,11 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
        ieee80211_bss_info_change_notify(sdata, changed);
        cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
 
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
 
        return 0;
 }
@@ -3160,6 +2857,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
 
        sdata_lock(sdata);
        mutex_lock(&local->mtx);
+       mutex_lock(&local->chanctx_mtx);
 
        /* AP might have been stopped while waiting for the lock. */
        if (!sdata->vif.csa_active)
@@ -3171,6 +2869,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
        ieee80211_csa_finalize(sdata);
 
 unlock:
+       mutex_unlock(&local->chanctx_mtx);
        mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 }
@@ -3179,6 +2878,7 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                                    struct cfg80211_csa_settings *params,
                                    u32 *changed)
 {
+       struct ieee80211_csa_settings csa = {};
        int err;
 
        switch (sdata->vif.type) {
@@ -3213,20 +2913,13 @@ static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
                     IEEE80211_MAX_CSA_COUNTERS_NUM))
                        return -EINVAL;
 
-               /* make sure we don't have garbage in other counters */
-               memset(sdata->csa_counter_offset_beacon, 0,
-                      sizeof(sdata->csa_counter_offset_beacon));
-               memset(sdata->csa_counter_offset_presp, 0,
-                      sizeof(sdata->csa_counter_offset_presp));
-
-               memcpy(sdata->csa_counter_offset_beacon,
-                      params->counter_offsets_beacon,
-                      params->n_counter_offsets_beacon * sizeof(u16));
-               memcpy(sdata->csa_counter_offset_presp,
-                      params->counter_offsets_presp,
-                      params->n_counter_offsets_presp * sizeof(u16));
+               csa.counter_offsets_beacon = params->counter_offsets_beacon;
+               csa.counter_offsets_presp = params->counter_offsets_presp;
+               csa.n_counter_offsets_beacon = params->n_counter_offsets_beacon;
+               csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
+               csa.count = params->count;
 
-               err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+               err = ieee80211_assign_beacon(sdata, &params->beacon_csa, &csa);
                if (err < 0) {
                        kfree(sdata->u.ap.next_beacon);
                        return err;
@@ -3322,7 +3015,7 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *chanctx;
-       int err, num_chanctx, changed = 0;
+       int err, changed = 0;
 
        sdata_assert_lock(sdata);
        lockdep_assert_held(&local->mtx);
@@ -3337,46 +3030,50 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                                       &sdata->vif.bss_conf.chandef))
                return -EINVAL;
 
+       /* don't allow another channel switch if one is already active. */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
+
        mutex_lock(&local->chanctx_mtx);
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
        if (!conf) {
-               mutex_unlock(&local->chanctx_mtx);
-               return -EBUSY;
+               err = -EBUSY;
+               goto out;
        }
 
-       /* don't handle for multi-VIF cases */
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
-               mutex_unlock(&local->chanctx_mtx);
-               return -EBUSY;
+       if (!chanctx) {
+               err = -EBUSY;
+               goto out;
        }
-       num_chanctx = 0;
-       list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
-               num_chanctx++;
-       mutex_unlock(&local->chanctx_mtx);
 
-       if (num_chanctx > 1)
-               return -EBUSY;
+       err = ieee80211_vif_reserve_chanctx(sdata, &params->chandef,
+                                           chanctx->mode,
+                                           params->radar_required);
+       if (err)
+               goto out;
 
-       /* don't allow another channel switch if one is already active. */
-       if (sdata->vif.csa_active)
-               return -EBUSY;
+       /* if reservation is invalid then this will fail */
+       err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0);
+       if (err) {
+               ieee80211_vif_unreserve_chanctx(sdata);
+               goto out;
+       }
 
        err = ieee80211_set_csa_beacon(sdata, params, &changed);
-       if (err)
-               return err;
+       if (err) {
+               ieee80211_vif_unreserve_chanctx(sdata);
+               goto out;
+       }
 
-       sdata->csa_radar_required = params->radar_required;
        sdata->csa_chandef = params->chandef;
        sdata->csa_block_tx = params->block_tx;
-       sdata->csa_current_counter = params->count;
        sdata->vif.csa_active = true;
 
        if (sdata->csa_block_tx)
-               ieee80211_stop_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+               ieee80211_stop_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
 
        if (changed) {
                ieee80211_bss_info_change_notify(sdata, changed);
@@ -3386,7 +3083,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                ieee80211_csa_finalize(sdata);
        }
 
-       return 0;
+out:
+       mutex_unlock(&local->chanctx_mtx);
+       return err;
 }
 
 int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
@@ -3518,10 +3217,23 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
             sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            params->n_csa_offsets) {
                int i;
-               u8 c = sdata->csa_current_counter;
+               struct beacon_data *beacon = NULL;
 
-               for (i = 0; i < params->n_csa_offsets; i++)
-                       data[params->csa_offsets[i]] = c;
+               rcu_read_lock();
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP)
+                       beacon = rcu_dereference(sdata->u.ap.beacon);
+               else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+                       beacon = rcu_dereference(sdata->u.ibss.presp);
+               else if (ieee80211_vif_is_mesh(&sdata->vif))
+                       beacon = rcu_dereference(sdata->u.mesh.beacon);
+
+               if (beacon)
+                       for (i = 0; i < params->n_csa_offsets; i++)
+                               data[params->csa_offsets[i]] =
+                                       beacon->csa_current_counter;
+
+               rcu_read_unlock();
        }
 
        IEEE80211_SKB_CB(skb)->flags = flags;
@@ -3601,21 +3313,6 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
        return drv_get_antenna(local, tx_ant, rx_ant);
 }
 
-static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
-{
-       struct ieee80211_local *local = wiphy_priv(wiphy);
-
-       return drv_set_ringparam(local, tx, rx);
-}
-
-static void ieee80211_get_ringparam(struct wiphy *wiphy,
-                                   u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
-{
-       struct ieee80211_local *local = wiphy_priv(wiphy);
-
-       drv_get_ringparam(local, tx, tx_max, rx, rx_max);
-}
-
 static int ieee80211_set_rekey_data(struct wiphy *wiphy,
                                    struct net_device *dev,
                                    struct cfg80211_gtk_rekey_data *data)
@@ -3847,8 +3544,6 @@ const struct cfg80211_ops mac80211_config_ops = {
        .mgmt_frame_register = ieee80211_mgmt_frame_register,
        .set_antenna = ieee80211_set_antenna,
        .get_antenna = ieee80211_get_antenna,
-       .set_ringparam = ieee80211_set_ringparam,
-       .get_ringparam = ieee80211_get_ringparam,
        .set_rekey_data = ieee80211_set_rekey_data,
        .tdls_oper = ieee80211_tdls_oper,
        .tdls_mgmt = ieee80211_tdls_mgmt,
@@ -3857,9 +3552,6 @@ const struct cfg80211_ops mac80211_config_ops = {
 #ifdef CONFIG_PM
        .set_wakeup = ieee80211_set_wakeup,
 #endif
-       .get_et_sset_count = ieee80211_get_et_sset_count,
-       .get_et_stats = ieee80211_get_et_stats,
-       .get_et_strings = ieee80211_get_et_strings,
        .get_channel = ieee80211_cfg_get_channel,
        .start_radar_detection = ieee80211_start_radar_detection,
        .channel_switch = ieee80211_channel_switch,
index a310e33972de8881bf4dd71bdff36d55fa966226..6d537f03c0baa0450eefc5c22f496be085072cfd 100644 (file)
@@ -63,6 +63,20 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
        return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
 }
 
+static struct ieee80211_chanctx *
+ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local __maybe_unused = sdata->local;
+       struct ieee80211_chanctx_conf *conf;
+
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf)
+               return NULL;
+
+       return container_of(conf, struct ieee80211_chanctx, conf);
+}
+
 static const struct cfg80211_chan_def *
 ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *ctx,
@@ -160,6 +174,9 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
                return NULL;
 
        list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                       continue;
+
                if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
                        continue;
 
@@ -347,6 +364,9 @@ ieee80211_find_chanctx(struct ieee80211_local *local,
        list_for_each_entry(ctx, &local->chanctx_list, list) {
                const struct cfg80211_chan_def *compat;
 
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE)
+                       continue;
+
                if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
                        continue;
 
@@ -622,6 +642,7 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
+       bool use_reserved_switch = false;
 
        lockdep_assert_held(&local->chanctx_mtx);
 
@@ -632,12 +653,23 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
 
        ctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       if (sdata->reserved_chanctx)
+       if (sdata->reserved_chanctx) {
+               if (sdata->reserved_chanctx->replace_state ==
+                   IEEE80211_CHANCTX_REPLACES_OTHER &&
+                   ieee80211_chanctx_num_reserved(local,
+                                                  sdata->reserved_chanctx) > 1)
+                       use_reserved_switch = true;
+
                ieee80211_vif_unreserve_chanctx(sdata);
+       }
 
        ieee80211_assign_vif_chanctx(sdata, NULL);
        if (ieee80211_chanctx_refcount(local, ctx) == 0)
                ieee80211_free_chanctx(local, ctx);
+
+       /* Unreserving may ready an in-place reservation. */
+       if (use_reserved_switch)
+               ieee80211_vif_use_reserved_switch(local);
 }
 
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
@@ -787,70 +819,6 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
-static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
-                                         struct ieee80211_chanctx *ctx,
-                                         u32 *changed)
-{
-       struct ieee80211_local *local = sdata->local;
-       const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
-       u32 chanctx_changed = 0;
-
-       if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
-                                    IEEE80211_CHAN_DISABLED))
-               return -EINVAL;
-
-       if (ieee80211_chanctx_refcount(local, ctx) != 1)
-               return -EINVAL;
-
-       if (sdata->vif.bss_conf.chandef.width != chandef->width) {
-               chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
-               *changed |= BSS_CHANGED_BANDWIDTH;
-       }
-
-       sdata->vif.bss_conf.chandef = *chandef;
-       ctx->conf.def = *chandef;
-
-       chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
-       drv_change_chanctx(local, ctx, chanctx_changed);
-
-       ieee80211_recalc_chanctx_chantype(local, ctx);
-       ieee80211_recalc_smps_chanctx(local, ctx);
-       ieee80211_recalc_radar_chanctx(local, ctx);
-       ieee80211_recalc_chanctx_min_def(local, ctx);
-
-       return 0;
-}
-
-int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
-                                u32 *changed)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx_conf *conf;
-       struct ieee80211_chanctx *ctx;
-       int ret;
-
-       lockdep_assert_held(&local->mtx);
-
-       /* should never be called if not performing a channel switch. */
-       if (WARN_ON(!sdata->vif.csa_active))
-               return -EINVAL;
-
-       mutex_lock(&local->chanctx_mtx);
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                                        lockdep_is_held(&local->chanctx_mtx));
-       if (!conf) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       ctx = container_of(conf, struct ieee80211_chanctx, conf);
-
-       ret = __ieee80211_vif_change_channel(sdata, ctx, changed);
- out:
-       mutex_unlock(&local->chanctx_mtx);
-       return ret;
-}
-
 static void
 __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
                                      bool clear)
@@ -905,8 +873,25 @@ int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
        list_del(&sdata->reserved_chanctx_list);
        sdata->reserved_chanctx = NULL;
 
-       if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0)
-               ieee80211_free_chanctx(sdata->local, ctx);
+       if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) {
+               if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
+                       if (WARN_ON(!ctx->replace_ctx))
+                               return -EINVAL;
+
+                       WARN_ON(ctx->replace_ctx->replace_state !=
+                               IEEE80211_CHANCTX_WILL_BE_REPLACED);
+                       WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
+
+                       ctx->replace_ctx->replace_ctx = NULL;
+                       ctx->replace_ctx->replace_state =
+                                       IEEE80211_CHANCTX_REPLACE_NONE;
+
+                       list_del_rcu(&ctx->list);
+                       kfree_rcu(ctx, rcu_head);
+               } else {
+                       ieee80211_free_chanctx(sdata->local, ctx);
+               }
+       }
 
        return 0;
 }
@@ -917,40 +902,84 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
                                  bool radar_required)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx_conf *conf;
-       struct ieee80211_chanctx *new_ctx, *curr_ctx;
-       int ret = 0;
+       struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
 
-       mutex_lock(&local->chanctx_mtx);
-
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                                        lockdep_is_held(&local->chanctx_mtx));
-       if (!conf) {
-               ret = -EINVAL;
-               goto out;
-       }
+       lockdep_assert_held(&local->chanctx_mtx);
 
-       curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
+       curr_ctx = ieee80211_vif_get_chanctx(sdata);
+       if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx)
+               return -ENOTSUPP;
 
        new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
        if (!new_ctx) {
-               if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 &&
-                   (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
-                       /* if we're the only users of the chanctx and
-                        * the driver supports changing a running
-                        * context, reserve our current context
-                        */
-                       new_ctx = curr_ctx;
-               } else if (ieee80211_can_create_new_chanctx(local)) {
-                       /* create a new context and reserve it */
+               if (ieee80211_can_create_new_chanctx(local)) {
                        new_ctx = ieee80211_new_chanctx(local, chandef, mode);
-                       if (IS_ERR(new_ctx)) {
-                               ret = PTR_ERR(new_ctx);
-                               goto out;
-                       }
+                       if (IS_ERR(new_ctx))
+                               return PTR_ERR(new_ctx);
                } else {
-                       ret = -EBUSY;
-                       goto out;
+                       if (!curr_ctx ||
+                           (curr_ctx->replace_state ==
+                            IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
+                           !list_empty(&curr_ctx->reserved_vifs)) {
+                               /*
+                                * Another vif already requested this context
+                                * for a reservation. Find another one hoping
+                                * all vifs assigned to it will also switch
+                                * soon enough.
+                                *
+                                * TODO: This needs a little more work as some
+                                * cases (more than 2 chanctx capable devices)
+                                * may fail which could otherwise succeed
+                                * provided some channel context juggling was
+                                * performed.
+                                *
+                                * Consider ctx1..3, vif1..6, each ctx has 2
+                                * vifs. vif1 and vif2 from ctx1 request new
+                                * different chandefs starting 2 in-place
+                                * reserations with ctx4 and ctx5 replacing
+                                * ctx1 and ctx2 respectively. Next vif5 and
+                                * vif6 from ctx3 reserve ctx4. If vif3 and
+                                * vif4 remain on ctx2 as they are then this
+                                * fails unless `replace_ctx` from ctx5 is
+                                * replaced with ctx3.
+                                */
+                               list_for_each_entry(ctx, &local->chanctx_list,
+                                                   list) {
+                                       if (ctx->replace_state !=
+                                           IEEE80211_CHANCTX_REPLACE_NONE)
+                                               continue;
+
+                                       if (!list_empty(&ctx->reserved_vifs))
+                                               continue;
+
+                                       curr_ctx = ctx;
+                                       break;
+                               }
+                       }
+
+                       /*
+                        * If that's true then all available contexts already
+                        * have reservations and cannot be used.
+                        */
+                       if (!curr_ctx ||
+                           (curr_ctx->replace_state ==
+                            IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
+                           !list_empty(&curr_ctx->reserved_vifs))
+                               return -EBUSY;
+
+                       new_ctx = ieee80211_alloc_chanctx(local, chandef, mode);
+                       if (!new_ctx)
+                               return -ENOMEM;
+
+                       new_ctx->replace_ctx = curr_ctx;
+                       new_ctx->replace_state =
+                                       IEEE80211_CHANCTX_REPLACES_OTHER;
+
+                       curr_ctx->replace_ctx = new_ctx;
+                       curr_ctx->replace_state =
+                                       IEEE80211_CHANCTX_WILL_BE_REPLACED;
+
+                       list_add_rcu(&new_ctx->list, &local->chanctx_list);
                }
        }
 
@@ -958,82 +987,601 @@ int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
        sdata->reserved_chanctx = new_ctx;
        sdata->reserved_chandef = *chandef;
        sdata->reserved_radar_required = radar_required;
-out:
-       mutex_unlock(&local->chanctx_mtx);
-       return ret;
+       sdata->reserved_ready = false;
+
+       return 0;
 }
 
-int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
-                                      u32 *changed)
+static void
+ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_chanctx *ctx;
-       struct ieee80211_chanctx *old_ctx;
-       struct ieee80211_chanctx_conf *conf;
-       int ret;
-       u32 tmp_changed = *changed;
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_MESH_POINT:
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->csa_finalize_work);
+               break;
+       case NL80211_IFTYPE_STATION:
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &sdata->u.mgd.chswitch_work);
+               break;
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_P2P_DEVICE:
+       case NUM_NL80211_IFTYPES:
+               WARN_ON(1);
+               break;
+       }
+}
 
-       /* TODO: need to recheck if the chandef is usable etc.? */
+static int
+ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_vif_chanctx_switch vif_chsw[1] = {};
+       struct ieee80211_chanctx *old_ctx, *new_ctx;
+       const struct cfg80211_chan_def *chandef;
+       u32 changed = 0;
+       int err;
 
        lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
 
-       mutex_lock(&local->chanctx_mtx);
+       new_ctx = sdata->reserved_chanctx;
+       old_ctx = ieee80211_vif_get_chanctx(sdata);
 
-       ctx = sdata->reserved_chanctx;
-       if (WARN_ON(!ctx)) {
-               ret = -EINVAL;
-               goto out;
-       }
+       if (WARN_ON(!sdata->reserved_ready))
+               return -EBUSY;
+
+       if (WARN_ON(!new_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(!old_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(new_ctx->replace_state ==
+                   IEEE80211_CHANCTX_REPLACES_OTHER))
+               return -EINVAL;
+
+       chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
+                               &sdata->reserved_chandef);
+       if (WARN_ON(!chandef))
+               return -EINVAL;
+
+       vif_chsw[0].vif = &sdata->vif;
+       vif_chsw[0].old_ctx = &old_ctx->conf;
+       vif_chsw[0].new_ctx = &new_ctx->conf;
+
+       list_del(&sdata->reserved_chanctx_list);
+       sdata->reserved_chanctx = NULL;
+
+       err = drv_switch_vif_chanctx(local, vif_chsw, 1,
+                                    CHANCTX_SWMODE_REASSIGN_VIF);
+       if (err) {
+               if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+                       ieee80211_free_chanctx(local, new_ctx);
 
-       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
-                                        lockdep_is_held(&local->chanctx_mtx));
-       if (!conf) {
-               ret = -EINVAL;
                goto out;
        }
 
-       old_ctx = container_of(conf, struct ieee80211_chanctx, conf);
+       list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs);
+       rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf);
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+
+       if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
+               ieee80211_free_chanctx(local, old_ctx);
 
        if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
-               tmp_changed |= BSS_CHANGED_BANDWIDTH;
+               changed = BSS_CHANGED_BANDWIDTH;
 
        sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
 
-       /* unref our reservation */
-       sdata->reserved_chanctx = NULL;
-       sdata->radar_required = sdata->reserved_radar_required;
+       if (changed)
+               ieee80211_bss_info_change_notify(sdata, changed);
+
+out:
+       ieee80211_vif_chanctx_reservation_complete(sdata);
+       return err;
+}
+
+static int
+ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx *old_ctx, *new_ctx;
+       const struct cfg80211_chan_def *chandef;
+       int err;
+
+       old_ctx = ieee80211_vif_get_chanctx(sdata);
+       new_ctx = sdata->reserved_chanctx;
+
+       if (WARN_ON(!sdata->reserved_ready))
+               return -EINVAL;
+
+       if (WARN_ON(old_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(!new_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(new_ctx->replace_state ==
+                   IEEE80211_CHANCTX_REPLACES_OTHER))
+               return -EINVAL;
+
+       chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx,
+                               &sdata->reserved_chandef);
+       if (WARN_ON(!chandef))
+               return -EINVAL;
+
        list_del(&sdata->reserved_chanctx_list);
+       sdata->reserved_chanctx = NULL;
 
-       if (old_ctx == ctx) {
-               /* This is our own context, just change it */
-               ret = __ieee80211_vif_change_channel(sdata, old_ctx,
-                                                    &tmp_changed);
-               if (ret)
-                       goto out;
-       } else {
-               ret = ieee80211_assign_vif_chanctx(sdata, ctx);
-               if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
-                       ieee80211_free_chanctx(local, old_ctx);
-               if (ret) {
-                       /* if assign fails refcount stays the same */
-                       if (ieee80211_chanctx_refcount(local, ctx) == 0)
-                               ieee80211_free_chanctx(local, ctx);
+       err = ieee80211_assign_vif_chanctx(sdata, new_ctx);
+       if (err) {
+               if (ieee80211_chanctx_refcount(local, new_ctx) == 0)
+                       ieee80211_free_chanctx(local, new_ctx);
+
+               goto out;
+       }
+
+out:
+       ieee80211_vif_chanctx_reservation_complete(sdata);
+       return err;
+}
+
+static bool
+ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_chanctx *old_ctx, *new_ctx;
+
+       lockdep_assert_held(&sdata->local->chanctx_mtx);
+
+       new_ctx = sdata->reserved_chanctx;
+       old_ctx = ieee80211_vif_get_chanctx(sdata);
+
+       if (!old_ctx)
+               return false;
+
+       if (WARN_ON(!new_ctx))
+               return false;
+
+       if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
+               return false;
+
+       if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+               return false;
+
+       return true;
+}
+
+static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local,
+                                       struct ieee80211_chanctx *new_ctx)
+{
+       const struct cfg80211_chan_def *chandef;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL);
+       if (WARN_ON(!chandef))
+               return -EINVAL;
+
+       local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled;
+       local->_oper_chandef = *chandef;
+       ieee80211_hw_config(local, 0);
+
+       return 0;
+}
+
+static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
+                                     int n_vifs)
+{
+       struct ieee80211_vif_chanctx_switch *vif_chsw;
+       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_chanctx *ctx, *old_ctx;
+       int i, err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL);
+       if (!vif_chsw)
+               return -ENOMEM;
+
+       i = 0;
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (WARN_ON(!ctx->replace_ctx)) {
+                       err = -EINVAL;
                        goto out;
                }
 
-               if (sdata->vif.type == NL80211_IFTYPE_AP)
-                       __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
+               list_for_each_entry(sdata, &ctx->reserved_vifs,
+                                   reserved_chanctx_list) {
+                       if (!ieee80211_vif_has_in_place_reservation(
+                                       sdata))
+                               continue;
+
+                       old_ctx = ieee80211_vif_get_chanctx(sdata);
+                       vif_chsw[i].vif = &sdata->vif;
+                       vif_chsw[i].old_ctx = &old_ctx->conf;
+                       vif_chsw[i].new_ctx = &ctx->conf;
+
+                       i++;
+               }
        }
 
-       *changed = tmp_changed;
+       err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs,
+                                    CHANCTX_SWMODE_SWAP_CONTEXTS);
 
-       ieee80211_recalc_chanctx_chantype(local, ctx);
-       ieee80211_recalc_smps_chanctx(local, ctx);
-       ieee80211_recalc_radar_chanctx(local, ctx);
-       ieee80211_recalc_chanctx_min_def(local, ctx);
 out:
-       mutex_unlock(&local->chanctx_mtx);
-       return ret;
+       kfree(vif_chsw);
+       return err;
+}
+
+static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local)
+{
+       struct ieee80211_chanctx *ctx;
+       int err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (!list_empty(&ctx->replace_ctx->assigned_vifs))
+                       continue;
+
+               ieee80211_del_chanctx(local, ctx->replace_ctx);
+               err = ieee80211_add_chanctx(local, ctx);
+               if (err)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       WARN_ON(ieee80211_add_chanctx(local, ctx));
+       list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (!list_empty(&ctx->replace_ctx->assigned_vifs))
+                       continue;
+
+               ieee80211_del_chanctx(local, ctx);
+               WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx));
+       }
+
+       return err;
+}
+
+int
+ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata, *sdata_tmp;
+       struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx;
+       struct ieee80211_chanctx *new_ctx = NULL;
+       int i, err, n_assigned, n_reserved, n_ready;
+       int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       /*
+        * If there are 2 independent pairs of channel contexts performing
+        * cross-switch of their vifs this code will still wait until both are
+        * ready even though it could be possible to switch one before the
+        * other is ready.
+        *
+        * For practical reasons and code simplicity just do a single huge
+        * switch.
+        */
+
+       /*
+        * Verify if the reservation is still feasible.
+        *  - if it's not then disconnect
+        *  - if it is but not all vifs necessary are ready then defer
+        */
+
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (WARN_ON(!ctx->replace_ctx)) {
+                       err = -EINVAL;
+                       goto err;
+               }
+
+               if (!local->use_chanctx)
+                       new_ctx = ctx;
+
+               n_ctx++;
+
+               n_assigned = 0;
+               n_reserved = 0;
+               n_ready = 0;
+
+               list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs,
+                                   assigned_chanctx_list) {
+                       n_assigned++;
+                       if (sdata->reserved_chanctx) {
+                               n_reserved++;
+                               if (sdata->reserved_ready)
+                                       n_ready++;
+                       }
+               }
+
+               if (n_assigned != n_reserved) {
+                       if (n_ready == n_reserved) {
+                               wiphy_info(local->hw.wiphy,
+                                          "channel context reservation cannot be finalized because some interfaces aren't switching\n");
+                               err = -EBUSY;
+                               goto err;
+                       }
+
+                       return -EAGAIN;
+               }
+
+               ctx->conf.radar_enabled = false;
+               list_for_each_entry(sdata, &ctx->reserved_vifs,
+                                   reserved_chanctx_list) {
+                       if (ieee80211_vif_has_in_place_reservation(sdata) &&
+                           !sdata->reserved_ready)
+                               return -EAGAIN;
+
+                       old_ctx = ieee80211_vif_get_chanctx(sdata);
+                       if (old_ctx) {
+                               if (old_ctx->replace_state ==
+                                   IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                                       n_vifs_switch++;
+                               else
+                                       n_vifs_assign++;
+                       } else {
+                               n_vifs_ctxless++;
+                       }
+
+                       if (sdata->reserved_radar_required)
+                               ctx->conf.radar_enabled = true;
+               }
+       }
+
+       if (WARN_ON(n_ctx == 0) ||
+           WARN_ON(n_vifs_switch == 0 &&
+                   n_vifs_assign == 0 &&
+                   n_vifs_ctxless == 0) ||
+           WARN_ON(n_ctx > 1 && !local->use_chanctx) ||
+           WARN_ON(!new_ctx && !local->use_chanctx)) {
+               err = -EINVAL;
+               goto err;
+       }
+
+       /*
+        * All necessary vifs are ready. Perform the switch now depending on
+        * reservations and driver capabilities.
+        */
+
+       if (local->use_chanctx) {
+               if (n_vifs_switch > 0) {
+                       err = ieee80211_chsw_switch_vifs(local, n_vifs_switch);
+                       if (err)
+                               goto err;
+               }
+
+               if (n_vifs_assign > 0 || n_vifs_ctxless > 0) {
+                       err = ieee80211_chsw_switch_ctxs(local);
+                       if (err)
+                               goto err;
+               }
+       } else {
+               err = ieee80211_chsw_switch_hwconf(local, new_ctx);
+               if (err)
+                       goto err;
+       }
+
+       /*
+        * Update all structures, values and pointers to point to new channel
+        * context(s).
+        */
+
+       i = 0;
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               if (WARN_ON(!ctx->replace_ctx)) {
+                       err = -EINVAL;
+                       goto err;
+               }
+
+               list_for_each_entry(sdata, &ctx->reserved_vifs,
+                                   reserved_chanctx_list) {
+                       u32 changed = 0;
+
+                       if (!ieee80211_vif_has_in_place_reservation(sdata))
+                               continue;
+
+                       rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
+
+                       if (sdata->vif.type == NL80211_IFTYPE_AP)
+                               __ieee80211_vif_copy_chanctx_to_vlans(sdata,
+                                                                     false);
+
+                       sdata->radar_required = sdata->reserved_radar_required;
+
+                       if (sdata->vif.bss_conf.chandef.width !=
+                           sdata->reserved_chandef.width)
+                               changed = BSS_CHANGED_BANDWIDTH;
+
+                       sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+                       if (changed)
+                               ieee80211_bss_info_change_notify(sdata,
+                                                                changed);
+
+                       ieee80211_recalc_txpower(sdata);
+               }
+
+               ieee80211_recalc_chanctx_chantype(local, ctx);
+               ieee80211_recalc_smps_chanctx(local, ctx);
+               ieee80211_recalc_radar_chanctx(local, ctx);
+               ieee80211_recalc_chanctx_min_def(local, ctx);
+
+               list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
+                                        reserved_chanctx_list) {
+                       if (ieee80211_vif_get_chanctx(sdata) != ctx)
+                               continue;
+
+                       list_del(&sdata->reserved_chanctx_list);
+                       list_move(&sdata->assigned_chanctx_list,
+                                 &new_ctx->assigned_vifs);
+                       sdata->reserved_chanctx = NULL;
+
+                       ieee80211_vif_chanctx_reservation_complete(sdata);
+               }
+
+               /*
+                * This context might have been a dependency for an already
+                * ready re-assign reservation interface that was deferred. Do
+                * not propagate error to the caller though. The in-place
+                * reservation for originally requested interface has already
+                * succeeded at this point.
+                */
+               list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
+                                        reserved_chanctx_list) {
+                       if (WARN_ON(ieee80211_vif_has_in_place_reservation(
+                                       sdata)))
+                               continue;
+
+                       if (WARN_ON(sdata->reserved_chanctx != ctx))
+                               continue;
+
+                       if (!sdata->reserved_ready)
+                               continue;
+
+                       if (ieee80211_vif_get_chanctx(sdata))
+                               err = ieee80211_vif_use_reserved_reassign(
+                                               sdata);
+                       else
+                               err = ieee80211_vif_use_reserved_assign(sdata);
+
+                       if (err) {
+                               sdata_info(sdata,
+                                          "failed to finalize (re-)assign reservation (err=%d)\n",
+                                          err);
+                               ieee80211_vif_unreserve_chanctx(sdata);
+                               cfg80211_stop_iface(local->hw.wiphy,
+                                                   &sdata->wdev,
+                                                   GFP_KERNEL);
+                       }
+               }
+       }
+
+       /*
+        * Finally free old contexts
+        */
+
+       list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                       continue;
+
+               ctx->replace_ctx->replace_ctx = NULL;
+               ctx->replace_ctx->replace_state =
+                               IEEE80211_CHANCTX_REPLACE_NONE;
+
+               list_del_rcu(&ctx->list);
+               kfree_rcu(ctx, rcu_head);
+       }
+
+       return 0;
+
+err:
+       list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
+                       continue;
+
+               list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs,
+                                        reserved_chanctx_list) {
+                       ieee80211_vif_unreserve_chanctx(sdata);
+                       ieee80211_vif_chanctx_reservation_complete(sdata);
+               }
+       }
+
+       return err;
+}
+
+int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx *new_ctx;
+       struct ieee80211_chanctx *old_ctx;
+       int err;
+
+       lockdep_assert_held(&local->mtx);
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       new_ctx = sdata->reserved_chanctx;
+       old_ctx = ieee80211_vif_get_chanctx(sdata);
+
+       if (WARN_ON(!new_ctx))
+               return -EINVAL;
+
+       if (WARN_ON(new_ctx->replace_state ==
+                   IEEE80211_CHANCTX_WILL_BE_REPLACED))
+               return -EINVAL;
+
+       if (WARN_ON(sdata->reserved_ready))
+               return -EINVAL;
+
+       sdata->reserved_ready = true;
+
+       if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) {
+               if (old_ctx)
+                       err = ieee80211_vif_use_reserved_reassign(sdata);
+               else
+                       err = ieee80211_vif_use_reserved_assign(sdata);
+
+               if (err)
+                       return err;
+       }
+
+       /*
+        * In-place reservation may need to be finalized now either if:
+        *  a) sdata is taking part in the swapping itself and is the last one
+        *  b) sdata has switched with a re-assign reservation to an existing
+        *     context readying in-place switching of old_ctx
+        *
+        * In case of (b) do not propagate the error up because the requested
+        * sdata already switched successfully. Just spill an extra warning.
+        * The ieee80211_vif_use_reserved_switch() already stops all necessary
+        * interfaces upon failure.
+        */
+       if ((old_ctx &&
+            old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
+           new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) {
+               err = ieee80211_vif_use_reserved_switch(local);
+               if (err && err != -EAGAIN) {
+                       if (new_ctx->replace_state ==
+                           IEEE80211_CHANCTX_REPLACES_OTHER)
+                               return err;
+
+                       wiphy_info(local->hw.wiphy,
+                                  "depending in-place reservation failed (err=%d)\n",
+                                  err);
+               }
+       }
+
+       return 0;
 }
 
 int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
@@ -1043,6 +1591,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *ctx;
+       const struct cfg80211_chan_def *compat;
        int ret;
 
        if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
@@ -1069,11 +1618,33 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
        }
 
        ctx = container_of(conf, struct ieee80211_chanctx, conf);
-       if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
+
+       compat = cfg80211_chandef_compatible(&conf->def, chandef);
+       if (!compat) {
                ret = -EINVAL;
                goto out;
        }
 
+       switch (ctx->replace_state) {
+       case IEEE80211_CHANCTX_REPLACE_NONE:
+               if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               break;
+       case IEEE80211_CHANCTX_WILL_BE_REPLACED:
+               /* TODO: Perhaps the bandwith change could be treated as a
+                * reservation itself? */
+               ret = -EBUSY;
+               goto out;
+       case IEEE80211_CHANCTX_REPLACES_OTHER:
+               /* channel context that is going to replace another channel
+                * context doesn't really exist and shouldn't be assigned
+                * anywhere yet */
+               WARN_ON(1);
+               break;
+       }
+
        sdata->vif.bss_conf.chandef = *chandef;
 
        ieee80211_recalc_chanctx_chantype(local, ctx);
index 2ecb4deddb5df0ca74eb9630de15bbc9c789f274..3db96648b45a02c0e0235b724210a15ba32747b7 100644 (file)
@@ -124,7 +124,7 @@ static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf,
        long connected_time_secs;
        char buf[100];
        int res;
-       do_posix_clock_monotonic_gettime(&uptime);
+       ktime_get_ts(&uptime);
        connected_time_secs = uptime.tv_sec - sta->last_connected;
        time_to_tm(connected_time_secs, 0, &result);
        result.tm_year -= 70;
@@ -587,7 +587,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
        DEBUGFS_ADD_COUNTER(tx_filtered, tx_filtered_count);
        DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed);
        DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
-       DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
 
        if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
                debugfs_create_x32("driver_buffered_tids", 0400,
index bd782dcffcc7b81478277bcb2b5545dfef545da8..11423958116a3fc9e37c91684de3aade8fdef763 100644 (file)
@@ -314,7 +314,7 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
 
 static inline int drv_hw_scan(struct ieee80211_local *local,
                              struct ieee80211_sub_if_data *sdata,
-                             struct cfg80211_scan_request *req)
+                             struct ieee80211_scan_request *req)
 {
        int ret;
 
@@ -346,7 +346,7 @@ static inline int
 drv_sched_scan_start(struct ieee80211_local *local,
                     struct ieee80211_sub_if_data *sdata,
                     struct cfg80211_sched_scan_request *req,
-                    struct ieee80211_sched_scan_ies *ies)
+                    struct ieee80211_scan_ies *ies)
 {
        int ret;
 
@@ -970,6 +970,22 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline void
+drv_mgd_protect_tdls_discover(struct ieee80211_local *local,
+                             struct ieee80211_sub_if_data *sdata)
+{
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return;
+       WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION);
+
+       trace_drv_mgd_protect_tdls_discover(local, sdata);
+       if (local->ops->mgd_protect_tdls_discover)
+               local->ops->mgd_protect_tdls_discover(&local->hw, &sdata->vif);
+       trace_drv_return_void(local);
+}
+
 static inline int drv_add_chanctx(struct ieee80211_local *local,
                                  struct ieee80211_chanctx *ctx)
 {
diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c
new file mode 100644 (file)
index 0000000..ebfc809
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * mac80211 ethtool hooks for cfg80211
+ *
+ * Copied from cfg.c - originally
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2014      Intel Corporation (Author: Johannes Berg)
+ *
+ * This file is GPLv2 as found in COPYING.
+ */
+#include <linux/types.h>
+#include <net/cfg80211.h>
+#include "ieee80211_i.h"
+#include "sta_info.h"
+#include "driver-ops.h"
+
+static int ieee80211_set_ringparam(struct net_device *dev,
+                                  struct ethtool_ringparam *rp)
+{
+       struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy);
+
+       if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
+               return -EINVAL;
+
+       return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending);
+}
+
+static void ieee80211_get_ringparam(struct net_device *dev,
+                                   struct ethtool_ringparam *rp)
+{
+       struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy);
+
+       memset(rp, 0, sizeof(*rp));
+
+       drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending,
+                         &rp->rx_pending, &rp->rx_max_pending);
+}
+
+static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
+       "rx_packets", "rx_bytes",
+       "rx_duplicates", "rx_fragments", "rx_dropped",
+       "tx_packets", "tx_bytes", "tx_fragments",
+       "tx_filtered", "tx_retry_failed", "tx_retries",
+       "beacon_loss", "sta_state", "txrate", "rxrate", "signal",
+       "channel", "noise", "ch_time", "ch_time_busy",
+       "ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
+};
+#define STA_STATS_LEN  ARRAY_SIZE(ieee80211_gstrings_sta_stats)
+
+static int ieee80211_get_sset_count(struct net_device *dev, int sset)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int rv = 0;
+
+       if (sset == ETH_SS_STATS)
+               rv += STA_STATS_LEN;
+
+       rv += drv_get_et_sset_count(sdata, sset);
+
+       if (rv == 0)
+               return -EOPNOTSUPP;
+       return rv;
+}
+
+static void ieee80211_get_stats(struct net_device *dev,
+                               struct ethtool_stats *stats,
+                               u64 *data)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *channel;
+       struct sta_info *sta;
+       struct ieee80211_local *local = sdata->local;
+       struct station_info sinfo;
+       struct survey_info survey;
+       int i, q;
+#define STA_STATS_SURVEY_LEN 7
+
+       memset(data, 0, sizeof(u64) * STA_STATS_LEN);
+
+#define ADD_STA_STATS(sta)                             \
+       do {                                            \
+               data[i++] += sta->rx_packets;           \
+               data[i++] += sta->rx_bytes;             \
+               data[i++] += sta->num_duplicates;       \
+               data[i++] += sta->rx_fragments;         \
+               data[i++] += sta->rx_dropped;           \
+                                                       \
+               data[i++] += sinfo.tx_packets;          \
+               data[i++] += sinfo.tx_bytes;            \
+               data[i++] += sta->tx_fragments;         \
+               data[i++] += sta->tx_filtered_count;    \
+               data[i++] += sta->tx_retry_failed;      \
+               data[i++] += sta->tx_retry_count;       \
+               data[i++] += sta->beacon_loss_count;    \
+       } while (0)
+
+       /* For Managed stations, find the single station based on BSSID
+        * and use that.  For interface types, iterate through all available
+        * stations and add stats for any station that is assigned to this
+        * network device.
+        */
+
+       mutex_lock(&local->sta_mtx);
+
+       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+               sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid);
+
+               if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
+                       goto do_survey;
+
+               sinfo.filled = 0;
+               sta_set_sinfo(sta, &sinfo);
+
+               i = 0;
+               ADD_STA_STATS(sta);
+
+               data[i++] = sta->sta_state;
+
+
+               if (sinfo.filled & STATION_INFO_TX_BITRATE)
+                       data[i] = 100000 *
+                               cfg80211_calculate_bitrate(&sinfo.txrate);
+               i++;
+               if (sinfo.filled & STATION_INFO_RX_BITRATE)
+                       data[i] = 100000 *
+                               cfg80211_calculate_bitrate(&sinfo.rxrate);
+               i++;
+
+               if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
+                       data[i] = (u8)sinfo.signal_avg;
+               i++;
+       } else {
+               list_for_each_entry(sta, &local->sta_list, list) {
+                       /* Make sure this station belongs to the proper dev */
+                       if (sta->sdata->dev != dev)
+                               continue;
+
+                       sinfo.filled = 0;
+                       sta_set_sinfo(sta, &sinfo);
+                       i = 0;
+                       ADD_STA_STATS(sta);
+               }
+       }
+
+do_survey:
+       i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
+       /* Get survey stats for current channel */
+       survey.filled = 0;
+
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (chanctx_conf)
+               channel = chanctx_conf->def.chan;
+       else
+               channel = NULL;
+       rcu_read_unlock();
+
+       if (channel) {
+               q = 0;
+               do {
+                       survey.filled = 0;
+                       if (drv_get_survey(local, q, &survey) != 0) {
+                               survey.filled = 0;
+                               break;
+                       }
+                       q++;
+               } while (channel != survey.channel);
+       }
+
+       if (survey.filled)
+               data[i++] = survey.channel->center_freq;
+       else
+               data[i++] = 0;
+       if (survey.filled & SURVEY_INFO_NOISE_DBM)
+               data[i++] = (u8)survey.noise;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
+               data[i++] = survey.channel_time;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
+               data[i++] = survey.channel_time_busy;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
+               data[i++] = survey.channel_time_ext_busy;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
+               data[i++] = survey.channel_time_rx;
+       else
+               data[i++] = -1LL;
+       if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
+               data[i++] = survey.channel_time_tx;
+       else
+               data[i++] = -1LL;
+
+       mutex_unlock(&local->sta_mtx);
+
+       if (WARN_ON(i != STA_STATS_LEN))
+               return;
+
+       drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
+}
+
+static void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int sz_sta_stats = 0;
+
+       if (sset == ETH_SS_STATS) {
+               sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
+               memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
+       }
+       drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
+}
+
+static int ieee80211_get_regs_len(struct net_device *dev)
+{
+       return 0;
+}
+
+static void ieee80211_get_regs(struct net_device *dev,
+                              struct ethtool_regs *regs,
+                              void *data)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+
+       regs->version = wdev->wiphy->hw_version;
+       regs->len = 0;
+}
+
+const struct ethtool_ops ieee80211_ethtool_ops = {
+       .get_drvinfo = cfg80211_get_drvinfo,
+       .get_regs_len = ieee80211_get_regs_len,
+       .get_regs = ieee80211_get_regs,
+       .get_link = ethtool_op_get_link,
+       .get_ringparam = ieee80211_get_ringparam,
+       .set_ringparam = ieee80211_set_ringparam,
+       .get_strings = ieee80211_get_strings,
+       .get_ethtool_stats = ieee80211_get_stats,
+       .get_sset_count = ieee80211_get_sset_count,
+};
index 15702ff64a4c89fb89541f620b9da7dcccc2a7bd..ff630be2ca750182fabfce3ea363a44cd24d7928 100644 (file)
@@ -150,13 +150,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
 
        /*
         * If user has specified capability over-rides, take care
-        * of that if the station we're setting up is the AP that
+        * of that if the station we're setting up is the AP or TDLS peer that
         * we advertised a restricted capability set to. Override
         * our own capabilities and then use those below.
         */
-       if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
-            sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
-           !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+       if (sdata->vif.type == NL80211_IFTYPE_STATION ||
+           sdata->vif.type == NL80211_IFTYPE_ADHOC)
                ieee80211_apply_htcap_overrides(sdata, &own_cap);
 
        /*
@@ -228,6 +227,9 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
        if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
                ht_cap.mcs.rx_mask[32/8] |= 1;
 
+       /* set Rx highest rate */
+       ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
+
  apply:
        changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));
 
index 18ee0a256b1e300d5fc4805d110af12ca1a77308..9713dc54ea4bb385abf1d48ae891ff5fceb7440f 100644 (file)
@@ -143,7 +143,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                *pos++ = csa_settings->block_tx ? 1 : 0;
                *pos++ = ieee80211_frequency_to_channel(
                                csa_settings->chandef.chan->center_freq);
-               sdata->csa_counter_offset_beacon[0] = (pos - presp->head);
+               presp->csa_counter_offsets[0] = (pos - presp->head);
                *pos++ = csa_settings->count;
        }
 
@@ -189,17 +189,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
                                                 chandef, 0);
        }
 
-       if (local->hw.queues >= IEEE80211_NUM_ACS) {
-               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
-               *pos++ = 7; /* len */
-               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-               *pos++ = 0x50;
-               *pos++ = 0xf2;
-               *pos++ = 2; /* WME */
-               *pos++ = 0; /* WME info */
-               *pos++ = 1; /* WME ver */
-               *pos++ = 0; /* U-APSD no in use */
-       }
+       if (local->hw.queues >= IEEE80211_NUM_ACS)
+               pos = ieee80211_add_wmm_info_ie(pos, 0); /* U-APSD not in use */
 
        presp->head_len = pos - presp->head;
        if (WARN_ON(presp->head_len > frame_len))
index ac9836e0aab335ddf75295b5ad020d77e0e3dd0a..ef7a089ac54647eb763b8bbb0793bef98454b48d 100644 (file)
@@ -229,16 +229,29 @@ struct ieee80211_rx_data {
        u16 tkip_iv16;
 };
 
+struct ieee80211_csa_settings {
+       const u16 *counter_offsets_beacon;
+       const u16 *counter_offsets_presp;
+
+       int n_counter_offsets_beacon;
+       int n_counter_offsets_presp;
+
+       u8 count;
+};
+
 struct beacon_data {
        u8 *head, *tail;
        int head_len, tail_len;
        struct ieee80211_meshconf_ie *meshconf;
+       u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM];
+       u8 csa_current_counter;
        struct rcu_head rcu_head;
 };
 
 struct probe_resp {
        struct rcu_head rcu_head;
        int len;
+       u16 csa_counter_offsets[IEEE80211_MAX_CSA_COUNTERS_NUM];
        u8 data[0];
 };
 
@@ -332,7 +345,6 @@ enum ieee80211_sta_flags {
        IEEE80211_STA_CONNECTION_POLL   = BIT(1),
        IEEE80211_STA_CONTROL_PORT      = BIT(2),
        IEEE80211_STA_DISABLE_HT        = BIT(4),
-       IEEE80211_STA_CSA_RECEIVED      = BIT(5),
        IEEE80211_STA_MFP_ENABLED       = BIT(6),
        IEEE80211_STA_UAPSD_ENABLED     = BIT(7),
        IEEE80211_STA_NULLFUNC_ACKED    = BIT(8),
@@ -490,6 +502,9 @@ struct ieee80211_if_managed {
        struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
        struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
        struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
+
+       u8 tdls_peer[ETH_ALEN] __aligned(2);
+       struct delayed_work tdls_peer_del_work;
 };
 
 struct ieee80211_if_ibss {
@@ -688,6 +703,24 @@ enum ieee80211_chanctx_mode {
        IEEE80211_CHANCTX_EXCLUSIVE
 };
 
+/**
+ * enum ieee80211_chanctx_replace_state - channel context replacement state
+ *
+ * This is used for channel context in-place reservations that require channel
+ * context switch/swap.
+ *
+ * @IEEE80211_CHANCTX_REPLACE_NONE: no replacement is taking place
+ * @IEEE80211_CHANCTX_WILL_BE_REPLACED: this channel context will be replaced
+ *     by a (not yet registered) channel context pointed by %replace_ctx.
+ * @IEEE80211_CHANCTX_REPLACES_OTHER: this (not yet registered) channel context
+ *     replaces an existing channel context pointed to by %replace_ctx.
+ */
+enum ieee80211_chanctx_replace_state {
+       IEEE80211_CHANCTX_REPLACE_NONE,
+       IEEE80211_CHANCTX_WILL_BE_REPLACED,
+       IEEE80211_CHANCTX_REPLACES_OTHER,
+};
+
 struct ieee80211_chanctx {
        struct list_head list;
        struct rcu_head rcu_head;
@@ -695,6 +728,9 @@ struct ieee80211_chanctx {
        struct list_head assigned_vifs;
        struct list_head reserved_vifs;
 
+       enum ieee80211_chanctx_replace_state replace_state;
+       struct ieee80211_chanctx *replace_ctx;
+
        enum ieee80211_chanctx_mode mode;
        bool driver_present;
 
@@ -754,9 +790,6 @@ struct ieee80211_sub_if_data {
        struct mac80211_qos_map __rcu *qos_map;
 
        struct work_struct csa_finalize_work;
-       u16 csa_counter_offset_beacon[IEEE80211_MAX_CSA_COUNTERS_NUM];
-       u16 csa_counter_offset_presp[IEEE80211_MAX_CSA_COUNTERS_NUM];
-       bool csa_radar_required;
        bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
        struct cfg80211_chan_def csa_chandef;
 
@@ -767,7 +800,7 @@ struct ieee80211_sub_if_data {
        struct ieee80211_chanctx *reserved_chanctx;
        struct cfg80211_chan_def reserved_chandef;
        bool reserved_radar_required;
-       u8 csa_current_counter;
+       bool reserved_ready;
 
        /* used to reconfigure hardware SM PS */
        struct work_struct recalc_smps;
@@ -892,10 +925,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif)
        return shift;
 }
 
+struct ieee80211_rx_agg {
+       u8 addr[ETH_ALEN];
+       u16 tid;
+};
+
 enum sdata_queue_type {
        IEEE80211_SDATA_QUEUE_TYPE_FRAME        = 0,
        IEEE80211_SDATA_QUEUE_AGG_START         = 1,
        IEEE80211_SDATA_QUEUE_AGG_STOP          = 2,
+       IEEE80211_SDATA_QUEUE_RX_AGG_START      = 3,
+       IEEE80211_SDATA_QUEUE_RX_AGG_STOP       = 4,
 };
 
 enum {
@@ -912,6 +952,9 @@ enum queue_stop_reason {
        IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
        IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
        IEEE80211_QUEUE_STOP_REASON_FLUSH,
+       IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
+
+       IEEE80211_QUEUE_STOP_REASONS,
 };
 
 #ifdef CONFIG_MAC80211_LEDS
@@ -1008,6 +1051,7 @@ struct ieee80211_local {
        struct workqueue_struct *workqueue;
 
        unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
+       int q_stop_reasons[IEEE80211_MAX_QUEUES][IEEE80211_QUEUE_STOP_REASONS];
        /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
        spinlock_t queue_stop_reason_lock;
 
@@ -1135,7 +1179,8 @@ struct ieee80211_local {
        unsigned long scanning;
        struct cfg80211_ssid scan_ssid;
        struct cfg80211_scan_request *int_scan_req;
-       struct cfg80211_scan_request *scan_req, *hw_scan_req;
+       struct cfg80211_scan_request *scan_req;
+       struct ieee80211_scan_request *hw_scan_req;
        struct cfg80211_chan_def scan_chandef;
        enum ieee80211_band hw_scan_band;
        int scan_channel_idx;
@@ -1476,7 +1521,6 @@ void ieee80211_sw_roc_work(struct work_struct *work);
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
 
 /* channel switch handling */
-bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local);
 void ieee80211_csa_finalize_work(struct work_struct *work);
 int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
                             struct cfg80211_csa_settings *params);
@@ -1540,6 +1584,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                     u16 initiator, u16 reason, bool stop);
 void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
                                    u16 initiator, u16 reason, bool stop);
+void __ieee80211_start_rx_ba_session(struct sta_info *sta,
+                                    u8 dialog_token, u16 timeout,
+                                    u16 start_seq_num, u16 ba_policy, u16 tid,
+                                    u16 buf_size, bool tx);
 void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
                                         enum ieee80211_agg_stop_reason reason);
 void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
@@ -1692,6 +1740,21 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len,
        ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
 }
 
+static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
+{
+       struct sk_buff *tail = skb_peek_tail(frames);
+       struct ieee80211_rx_status *status;
+
+       if (!tail)
+               return false;
+
+       status = IEEE80211_SKB_RXCB(tail);
+       if (status->flag & RX_FLAG_AMSDU_MORE)
+               return false;
+
+       return true;
+}
+
 void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_timer(unsigned long data);
@@ -1705,14 +1768,24 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
-                                    enum queue_stop_reason reason);
+                                    enum queue_stop_reason reason,
+                                    bool refcounted);
+void ieee80211_stop_vif_queues(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata,
+                              enum queue_stop_reason reason);
+void ieee80211_wake_vif_queues(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata,
+                              enum queue_stop_reason reason);
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
-                                    enum queue_stop_reason reason);
+                                    enum queue_stop_reason reason,
+                                    bool refcounted);
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
-                                   enum queue_stop_reason reason);
+                                   enum queue_stop_reason reason,
+                                   bool refcounted);
 void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
-                                   enum queue_stop_reason reason);
+                                   enum queue_stop_reason reason,
+                                   bool refcounted);
 void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue);
 void ieee80211_add_pending_skb(struct ieee80211_local *local,
                               struct sk_buff *skb);
@@ -1730,8 +1803,10 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
                                    const u8 *bssid, u16 stype, u16 reason,
                                    bool send_frame, u8 *frame_buf);
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
-                            size_t buffer_len, const u8 *ie, size_t ie_len,
-                            enum ieee80211_band band, u32 rate_mask,
+                            size_t buffer_len,
+                            struct ieee80211_scan_ies *ie_desc,
+                            const u8 *ie, size_t ie_len,
+                            u8 bands_used, u32 *rate_masks,
                             struct cfg80211_chan_def *chandef);
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
@@ -1774,6 +1849,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
 int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
                                struct sk_buff *skb, bool need_basic,
                                enum ieee80211_band band);
+u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo);
 
 /* channel management */
 void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
@@ -1791,18 +1867,14 @@ ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
                              enum ieee80211_chanctx_mode mode,
                              bool radar_required);
 int __must_check
-ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
-                                  u32 *changed);
+ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata);
 int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata);
+int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local);
 
 int __must_check
 ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
                               const struct cfg80211_chan_def *chandef,
                               u32 *changed);
-/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
-int __must_check
-ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
-                            u32 *changed);
 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
 void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
@@ -1842,11 +1914,14 @@ int ieee80211_max_num_channels(struct ieee80211_local *local);
 int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
                        const u8 *peer, u8 action_code, u8 dialog_token,
                        u16 status_code, u32 peer_capability,
-                       const u8 *extra_ies, size_t extra_ies_len);
+                       bool initiator, const u8 *extra_ies,
+                       size_t extra_ies_len);
 int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                        const u8 *peer, enum nl80211_tdls_operation oper);
 
 
+extern const struct ethtool_ops ieee80211_ethtool_ops;
+
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
 #else
@@ -1854,3 +1929,4 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
 #endif
 
 #endif /* IEEE80211_I_H */
+void ieee80211_tdls_peer_del_work(struct work_struct *wk);
index 388b863e821c6beedbcdb01a602c0d18db701d2f..29be8854a027556310482298c152894b34cf8d2b 100644 (file)
@@ -841,10 +841,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        sdata_lock(sdata);
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
        mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 
@@ -1139,6 +1140,7 @@ static void ieee80211_iface_work(struct work_struct *work)
        struct sk_buff *skb;
        struct sta_info *sta;
        struct ieee80211_ra_tid *ra_tid;
+       struct ieee80211_rx_agg *rx_agg;
 
        if (!ieee80211_sdata_running(sdata))
                return;
@@ -1166,6 +1168,34 @@ static void ieee80211_iface_work(struct work_struct *work)
                        ra_tid = (void *)&skb->cb;
                        ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra,
                                                ra_tid->tid);
+               } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) {
+                       rx_agg = (void *)&skb->cb;
+                       mutex_lock(&local->sta_mtx);
+                       sta = sta_info_get_bss(sdata, rx_agg->addr);
+                       if (sta) {
+                               u16 last_seq;
+
+                               last_seq = le16_to_cpu(
+                                       sta->last_seq_ctrl[rx_agg->tid]);
+
+                               __ieee80211_start_rx_ba_session(sta,
+                                               0, 0,
+                                               ieee80211_sn_inc(last_seq),
+                                               1, rx_agg->tid,
+                                               IEEE80211_MAX_AMPDU_BUF,
+                                               false);
+                       }
+                       mutex_unlock(&local->sta_mtx);
+               } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) {
+                       rx_agg = (void *)&skb->cb;
+                       mutex_lock(&local->sta_mtx);
+                       sta = sta_info_get_bss(sdata, rx_agg->addr);
+                       if (sta)
+                               __ieee80211_stop_rx_ba_session(sta,
+                                                       rx_agg->tid,
+                                                       WLAN_BACK_RECIPIENT, 0,
+                                                       false);
+                       mutex_unlock(&local->sta_mtx);
                } else if (ieee80211_is_action(mgmt->frame_control) &&
                           mgmt->u.action.category == WLAN_CATEGORY_BACK) {
                        int len = skb->len;
@@ -1705,6 +1735,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 
                ndev->features |= local->hw.netdev_features;
 
+               netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops);
+
                ret = register_netdevice(ndev);
                if (ret) {
                        free_netdev(ndev);
index 16d97f044a202e61020f3ef563941e4c9adace0a..d808cff8015374ddfb927d5695a3d3973bcc0428 100644 (file)
@@ -482,9 +482,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
        int idx, ret;
        bool pairwise;
 
-       if (WARN_ON(!sdata || !key))
-               return -EINVAL;
-
        pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
        idx = key->conf.keyidx;
        key->local = sdata->local;
index d17c26d6e369f8db71061f3c73f4f27196ed9e3d..e0ab4320a078528b27e101c17bfd70ad422ac4fb 100644 (file)
@@ -272,7 +272,8 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
 
        /* use this reason, ieee80211_reconfig will unblock it */
        ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+                                       false);
 
        /*
         * Stop all Rx during the reconfig. We don't want state changes
@@ -1187,18 +1188,12 @@ static int __init ieee80211_init(void)
        if (ret)
                goto err_minstrel;
 
-       ret = rc80211_pid_init();
-       if (ret)
-               goto err_pid;
-
        ret = ieee80211_iface_init();
        if (ret)
                goto err_netdev;
 
        return 0;
  err_netdev:
-       rc80211_pid_exit();
- err_pid:
        rc80211_minstrel_ht_exit();
  err_minstrel:
        rc80211_minstrel_exit();
@@ -1208,7 +1203,6 @@ static int __init ieee80211_init(void)
 
 static void __exit ieee80211_exit(void)
 {
-       rc80211_pid_exit();
        rc80211_minstrel_ht_exit();
        rc80211_minstrel_exit();
 
index 6495a3f0428dae6a93bafea04da26b7390f45599..e9f99c1e3fad5905682a61f978d9897833426fd0 100644 (file)
@@ -679,7 +679,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
                *pos++ = 0x0;
                *pos++ = ieee80211_frequency_to_channel(
                                csa->settings.chandef.chan->center_freq);
-               sdata->csa_counter_offset_beacon[0] = hdr_len + 6;
+               bcn->csa_counter_offsets[0] = hdr_len + 6;
                *pos++ = csa->settings.count;
                *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
                *pos++ = 6;
@@ -1122,7 +1122,7 @@ static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
        mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
 
        /* offset_ttl is based on whether the secondary channel
-        * offset is available or not. Substract 1 from the mesh TTL
+        * offset is available or not. Subtract 1 from the mesh TTL
         * and disable the initiator flag before forwarding.
         */
        offset_ttl = (len < 42) ? 7 : 10;
index e8f60aa2e848b16982993f134fc63511186542af..63b874101b2763d5997dc561073e96807596c11a 100644 (file)
@@ -551,11 +551,30 @@ static void mesh_plink_timer(unsigned long data)
                return;
 
        spin_lock_bh(&sta->lock);
-       if (sta->ignore_plink_timer) {
-               sta->ignore_plink_timer = false;
+
+       /* If a timer fires just before a state transition on another CPU,
+        * we may have already extended the timeout and changed state by the
+        * time we've acquired the lock and arrived  here.  In that case,
+        * skip this timer and wait for the new one.
+        */
+       if (time_before(jiffies, sta->plink_timer.expires)) {
+               mpl_dbg(sta->sdata,
+                       "Ignoring timer for %pM in state %s (timer adjusted)",
+                       sta->sta.addr, mplstates[sta->plink_state]);
                spin_unlock_bh(&sta->lock);
                return;
        }
+
+       /* del_timer() and handler may race when entering these states */
+       if (sta->plink_state == NL80211_PLINK_LISTEN ||
+           sta->plink_state == NL80211_PLINK_ESTAB) {
+               mpl_dbg(sta->sdata,
+                       "Ignoring timer for %pM in state %s (timer deleted)",
+                       sta->sta.addr, mplstates[sta->plink_state]);
+               spin_unlock_bh(&sta->lock);
+               return;
+       }
+
        mpl_dbg(sta->sdata,
                "Mesh plink timer for %pM fired on state %s\n",
                sta->sta.addr, mplstates[sta->plink_state]);
@@ -773,9 +792,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
                        break;
                case CNF_ACPT:
                        sta->plink_state = NL80211_PLINK_CNF_RCVD;
-                       if (!mod_plink_timer(sta,
-                                            mshcfg->dot11MeshConfirmTimeout))
-                               sta->ignore_plink_timer = true;
+                       mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
                        break;
                default:
                        break;
@@ -834,8 +851,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
        case NL80211_PLINK_HOLDING:
                switch (event) {
                case CLS_ACPT:
-                       if (del_timer(&sta->plink_timer))
-                               sta->ignore_plink_timer = 1;
+                       del_timer(&sta->plink_timer);
                        mesh_plink_fsm_restart(sta);
                        break;
                case OPN_ACPT:
index 3345401be1b3c26744cb2ab6e384672a0cab0d6b..31a8afaf73323bc09fb99f8e960b17c3fa2e31fd 100644 (file)
@@ -830,16 +830,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
                        qos_info = 0;
                }
 
-               pos = skb_put(skb, 9);
-               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
-               *pos++ = 7; /* len */
-               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
-               *pos++ = 0x50;
-               *pos++ = 0xf2;
-               *pos++ = 2; /* WME */
-               *pos++ = 0; /* WME info */
-               *pos++ = 1; /* WME ver */
-               *pos++ = qos_info;
+               pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
        }
 
        /* add any remaining custom (i.e. vendor specific here) IEs */
@@ -940,58 +931,77 @@ static void ieee80211_chswitch_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       u32 changed = 0;
        int ret;
 
        if (!ieee80211_sdata_running(sdata))
                return;
 
        sdata_lock(sdata);
+       mutex_lock(&local->mtx);
+       mutex_lock(&local->chanctx_mtx);
+
        if (!ifmgd->associated)
                goto out;
 
-       mutex_lock(&local->mtx);
-       ret = ieee80211_vif_change_channel(sdata, &changed);
-       mutex_unlock(&local->mtx);
-       if (ret) {
+       if (!sdata->vif.csa_active)
+               goto out;
+
+       /*
+        * using reservation isn't immediate as it may be deferred until later
+        * with multi-vif. once reservation is complete it will re-schedule the
+        * work with no reserved_chanctx so verify chandef to check if it
+        * completed successfully
+        */
+
+       if (sdata->reserved_chanctx) {
+               /*
+                * with multi-vif csa driver may call ieee80211_csa_finish()
+                * many times while waiting for other interfaces to use their
+                * reservations
+                */
+               if (sdata->reserved_ready)
+                       goto out;
+
+               ret = ieee80211_vif_use_reserved_context(sdata);
+               if (ret) {
+                       sdata_info(sdata,
+                                  "failed to use reserved channel context, disconnecting (err=%d)\n",
+                                  ret);
+                       ieee80211_queue_work(&sdata->local->hw,
+                                            &ifmgd->csa_connection_drop_work);
+                       goto out;
+               }
+
+               goto out;
+       }
+
+       if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
+                                       &sdata->csa_chandef)) {
                sdata_info(sdata,
-                          "vif channel switch failed, disconnecting\n");
+                          "failed to finalize channel switch, disconnecting\n");
                ieee80211_queue_work(&sdata->local->hw,
                                     &ifmgd->csa_connection_drop_work);
                goto out;
        }
 
-       if (!local->use_chanctx) {
-               local->_oper_chandef = sdata->csa_chandef;
-               /* Call "hw_config" only if doing sw channel switch.
-                * Otherwise update the channel directly
-                */
-               if (!local->ops->channel_switch)
-                       ieee80211_hw_config(local, 0);
-               else
-                       local->hw.conf.chandef = local->_oper_chandef;
-       }
-
        /* XXX: shouldn't really modify cfg80211-owned data! */
        ifmgd->associated->channel = sdata->csa_chandef.chan;
 
-       ieee80211_bss_info_change_notify(sdata, changed);
-
-       mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       /* XXX: wait for a beacon first? */
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
-       mutex_unlock(&local->mtx);
 
-       ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+       /* XXX: wait for a beacon first? */
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
 
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_sta_reset_conn_monitor(sdata);
 
 out:
+       mutex_unlock(&local->chanctx_mtx);
+       mutex_unlock(&local->mtx);
        sdata_unlock(sdata);
 }
 
@@ -1028,6 +1038,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct cfg80211_bss *cbss = ifmgd->associated;
+       struct ieee80211_chanctx_conf *conf;
        struct ieee80211_chanctx *chanctx;
        enum ieee80211_band current_band;
        struct ieee80211_csa_ie csa_ie;
@@ -1042,7 +1053,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
 
        /* disregard subsequent announcements if we are already processing */
-       if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
+       if (sdata->vif.csa_active)
                return;
 
        current_band = cbss->channel->band;
@@ -1069,9 +1080,22 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
-       ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
-
+       mutex_lock(&local->mtx);
        mutex_lock(&local->chanctx_mtx);
+       conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+                                        lockdep_is_held(&local->chanctx_mtx));
+       if (!conf) {
+               sdata_info(sdata,
+                          "no channel context assigned to vif?, disconnecting\n");
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               mutex_unlock(&local->chanctx_mtx);
+               mutex_unlock(&local->mtx);
+               return;
+       }
+
+       chanctx = container_of(conf, struct ieee80211_chanctx, conf);
+
        if (local->use_chanctx) {
                u32 num_chanctx = 0;
                list_for_each_entry(chanctx, &local->chanctx_list, list)
@@ -1084,38 +1108,32 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                        ieee80211_queue_work(&local->hw,
                                             &ifmgd->csa_connection_drop_work);
                        mutex_unlock(&local->chanctx_mtx);
+                       mutex_unlock(&local->mtx);
                        return;
                }
        }
 
-       if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
-               ieee80211_queue_work(&local->hw,
-                                    &ifmgd->csa_connection_drop_work);
-               mutex_unlock(&local->chanctx_mtx);
-               return;
-       }
-       chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
-                              struct ieee80211_chanctx, conf);
-       if (ieee80211_chanctx_refcount(local, chanctx) > 1) {
+       res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
+                                           chanctx->mode, false);
+       if (res) {
                sdata_info(sdata,
-                          "channel switch with multiple interfaces on the same channel, disconnecting\n");
+                          "failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
+                          res);
                ieee80211_queue_work(&local->hw,
                                     &ifmgd->csa_connection_drop_work);
                mutex_unlock(&local->chanctx_mtx);
+               mutex_unlock(&local->mtx);
                return;
        }
        mutex_unlock(&local->chanctx_mtx);
 
-       sdata->csa_chandef = csa_ie.chandef;
-
-       mutex_lock(&local->mtx);
        sdata->vif.csa_active = true;
+       sdata->csa_chandef = csa_ie.chandef;
        sdata->csa_block_tx = csa_ie.mode;
 
        if (sdata->csa_block_tx)
-               ieee80211_stop_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+               ieee80211_stop_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
        mutex_unlock(&local->mtx);
 
        if (local->ops->channel_switch) {
@@ -1385,7 +1403,8 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
 
        ieee80211_wake_queues_by_reason(&local->hw,
                                        IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_PS);
+                                       IEEE80211_QUEUE_STOP_REASON_PS,
+                                       false);
 }
 
 void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
@@ -1830,10 +1849,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ieee80211_vif_release_channel(sdata);
 
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
        mutex_unlock(&local->mtx);
 
        sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
@@ -2075,14 +2095,13 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
                               true, frame_buf);
-       ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
-
        mutex_lock(&local->mtx);
        sdata->vif.csa_active = false;
-       if (!ieee80211_csa_needs_block_tx(local))
-               ieee80211_wake_queues_by_reason(&local->hw,
-                                       IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       if (sdata->csa_block_tx) {
+               ieee80211_wake_vif_queues(local, sdata,
+                                         IEEE80211_QUEUE_STOP_REASON_CSA);
+               sdata->csa_block_tx = false;
+       }
        mutex_unlock(&local->mtx);
 
        cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
@@ -3688,6 +3707,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        INIT_WORK(&ifmgd->csa_connection_drop_work,
                  ieee80211_csa_connection_drop_work);
        INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
+       INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
+                         ieee80211_tdls_peer_del_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -4551,6 +4572,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
        cancel_work_sync(&ifmgd->request_smps_work);
        cancel_work_sync(&ifmgd->csa_connection_drop_work);
        cancel_work_sync(&ifmgd->chswitch_work);
+       cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
 
        sdata_lock(sdata);
        if (ifmgd->assoc_data) {
index 7a17decd27f91af8646da20b9ab75fc3e303e3c4..ff20b2ebdb3044207087930d76400421d5fda213 100644 (file)
@@ -119,7 +119,8 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
         * before sending nullfunc to enable powersave at the AP.
         */
        ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
+                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
+                                       false);
        ieee80211_flush_queues(local, NULL);
 
        mutex_lock(&local->iflist_mtx);
@@ -182,7 +183,8 @@ void ieee80211_offchannel_return(struct ieee80211_local *local)
        mutex_unlock(&local->iflist_mtx);
 
        ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL);
+                                       IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
+                                       false);
 }
 
 void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
index d478b880a0afd676dae699d5b5541c150a7e819f..4c5192e0d66c7d5ae99913c2e9f2e27ff1626d71 100644 (file)
@@ -35,7 +35,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 
        ieee80211_stop_queues_by_reason(hw,
                                        IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+                                       false);
 
        /* flush out all packets */
        synchronize_net();
@@ -74,7 +75,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                        }
                        ieee80211_wake_queues_by_reason(hw,
                                        IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+                                       false);
                        return err;
                } else if (err > 0) {
                        WARN_ON(err != 1);
index 9aa2a1190a86353a25deca879018b2750bdefad9..18babe30283212c18ddb113f3295d211a310d014 100644 (file)
@@ -143,19 +143,6 @@ void rate_control_deinitialize(struct ieee80211_local *local);
 
 
 /* Rate control algorithms */
-#ifdef CONFIG_MAC80211_RC_PID
-int rc80211_pid_init(void);
-void rc80211_pid_exit(void);
-#else
-static inline int rc80211_pid_init(void)
-{
-       return 0;
-}
-static inline void rc80211_pid_exit(void)
-{
-}
-#endif
-
 #ifdef CONFIG_MAC80211_RC_MINSTREL
 int rc80211_minstrel_init(void);
 void rc80211_minstrel_exit(void);
diff --git a/net/mac80211/rc80211_pid.h b/net/mac80211/rc80211_pid.h
deleted file mode 100644 (file)
index 19111c7..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
- * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it>
- *
- * 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.
- */
-
-#ifndef RC80211_PID_H
-#define RC80211_PID_H
-
-/* Sampling period for measuring percentage of failed frames in ms. */
-#define RC_PID_INTERVAL                        125
-
-/* Exponential averaging smoothness (used for I part of PID controller) */
-#define RC_PID_SMOOTHING_SHIFT         3
-#define RC_PID_SMOOTHING               (1 << RC_PID_SMOOTHING_SHIFT)
-
-/* Sharpening factor (used for D part of PID controller) */
-#define RC_PID_SHARPENING_FACTOR       0
-#define RC_PID_SHARPENING_DURATION     0
-
-/* Fixed point arithmetic shifting amount. */
-#define RC_PID_ARITH_SHIFT             8
-
-/* Proportional PID component coefficient. */
-#define RC_PID_COEFF_P                 15
-/* Integral PID component coefficient. */
-#define RC_PID_COEFF_I                 9
-/* Derivative PID component coefficient. */
-#define RC_PID_COEFF_D                 15
-
-/* Target failed frames rate for the PID controller. NB: This effectively gives
- * maximum failed frames percentage we're willing to accept. If the wireless
- * link quality is good, the controller will fail to adjust failed frames
- * percentage to the target. This is intentional.
- */
-#define RC_PID_TARGET_PF               14
-
-/* Rate behaviour normalization quantity over time. */
-#define RC_PID_NORM_OFFSET             3
-
-/* Push high rates right after loading. */
-#define RC_PID_FAST_START              0
-
-/* Arithmetic right shift for positive and negative values for ISO C. */
-#define RC_PID_DO_ARITH_RIGHT_SHIFT(x, y) \
-       ((x) < 0 ? -((-(x)) >> (y)) : (x) >> (y))
-
-enum rc_pid_event_type {
-       RC_PID_EVENT_TYPE_TX_STATUS,
-       RC_PID_EVENT_TYPE_RATE_CHANGE,
-       RC_PID_EVENT_TYPE_TX_RATE,
-       RC_PID_EVENT_TYPE_PF_SAMPLE,
-};
-
-union rc_pid_event_data {
-       /* RC_PID_EVENT_TX_STATUS */
-       struct {
-               u32 flags;
-               struct ieee80211_tx_info tx_status;
-       };
-       /* RC_PID_EVENT_TYPE_RATE_CHANGE */
-       /* RC_PID_EVENT_TYPE_TX_RATE */
-       struct {
-               int index;
-               int rate;
-       };
-       /* RC_PID_EVENT_TYPE_PF_SAMPLE */
-       struct {
-               s32 pf_sample;
-               s32 prop_err;
-               s32 int_err;
-               s32 der_err;
-       };
-};
-
-struct rc_pid_event {
-       /* The time when the event occurred */
-       unsigned long timestamp;
-
-       /* Event ID number */
-       unsigned int id;
-
-       /* Type of event */
-       enum rc_pid_event_type type;
-
-       /* type specific data */
-       union rc_pid_event_data data;
-};
-
-/* Size of the event ring buffer. */
-#define RC_PID_EVENT_RING_SIZE 32
-
-struct rc_pid_event_buffer {
-       /* Counter that generates event IDs */
-       unsigned int ev_count;
-
-       /* Ring buffer of events */
-       struct rc_pid_event ring[RC_PID_EVENT_RING_SIZE];
-
-       /* Index to the entry in events_buf to be reused */
-       unsigned int next_entry;
-
-       /* Lock that guards against concurrent access to this buffer struct */
-       spinlock_t lock;
-
-       /* Wait queue for poll/select and blocking I/O */
-       wait_queue_head_t waitqueue;
-};
-
-struct rc_pid_events_file_info {
-       /* The event buffer we read */
-       struct rc_pid_event_buffer *events;
-
-       /* The entry we have should read next */
-       unsigned int next_entry;
-};
-
-/**
- * struct rc_pid_debugfs_entries - tunable parameters
- *
- * Algorithm parameters, tunable via debugfs.
- * @target: target percentage for failed frames
- * @sampling_period: error sampling interval in milliseconds
- * @coeff_p: absolute value of the proportional coefficient
- * @coeff_i: absolute value of the integral coefficient
- * @coeff_d: absolute value of the derivative coefficient
- * @smoothing_shift: absolute value of the integral smoothing factor (i.e.
- *     amount of smoothing introduced by the exponential moving average)
- * @sharpen_factor: absolute value of the derivative sharpening factor (i.e.
- *     amount of emphasis given to the derivative term after low activity
- *     events)
- * @sharpen_duration: duration of the sharpening effect after the detected low
- *     activity event, relative to sampling_period
- * @norm_offset: amount of normalization periodically performed on the learnt
- *     rate behaviour values (lower means we should trust more what we learnt
- *     about behaviour of rates, higher means we should trust more the natural
- *     ordering of rates)
- */
-struct rc_pid_debugfs_entries {
-       struct dentry *target;
-       struct dentry *sampling_period;
-       struct dentry *coeff_p;
-       struct dentry *coeff_i;
-       struct dentry *coeff_d;
-       struct dentry *smoothing_shift;
-       struct dentry *sharpen_factor;
-       struct dentry *sharpen_duration;
-       struct dentry *norm_offset;
-};
-
-void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
-                                     struct ieee80211_tx_info *stat);
-
-void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
-                                              int index, int rate);
-
-void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf,
-                                          int index, int rate);
-
-void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf,
-                                            s32 pf_sample, s32 prop_err,
-                                            s32 int_err, s32 der_err);
-
-void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta,
-                                            struct dentry *dir);
-
-void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta);
-
-struct rc_pid_sta_info {
-       unsigned long last_change;
-       unsigned long last_sample;
-
-       u32 tx_num_failed;
-       u32 tx_num_xmit;
-
-       int txrate_idx;
-
-       /* Average failed frames percentage error (i.e. actual vs. target
-        * percentage), scaled by RC_PID_SMOOTHING. This value is computed
-        * using using an exponential weighted average technique:
-        *
-        *           (RC_PID_SMOOTHING - 1) * err_avg_old + err
-        * err_avg = ------------------------------------------
-        *                       RC_PID_SMOOTHING
-        *
-        * where err_avg is the new approximation, err_avg_old the previous one
-        * and err is the error w.r.t. to the current failed frames percentage
-        * sample. Note that the bigger RC_PID_SMOOTHING the more weight is
-        * given to the previous estimate, resulting in smoother behavior (i.e.
-        * corresponding to a longer integration window).
-        *
-        * For computation, we actually don't use the above formula, but this
-        * one:
-        *
-        * err_avg_scaled = err_avg_old_scaled - err_avg_old + err
-        *
-        * where:
-        *      err_avg_scaled = err * RC_PID_SMOOTHING
-        *      err_avg_old_scaled = err_avg_old * RC_PID_SMOOTHING
-        *
-        * This avoids floating point numbers and the per_failed_old value can
-        * easily be obtained by shifting per_failed_old_scaled right by
-        * RC_PID_SMOOTHING_SHIFT.
-        */
-       s32 err_avg_sc;
-
-       /* Last framed failes percentage sample. */
-       u32 last_pf;
-
-       /* Sharpening needed. */
-       u8 sharp_cnt;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /* Event buffer */
-       struct rc_pid_event_buffer events;
-
-       /* Events debugfs file entry */
-       struct dentry *events_entry;
-#endif
-};
-
-/* Algorithm parameters. We keep them on a per-algorithm approach, so they can
- * be tuned individually for each interface.
- */
-struct rc_pid_rateinfo {
-
-       /* Map sorted rates to rates in ieee80211_hw_mode. */
-       int index;
-
-       /* Map rates in ieee80211_hw_mode to sorted rates. */
-       int rev_index;
-
-       /* Did we do any measurement on this rate? */
-       bool valid;
-
-       /* Comparison with the lowest rate. */
-       int diff;
-};
-
-struct rc_pid_info {
-
-       /* The failed frames percentage target. */
-       unsigned int target;
-
-       /* Rate at which failed frames percentage is sampled in 0.001s. */
-       unsigned int sampling_period;
-
-       /* P, I and D coefficients. */
-       int coeff_p;
-       int coeff_i;
-       int coeff_d;
-
-       /* Exponential averaging shift. */
-       unsigned int smoothing_shift;
-
-       /* Sharpening factor and duration. */
-       unsigned int sharpen_factor;
-       unsigned int sharpen_duration;
-
-       /* Normalization offset. */
-       unsigned int norm_offset;
-
-       /* Rates information. */
-       struct rc_pid_rateinfo *rinfo;
-
-       /* Index of the last used rate. */
-       int oldrate;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /* Debugfs entries created for the parameters above. */
-       struct rc_pid_debugfs_entries dentries;
-#endif
-};
-
-#endif /* RC80211_PID_H */
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
deleted file mode 100644 (file)
index d0da2a7..0000000
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright 2002-2005, Instant802 Networks, Inc.
- * Copyright 2005, Devicescape Software, Inc.
- * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
- * Copyright 2007-2008, Stefano Brivio <stefano.brivio@polimi.it>
- *
- * 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.
- */
-
-#include <linux/netdevice.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-#include "rate.h"
-#include "mesh.h"
-#include "rc80211_pid.h"
-
-
-/* This is an implementation of a TX rate control algorithm that uses a PID
- * controller. Given a target failed frames rate, the controller decides about
- * TX rate changes to meet the target failed frames rate.
- *
- * The controller basically computes the following:
- *
- * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening)
- *
- * where
- *     adj     adjustment value that is used to switch TX rate (see below)
- *     err     current error: target vs. current failed frames percentage
- *     last_err        last error
- *     err_avg average (i.e. poor man's integral) of recent errors
- *     sharpening      non-zero when fast response is needed (i.e. right after
- *                     association or no frames sent for a long time), heading
- *                     to zero over time
- *     CP      Proportional coefficient
- *     CI      Integral coefficient
- *     CD      Derivative coefficient
- *
- * CP, CI, CD are subject to careful tuning.
- *
- * The integral component uses a exponential moving average approach instead of
- * an actual sliding window. The advantage is that we don't need to keep an
- * array of the last N error values and computation is easier.
- *
- * Once we have the adj value, we map it to a rate by means of a learning
- * algorithm. This algorithm keeps the state of the percentual failed frames
- * difference between rates. The behaviour of the lowest available rate is kept
- * as a reference value, and every time we switch between two rates, we compute
- * the difference between the failed frames each rate exhibited. By doing so,
- * we compare behaviours which different rates exhibited in adjacent timeslices,
- * thus the comparison is minimally affected by external conditions. This
- * difference gets propagated to the whole set of measurements, so that the
- * reference is always the same. Periodically, we normalize this set so that
- * recent events weigh the most. By comparing the adj value with this set, we
- * avoid pejorative switches to lower rates and allow for switches to higher
- * rates if they behaved well.
- *
- * Note that for the computations we use a fixed-point representation to avoid
- * floating point arithmetic. Hence, all values are shifted left by
- * RC_PID_ARITH_SHIFT.
- */
-
-
-/* Adjust the rate while ensuring that we won't switch to a lower rate if it
- * exhibited a worse failed frames behaviour and we'll choose the highest rate
- * whose failed frames behaviour is not worse than the one of the original rate
- * target. While at it, check that the new rate is valid. */
-static void rate_control_pid_adjust_rate(struct ieee80211_supported_band *sband,
-                                        struct ieee80211_sta *sta,
-                                        struct rc_pid_sta_info *spinfo, int adj,
-                                        struct rc_pid_rateinfo *rinfo)
-{
-       int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
-       int cur = spinfo->txrate_idx;
-
-       band = sband->band;
-       n_bitrates = sband->n_bitrates;
-
-       /* Map passed arguments to sorted values. */
-       cur_sorted = rinfo[cur].rev_index;
-       new_sorted = cur_sorted + adj;
-
-       /* Check limits. */
-       if (new_sorted < 0)
-               new_sorted = rinfo[0].rev_index;
-       else if (new_sorted >= n_bitrates)
-               new_sorted = rinfo[n_bitrates - 1].rev_index;
-
-       tmp = new_sorted;
-
-       if (adj < 0) {
-               /* Ensure that the rate decrease isn't disadvantageous. */
-               for (probe = cur_sorted; probe >= new_sorted; probe--)
-                       if (rinfo[probe].diff <= rinfo[cur_sorted].diff &&
-                           rate_supported(sta, band, rinfo[probe].index))
-                               tmp = probe;
-       } else {
-               /* Look for rate increase with zero (or below) cost. */
-               for (probe = new_sorted + 1; probe < n_bitrates; probe++)
-                       if (rinfo[probe].diff <= rinfo[new_sorted].diff &&
-                           rate_supported(sta, band, rinfo[probe].index))
-                               tmp = probe;
-       }
-
-       /* Fit the rate found to the nearest supported rate. */
-       do {
-               if (rate_supported(sta, band, rinfo[tmp].index)) {
-                       spinfo->txrate_idx = rinfo[tmp].index;
-                       break;
-               }
-               if (adj < 0)
-                       tmp--;
-               else
-                       tmp++;
-       } while (tmp < n_bitrates && tmp >= 0);
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       rate_control_pid_event_rate_change(&spinfo->events,
-               spinfo->txrate_idx,
-               sband->bitrates[spinfo->txrate_idx].bitrate);
-#endif
-}
-
-/* Normalize the failed frames per-rate differences. */
-static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l)
-{
-       int i, norm_offset = pinfo->norm_offset;
-       struct rc_pid_rateinfo *r = pinfo->rinfo;
-
-       if (r[0].diff > norm_offset)
-               r[0].diff -= norm_offset;
-       else if (r[0].diff < -norm_offset)
-               r[0].diff += norm_offset;
-       for (i = 0; i < l - 1; i++)
-               if (r[i + 1].diff > r[i].diff + norm_offset)
-                       r[i + 1].diff -= norm_offset;
-               else if (r[i + 1].diff <= r[i].diff)
-                       r[i + 1].diff += norm_offset;
-}
-
-static void rate_control_pid_sample(struct rc_pid_info *pinfo,
-                                   struct ieee80211_supported_band *sband,
-                                   struct ieee80211_sta *sta,
-                                   struct rc_pid_sta_info *spinfo)
-{
-       struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
-       u32 pf;
-       s32 err_avg;
-       u32 err_prop;
-       u32 err_int;
-       u32 err_der;
-       int adj, i, j, tmp;
-       unsigned long period;
-
-       /* In case nothing happened during the previous control interval, turn
-        * the sharpening factor on. */
-       period = msecs_to_jiffies(pinfo->sampling_period);
-       if (jiffies - spinfo->last_sample > 2 * period)
-               spinfo->sharp_cnt = pinfo->sharpen_duration;
-
-       spinfo->last_sample = jiffies;
-
-       /* This should never happen, but in case, we assume the old sample is
-        * still a good measurement and copy it. */
-       if (unlikely(spinfo->tx_num_xmit == 0))
-               pf = spinfo->last_pf;
-       else
-               pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
-
-       spinfo->tx_num_xmit = 0;
-       spinfo->tx_num_failed = 0;
-
-       /* If we just switched rate, update the rate behaviour info. */
-       if (pinfo->oldrate != spinfo->txrate_idx) {
-
-               i = rinfo[pinfo->oldrate].rev_index;
-               j = rinfo[spinfo->txrate_idx].rev_index;
-
-               tmp = (pf - spinfo->last_pf);
-               tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT);
-
-               rinfo[j].diff = rinfo[i].diff + tmp;
-               pinfo->oldrate = spinfo->txrate_idx;
-       }
-       rate_control_pid_normalize(pinfo, sband->n_bitrates);
-
-       /* Compute the proportional, integral and derivative errors. */
-       err_prop = (pinfo->target - pf) << RC_PID_ARITH_SHIFT;
-
-       err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift;
-       spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop;
-       err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift;
-
-       err_der = (pf - spinfo->last_pf) *
-                 (1 + pinfo->sharpen_factor * spinfo->sharp_cnt);
-       spinfo->last_pf = pf;
-       if (spinfo->sharp_cnt)
-                       spinfo->sharp_cnt--;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int,
-                                        err_der);
-#endif
-
-       /* Compute the controller output. */
-       adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i
-             + err_der * pinfo->coeff_d);
-       adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT);
-
-       /* Change rate. */
-       if (adj)
-               rate_control_pid_adjust_rate(sband, sta, spinfo, adj, rinfo);
-}
-
-static void rate_control_pid_tx_status(void *priv, struct ieee80211_supported_band *sband,
-                                      struct ieee80211_sta *sta, void *priv_sta,
-                                      struct sk_buff *skb)
-{
-       struct rc_pid_info *pinfo = priv;
-       struct rc_pid_sta_info *spinfo = priv_sta;
-       unsigned long period;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
-       if (!spinfo)
-               return;
-
-       /* Ignore all frames that were sent with a different rate than the rate
-        * we currently advise mac80211 to use. */
-       if (info->status.rates[0].idx != spinfo->txrate_idx)
-               return;
-
-       spinfo->tx_num_xmit++;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       rate_control_pid_event_tx_status(&spinfo->events, info);
-#endif
-
-       /* We count frames that totally failed to be transmitted as two bad
-        * frames, those that made it out but had some retries as one good and
-        * one bad frame. */
-       if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
-               spinfo->tx_num_failed += 2;
-               spinfo->tx_num_xmit++;
-       } else if (info->status.rates[0].count > 1) {
-               spinfo->tx_num_failed++;
-               spinfo->tx_num_xmit++;
-       }
-
-       /* Update PID controller state. */
-       period = msecs_to_jiffies(pinfo->sampling_period);
-       if (time_after(jiffies, spinfo->last_sample + period))
-               rate_control_pid_sample(pinfo, sband, sta, spinfo);
-}
-
-static void
-rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
-                         void *priv_sta,
-                         struct ieee80211_tx_rate_control *txrc)
-{
-       struct sk_buff *skb = txrc->skb;
-       struct ieee80211_supported_band *sband = txrc->sband;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       struct rc_pid_sta_info *spinfo = priv_sta;
-       int rateidx;
-
-       if (txrc->rts)
-               info->control.rates[0].count =
-                       txrc->hw->conf.long_frame_max_tx_count;
-       else
-               info->control.rates[0].count =
-                       txrc->hw->conf.short_frame_max_tx_count;
-
-       /* Send management frames and NO_ACK data using lowest rate. */
-       if (rate_control_send_low(sta, priv_sta, txrc))
-               return;
-
-       rateidx = spinfo->txrate_idx;
-
-       if (rateidx >= sband->n_bitrates)
-               rateidx = sband->n_bitrates - 1;
-
-       info->control.rates[0].idx = rateidx;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       rate_control_pid_event_tx_rate(&spinfo->events,
-               rateidx, sband->bitrates[rateidx].bitrate);
-#endif
-}
-
-static void
-rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
-                          struct cfg80211_chan_def *chandef,
-                          struct ieee80211_sta *sta, void *priv_sta)
-{
-       struct rc_pid_sta_info *spinfo = priv_sta;
-       struct rc_pid_info *pinfo = priv;
-       struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
-       int i, j, tmp;
-       bool s;
-
-       /* TODO: This routine should consider using RSSI from previous packets
-        * as we need to have IEEE 802.1X auth succeed immediately after assoc..
-        * Until that method is implemented, we will use the lowest supported
-        * rate as a workaround. */
-
-       /* Sort the rates. This is optimized for the most common case (i.e.
-        * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
-        * mapping too. */
-       for (i = 0; i < sband->n_bitrates; i++) {
-               rinfo[i].index = i;
-               rinfo[i].rev_index = i;
-               if (RC_PID_FAST_START)
-                       rinfo[i].diff = 0;
-               else
-                       rinfo[i].diff = i * pinfo->norm_offset;
-       }
-       for (i = 1; i < sband->n_bitrates; i++) {
-               s = false;
-               for (j = 0; j < sband->n_bitrates - i; j++)
-                       if (unlikely(sband->bitrates[rinfo[j].index].bitrate >
-                                    sband->bitrates[rinfo[j + 1].index].bitrate)) {
-                               tmp = rinfo[j].index;
-                               rinfo[j].index = rinfo[j + 1].index;
-                               rinfo[j + 1].index = tmp;
-                               rinfo[rinfo[j].index].rev_index = j;
-                               rinfo[rinfo[j + 1].index].rev_index = j + 1;
-                               s = true;
-                       }
-               if (!s)
-                       break;
-       }
-
-       spinfo->txrate_idx = rate_lowest_index(sband, sta);
-}
-
-static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
-                                   struct dentry *debugfsdir)
-{
-       struct rc_pid_info *pinfo;
-       struct rc_pid_rateinfo *rinfo;
-       struct ieee80211_supported_band *sband;
-       int i, max_rates = 0;
-#ifdef CONFIG_MAC80211_DEBUGFS
-       struct rc_pid_debugfs_entries *de;
-#endif
-
-       pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
-       if (!pinfo)
-               return NULL;
-
-       for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
-               sband = hw->wiphy->bands[i];
-               if (sband && sband->n_bitrates > max_rates)
-                       max_rates = sband->n_bitrates;
-       }
-
-       rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC);
-       if (!rinfo) {
-               kfree(pinfo);
-               return NULL;
-       }
-
-       pinfo->target = RC_PID_TARGET_PF;
-       pinfo->sampling_period = RC_PID_INTERVAL;
-       pinfo->coeff_p = RC_PID_COEFF_P;
-       pinfo->coeff_i = RC_PID_COEFF_I;
-       pinfo->coeff_d = RC_PID_COEFF_D;
-       pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT;
-       pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR;
-       pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION;
-       pinfo->norm_offset = RC_PID_NORM_OFFSET;
-       pinfo->rinfo = rinfo;
-       pinfo->oldrate = 0;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       de = &pinfo->dentries;
-       de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
-                                       debugfsdir, &pinfo->target);
-       de->sampling_period = debugfs_create_u32("sampling_period",
-                                                S_IRUSR | S_IWUSR, debugfsdir,
-                                                &pinfo->sampling_period);
-       de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
-                                        debugfsdir, (u32 *)&pinfo->coeff_p);
-       de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
-                                        debugfsdir, (u32 *)&pinfo->coeff_i);
-       de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
-                                        debugfsdir, (u32 *)&pinfo->coeff_d);
-       de->smoothing_shift = debugfs_create_u32("smoothing_shift",
-                                                S_IRUSR | S_IWUSR, debugfsdir,
-                                                &pinfo->smoothing_shift);
-       de->sharpen_factor = debugfs_create_u32("sharpen_factor",
-                                              S_IRUSR | S_IWUSR, debugfsdir,
-                                              &pinfo->sharpen_factor);
-       de->sharpen_duration = debugfs_create_u32("sharpen_duration",
-                                                 S_IRUSR | S_IWUSR, debugfsdir,
-                                                 &pinfo->sharpen_duration);
-       de->norm_offset = debugfs_create_u32("norm_offset",
-                                            S_IRUSR | S_IWUSR, debugfsdir,
-                                            &pinfo->norm_offset);
-#endif
-
-       return pinfo;
-}
-
-static void rate_control_pid_free(void *priv)
-{
-       struct rc_pid_info *pinfo = priv;
-#ifdef CONFIG_MAC80211_DEBUGFS
-       struct rc_pid_debugfs_entries *de = &pinfo->dentries;
-
-       debugfs_remove(de->norm_offset);
-       debugfs_remove(de->sharpen_duration);
-       debugfs_remove(de->sharpen_factor);
-       debugfs_remove(de->smoothing_shift);
-       debugfs_remove(de->coeff_d);
-       debugfs_remove(de->coeff_i);
-       debugfs_remove(de->coeff_p);
-       debugfs_remove(de->sampling_period);
-       debugfs_remove(de->target);
-#endif
-
-       kfree(pinfo->rinfo);
-       kfree(pinfo);
-}
-
-static void *rate_control_pid_alloc_sta(void *priv, struct ieee80211_sta *sta,
-                                       gfp_t gfp)
-{
-       struct rc_pid_sta_info *spinfo;
-
-       spinfo = kzalloc(sizeof(*spinfo), gfp);
-       if (spinfo == NULL)
-               return NULL;
-
-       spinfo->last_sample = jiffies;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       spin_lock_init(&spinfo->events.lock);
-       init_waitqueue_head(&spinfo->events.waitqueue);
-#endif
-
-       return spinfo;
-}
-
-static void rate_control_pid_free_sta(void *priv, struct ieee80211_sta *sta,
-                                     void *priv_sta)
-{
-       kfree(priv_sta);
-}
-
-static const struct rate_control_ops mac80211_rcpid = {
-       .name = "pid",
-       .tx_status = rate_control_pid_tx_status,
-       .get_rate = rate_control_pid_get_rate,
-       .rate_init = rate_control_pid_rate_init,
-       .alloc = rate_control_pid_alloc,
-       .free = rate_control_pid_free,
-       .alloc_sta = rate_control_pid_alloc_sta,
-       .free_sta = rate_control_pid_free_sta,
-#ifdef CONFIG_MAC80211_DEBUGFS
-       .add_sta_debugfs = rate_control_pid_add_sta_debugfs,
-       .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs,
-#endif
-};
-
-int __init rc80211_pid_init(void)
-{
-       return ieee80211_rate_control_register(&mac80211_rcpid);
-}
-
-void rc80211_pid_exit(void)
-{
-       ieee80211_rate_control_unregister(&mac80211_rcpid);
-}
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c
deleted file mode 100644 (file)
index 6ff1346..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
- *
- * 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.
- */
-
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/poll.h>
-#include <linux/netdevice.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-#include <net/mac80211.h>
-#include "rate.h"
-
-#include "rc80211_pid.h"
-
-static void rate_control_pid_event(struct rc_pid_event_buffer *buf,
-                                  enum rc_pid_event_type type,
-                                  union rc_pid_event_data *data)
-{
-       struct rc_pid_event *ev;
-       unsigned long status;
-
-       spin_lock_irqsave(&buf->lock, status);
-       ev = &(buf->ring[buf->next_entry]);
-       buf->next_entry = (buf->next_entry + 1) % RC_PID_EVENT_RING_SIZE;
-
-       ev->timestamp = jiffies;
-       ev->id = buf->ev_count++;
-       ev->type = type;
-       ev->data = *data;
-
-       spin_unlock_irqrestore(&buf->lock, status);
-
-       wake_up_all(&buf->waitqueue);
-}
-
-void rate_control_pid_event_tx_status(struct rc_pid_event_buffer *buf,
-                                     struct ieee80211_tx_info *stat)
-{
-       union rc_pid_event_data evd;
-
-       evd.flags = stat->flags;
-       memcpy(&evd.tx_status, stat, sizeof(struct ieee80211_tx_info));
-       rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_STATUS, &evd);
-}
-
-void rate_control_pid_event_rate_change(struct rc_pid_event_buffer *buf,
-                                              int index, int rate)
-{
-       union rc_pid_event_data evd;
-
-       evd.index = index;
-       evd.rate = rate;
-       rate_control_pid_event(buf, RC_PID_EVENT_TYPE_RATE_CHANGE, &evd);
-}
-
-void rate_control_pid_event_tx_rate(struct rc_pid_event_buffer *buf,
-                                          int index, int rate)
-{
-       union rc_pid_event_data evd;
-
-       evd.index = index;
-       evd.rate = rate;
-       rate_control_pid_event(buf, RC_PID_EVENT_TYPE_TX_RATE, &evd);
-}
-
-void rate_control_pid_event_pf_sample(struct rc_pid_event_buffer *buf,
-                                            s32 pf_sample, s32 prop_err,
-                                            s32 int_err, s32 der_err)
-{
-       union rc_pid_event_data evd;
-
-       evd.pf_sample = pf_sample;
-       evd.prop_err = prop_err;
-       evd.int_err = int_err;
-       evd.der_err = der_err;
-       rate_control_pid_event(buf, RC_PID_EVENT_TYPE_PF_SAMPLE, &evd);
-}
-
-static int rate_control_pid_events_open(struct inode *inode, struct file *file)
-{
-       struct rc_pid_sta_info *sinfo = inode->i_private;
-       struct rc_pid_event_buffer *events = &sinfo->events;
-       struct rc_pid_events_file_info *file_info;
-       unsigned long status;
-
-       /* Allocate a state struct */
-       file_info = kmalloc(sizeof(*file_info), GFP_KERNEL);
-       if (file_info == NULL)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&events->lock, status);
-
-       file_info->next_entry = events->next_entry;
-       file_info->events = events;
-
-       spin_unlock_irqrestore(&events->lock, status);
-
-       file->private_data = file_info;
-
-       return 0;
-}
-
-static int rate_control_pid_events_release(struct inode *inode,
-                                          struct file *file)
-{
-       struct rc_pid_events_file_info *file_info = file->private_data;
-
-       kfree(file_info);
-
-       return 0;
-}
-
-static unsigned int rate_control_pid_events_poll(struct file *file,
-                                                poll_table *wait)
-{
-       struct rc_pid_events_file_info *file_info = file->private_data;
-
-       poll_wait(file, &file_info->events->waitqueue, wait);
-
-       return POLLIN | POLLRDNORM;
-}
-
-#define RC_PID_PRINT_BUF_SIZE 64
-
-static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
-                                           size_t length, loff_t *offset)
-{
-       struct rc_pid_events_file_info *file_info = file->private_data;
-       struct rc_pid_event_buffer *events = file_info->events;
-       struct rc_pid_event *ev;
-       char pb[RC_PID_PRINT_BUF_SIZE];
-       int ret;
-       int p;
-       unsigned long status;
-
-       /* Check if there is something to read. */
-       if (events->next_entry == file_info->next_entry) {
-               if (file->f_flags & O_NONBLOCK)
-                       return -EAGAIN;
-
-               /* Wait */
-               ret = wait_event_interruptible(events->waitqueue,
-                               events->next_entry != file_info->next_entry);
-
-               if (ret)
-                       return ret;
-       }
-
-       /* Write out one event per call. I don't care whether it's a little
-        * inefficient, this is debugging code anyway. */
-       spin_lock_irqsave(&events->lock, status);
-
-       /* Get an event */
-       ev = &(events->ring[file_info->next_entry]);
-       file_info->next_entry = (file_info->next_entry + 1) %
-                               RC_PID_EVENT_RING_SIZE;
-
-       /* Print information about the event. Note that userspace needs to
-        * provide large enough buffers. */
-       length = length < RC_PID_PRINT_BUF_SIZE ?
-                length : RC_PID_PRINT_BUF_SIZE;
-       p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
-       switch (ev->type) {
-       case RC_PID_EVENT_TYPE_TX_STATUS:
-               p += scnprintf(pb + p, length - p, "tx_status %u %u",
-                              !(ev->data.flags & IEEE80211_TX_STAT_ACK),
-                              ev->data.tx_status.status.rates[0].idx);
-               break;
-       case RC_PID_EVENT_TYPE_RATE_CHANGE:
-               p += scnprintf(pb + p, length - p, "rate_change %d %d",
-                              ev->data.index, ev->data.rate);
-               break;
-       case RC_PID_EVENT_TYPE_TX_RATE:
-               p += scnprintf(pb + p, length - p, "tx_rate %d %d",
-                              ev->data.index, ev->data.rate);
-               break;
-       case RC_PID_EVENT_TYPE_PF_SAMPLE:
-               p += scnprintf(pb + p, length - p,
-                              "pf_sample %d %d %d %d",
-                              ev->data.pf_sample, ev->data.prop_err,
-                              ev->data.int_err, ev->data.der_err);
-               break;
-       }
-       p += scnprintf(pb + p, length - p, "\n");
-
-       spin_unlock_irqrestore(&events->lock, status);
-
-       if (copy_to_user(buf, pb, p))
-               return -EFAULT;
-
-       return p;
-}
-
-#undef RC_PID_PRINT_BUF_SIZE
-
-static const struct file_operations rc_pid_fop_events = {
-       .owner = THIS_MODULE,
-       .read = rate_control_pid_events_read,
-       .poll = rate_control_pid_events_poll,
-       .open = rate_control_pid_events_open,
-       .release = rate_control_pid_events_release,
-       .llseek = noop_llseek,
-};
-
-void rate_control_pid_add_sta_debugfs(void *priv, void *priv_sta,
-                                            struct dentry *dir)
-{
-       struct rc_pid_sta_info *spinfo = priv_sta;
-
-       spinfo->events_entry = debugfs_create_file("rc_pid_events", S_IRUGO,
-                                                  dir, spinfo,
-                                                  &rc_pid_fop_events);
-}
-
-void rate_control_pid_remove_sta_debugfs(void *priv, void *priv_sta)
-{
-       struct rc_pid_sta_info *spinfo = priv_sta;
-
-       debugfs_remove(spinfo->events_entry);
-}
index 394e201cde6d3b6d4375f973937df55395547fea..bd2c9b22c945669f8ec1d48e3959101fd9ce6754 100644 (file)
@@ -688,20 +688,27 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
                                            int index,
                                            struct sk_buff_head *frames)
 {
-       struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
+       struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index];
+       struct sk_buff *skb;
        struct ieee80211_rx_status *status;
 
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
-       if (!skb)
+       if (skb_queue_empty(skb_list))
+               goto no_frame;
+
+       if (!ieee80211_rx_reorder_ready(skb_list)) {
+               __skb_queue_purge(skb_list);
                goto no_frame;
+       }
 
-       /* release the frame from the reorder ring buffer */
+       /* release frames from the reorder ring buffer */
        tid_agg_rx->stored_mpdu_num--;
-       tid_agg_rx->reorder_buf[index] = NULL;
-       status = IEEE80211_SKB_RXCB(skb);
-       status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
-       __skb_queue_tail(frames, skb);
+       while ((skb = __skb_dequeue(skb_list))) {
+               status = IEEE80211_SKB_RXCB(skb);
+               status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
+               __skb_queue_tail(frames, skb);
+       }
 
 no_frame:
        tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
@@ -738,13 +745,13 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                          struct tid_ampdu_rx *tid_agg_rx,
                                          struct sk_buff_head *frames)
 {
-       int index, j;
+       int index, i, j;
 
        lockdep_assert_held(&tid_agg_rx->reorder_lock);
 
        /* release the buffer until next missing frame */
        index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
-       if (!tid_agg_rx->reorder_buf[index] &&
+       if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) &&
            tid_agg_rx->stored_mpdu_num) {
                /*
                 * No buffers ready to be released, but check whether any
@@ -753,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                int skipped = 1;
                for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
                     j = (j + 1) % tid_agg_rx->buf_size) {
-                       if (!tid_agg_rx->reorder_buf[j]) {
+                       if (!ieee80211_rx_reorder_ready(
+                                       &tid_agg_rx->reorder_buf[j])) {
                                skipped++;
                                continue;
                        }
@@ -762,6 +770,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                        HT_RX_REORDER_BUF_TIMEOUT))
                                goto set_release_timer;
 
+                       /* don't leave incomplete A-MSDUs around */
+                       for (i = (index + 1) % tid_agg_rx->buf_size; i != j;
+                            i = (i + 1) % tid_agg_rx->buf_size)
+                               __skb_queue_purge(&tid_agg_rx->reorder_buf[i]);
+
                        ht_dbg_ratelimited(sdata,
                                           "release an RX reorder frame due to timeout on earlier frames\n");
                        ieee80211_release_reorder_frame(sdata, tid_agg_rx, j,
@@ -775,7 +788,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
                                 skipped) & IEEE80211_SN_MASK;
                        skipped = 0;
                }
-       } else while (tid_agg_rx->reorder_buf[index]) {
+       } else while (ieee80211_rx_reorder_ready(
+                               &tid_agg_rx->reorder_buf[index])) {
                ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
                                                frames);
                index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
@@ -786,7 +800,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
 
                for (; j != (index - 1) % tid_agg_rx->buf_size;
                     j = (j + 1) % tid_agg_rx->buf_size) {
-                       if (tid_agg_rx->reorder_buf[j])
+                       if (ieee80211_rx_reorder_ready(
+                                       &tid_agg_rx->reorder_buf[j]))
                                break;
                }
 
@@ -811,6 +826,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
                                             struct sk_buff_head *frames)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        u16 sc = le16_to_cpu(hdr->seq_ctrl);
        u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
        u16 head_seq_num, buf_size;
@@ -845,7 +861,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
        index = mpdu_seq_num % tid_agg_rx->buf_size;
 
        /* check if we already stored this frame */
-       if (tid_agg_rx->reorder_buf[index]) {
+       if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) {
                dev_kfree_skb(skb);
                goto out;
        }
@@ -858,17 +874,20 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
         */
        if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
            tid_agg_rx->stored_mpdu_num == 0) {
-               tid_agg_rx->head_seq_num =
-                       ieee80211_sn_inc(tid_agg_rx->head_seq_num);
+               if (!(status->flag & RX_FLAG_AMSDU_MORE))
+                       tid_agg_rx->head_seq_num =
+                               ieee80211_sn_inc(tid_agg_rx->head_seq_num);
                ret = false;
                goto out;
        }
 
        /* put the frame in the reordering buffer */
-       tid_agg_rx->reorder_buf[index] = skb;
-       tid_agg_rx->reorder_time[index] = jiffies;
-       tid_agg_rx->stored_mpdu_num++;
-       ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
+       __skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb);
+       if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
+               tid_agg_rx->reorder_time[index] = jiffies;
+               tid_agg_rx->stored_mpdu_num++;
+               ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
+       }
 
  out:
        spin_unlock(&tid_agg_rx->reorder_lock);
@@ -1107,6 +1126,8 @@ static void sta_ps_end(struct sta_info *sta)
                return;
        }
 
+       set_sta_flag(sta, WLAN_STA_PS_DELIVER);
+       clear_sta_flag(sta, WLAN_STA_PS_STA);
        ieee80211_sta_ps_deliver_wakeup(sta);
 }
 
@@ -3127,6 +3148,14 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
                        if (!ieee80211_is_beacon(hdr->frame_control))
                                return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
+               } else if (!ieee80211_has_tods(hdr->frame_control)) {
+                       /* ignore data frames to TDLS-peers */
+                       if (ieee80211_is_data(hdr->frame_control))
+                               return false;
+                       /* ignore action frames to TDLS-peers */
+                       if (ieee80211_is_action(hdr->frame_control) &&
+                           !ether_addr_equal(bssid, hdr->addr1))
+                               return false;
                }
                break;
        case NL80211_IFTYPE_WDS:
index f40661eb75b578dd2e409757331c085d36461723..a0a938145dcc87397cb4ecfad5f59b1c5fb28dbc 100644 (file)
@@ -235,38 +235,51 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
 {
        struct cfg80211_scan_request *req = local->scan_req;
        struct cfg80211_chan_def chandef;
-       enum ieee80211_band band;
+       u8 bands_used = 0;
        int i, ielen, n_chans;
 
        if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
                return false;
 
-       do {
-               if (local->hw_scan_band == IEEE80211_NUM_BANDS)
-                       return false;
-
-               band = local->hw_scan_band;
-               n_chans = 0;
+       if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) {
                for (i = 0; i < req->n_channels; i++) {
-                       if (req->channels[i]->band == band) {
-                               local->hw_scan_req->channels[n_chans] =
+                       local->hw_scan_req->req.channels[i] = req->channels[i];
+                       bands_used |= BIT(req->channels[i]->band);
+               }
+
+               n_chans = req->n_channels;
+       } else {
+               do {
+                       if (local->hw_scan_band == IEEE80211_NUM_BANDS)
+                               return false;
+
+                       n_chans = 0;
+
+                       for (i = 0; i < req->n_channels; i++) {
+                               if (req->channels[i]->band !=
+                                   local->hw_scan_band)
+                                       continue;
+                               local->hw_scan_req->req.channels[n_chans] =
                                                        req->channels[i];
                                n_chans++;
+                               bands_used |= BIT(req->channels[i]->band);
                        }
-               }
 
-               local->hw_scan_band++;
-       } while (!n_chans);
+                       local->hw_scan_band++;
+               } while (!n_chans);
+       }
 
-       local->hw_scan_req->n_channels = n_chans;
+       local->hw_scan_req->req.n_channels = n_chans;
        ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
 
-       ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
+       ielen = ieee80211_build_preq_ies(local,
+                                        (u8 *)local->hw_scan_req->req.ie,
                                         local->hw_scan_ies_bufsize,
-                                        req->ie, req->ie_len, band,
-                                        req->rates[band], &chandef);
-       local->hw_scan_req->ie_len = ielen;
-       local->hw_scan_req->no_cck = req->no_cck;
+                                        &local->hw_scan_req->ies,
+                                        req->ie, req->ie_len,
+                                        bands_used, req->rates, &chandef);
+       local->hw_scan_req->req.ie_len = ielen;
+       local->hw_scan_req->req.no_cck = req->no_cck;
 
        return true;
 }
@@ -291,7 +304,9 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
        if (WARN_ON(!local->scan_req))
                return;
 
-       if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
+       if (hw_scan && !aborted &&
+           !(local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) &&
+           ieee80211_prep_hw_scan(local)) {
                int rc;
 
                rc = drv_hw_scan(local,
@@ -473,6 +488,21 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                u8 *ies;
 
                local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
+
+               if (local->hw.flags & IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS) {
+                       int i, n_bands = 0;
+                       u8 bands_counted = 0;
+
+                       for (i = 0; i < req->n_channels; i++) {
+                               if (bands_counted & BIT(req->channels[i]->band))
+                                       continue;
+                               bands_counted |= BIT(req->channels[i]->band);
+                               n_bands++;
+                       }
+
+                       local->hw_scan_ies_bufsize *= n_bands;
+               }
+
                local->hw_scan_req = kmalloc(
                                sizeof(*local->hw_scan_req) +
                                req->n_channels * sizeof(req->channels[0]) +
@@ -480,13 +510,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
                if (!local->hw_scan_req)
                        return -ENOMEM;
 
-               local->hw_scan_req->ssids = req->ssids;
-               local->hw_scan_req->n_ssids = req->n_ssids;
+               local->hw_scan_req->req.ssids = req->ssids;
+               local->hw_scan_req->req.n_ssids = req->n_ssids;
                ies = (u8 *)local->hw_scan_req +
                        sizeof(*local->hw_scan_req) +
                        req->n_channels * sizeof(req->channels[0]);
-               local->hw_scan_req->ie = ies;
-               local->hw_scan_req->flags = req->flags;
+               local->hw_scan_req->req.ie = ies;
+               local->hw_scan_req->req.flags = req->flags;
 
                local->hw_scan_band = 0;
 
@@ -973,9 +1003,13 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                                        struct cfg80211_sched_scan_request *req)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_sched_scan_ies sched_scan_ies = {};
+       struct ieee80211_scan_ies sched_scan_ies = {};
        struct cfg80211_chan_def chandef;
-       int ret, i, iebufsz;
+       int ret, i, iebufsz, num_bands = 0;
+       u32 rate_masks[IEEE80211_NUM_BANDS] = {};
+       u8 bands_used = 0;
+       u8 *ie;
+       size_t len;
 
        iebufsz = local->scan_ies_len + req->ie_len;
 
@@ -985,33 +1019,35 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                return -ENOTSUPP;
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
-               if (!local->hw.wiphy->bands[i])
-                       continue;
-
-               sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
-               if (!sched_scan_ies.ie[i]) {
-                       ret = -ENOMEM;
-                       goto out_free;
+               if (local->hw.wiphy->bands[i]) {
+                       bands_used |= BIT(i);
+                       rate_masks[i] = (u32) -1;
+                       num_bands++;
                }
+       }
 
-               ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
-
-               sched_scan_ies.len[i] =
-                       ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
-                                                iebufsz, req->ie, req->ie_len,
-                                                i, (u32) -1, &chandef);
+       ie = kzalloc(num_bands * iebufsz, GFP_KERNEL);
+       if (!ie) {
+               ret = -ENOMEM;
+               goto out;
        }
 
+       ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
+
+       len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz,
+                                      &sched_scan_ies, req->ie,
+                                      req->ie_len, bands_used,
+                                      rate_masks, &chandef);
+
        ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
        if (ret == 0) {
                rcu_assign_pointer(local->sched_scan_sdata, sdata);
                local->sched_scan_req = req;
        }
 
-out_free:
-       while (i > 0)
-               kfree(sched_scan_ies.ie[--i]);
+       kfree(ie);
 
+out:
        if (ret) {
                /* Clean in case of failure after HW restart or upon resume. */
                RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
index a9b46d8ea22ff696623cec4cad55949fc5afd2a1..c6ee2139fbc579bab35e8cc5bfb6f3b4994930d2 100644 (file)
@@ -100,7 +100,8 @@ static void __cleanup_single_sta(struct sta_info *sta)
        struct ps_data *ps;
 
        if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
-           test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
+           test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
+           test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
                    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                        ps = &sdata->bss->ps;
@@ -111,6 +112,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
 
                clear_sta_flag(sta, WLAN_STA_PS_STA);
                clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+               clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
 
                atomic_dec(&ps->num_sta_ps);
                sta_info_recalc_tim(sta);
@@ -125,7 +127,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
        if (ieee80211_vif_is_mesh(&sdata->vif))
                mesh_sta_cleanup(sta);
 
-       cancel_work_sync(&sta->drv_unblock_wk);
+       cancel_work_sync(&sta->drv_deliver_wk);
 
        /*
         * Destroy aggregation state here. It would be nice to wait for the
@@ -253,33 +255,23 @@ static void sta_info_hash_add(struct ieee80211_local *local,
        rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
 }
 
-static void sta_unblock(struct work_struct *wk)
+static void sta_deliver_ps_frames(struct work_struct *wk)
 {
        struct sta_info *sta;
 
-       sta = container_of(wk, struct sta_info, drv_unblock_wk);
+       sta = container_of(wk, struct sta_info, drv_deliver_wk);
 
        if (sta->dead)
                return;
 
-       if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
-               local_bh_disable();
+       local_bh_disable();
+       if (!test_sta_flag(sta, WLAN_STA_PS_STA))
                ieee80211_sta_ps_deliver_wakeup(sta);
-               local_bh_enable();
-       } else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) {
-               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-
-               local_bh_disable();
+       else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL))
                ieee80211_sta_ps_deliver_poll_response(sta);
-               local_bh_enable();
-       } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) {
-               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-
-               local_bh_disable();
+       else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD))
                ieee80211_sta_ps_deliver_uapsd(sta);
-               local_bh_enable();
-       } else
-               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       local_bh_enable();
 }
 
 static int sta_prepare_rate_control(struct ieee80211_local *local,
@@ -341,7 +333,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        spin_lock_init(&sta->lock);
        spin_lock_init(&sta->ps_lock);
-       INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
+       INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
 #ifdef CONFIG_MAC80211_MESH
@@ -358,7 +350,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        sta->sta_state = IEEE80211_STA_NONE;
 
-       do_posix_clock_monotonic_gettime(&uptime);
+       ktime_get_ts(&uptime);
        sta->last_connected = uptime.tv_sec;
        ewma_init(&sta->avg_signal, 1024, 8);
        for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
@@ -1141,8 +1133,15 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
        }
 
        ieee80211_add_pending_skbs(local, &pending);
-       clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
-       clear_sta_flag(sta, WLAN_STA_PS_STA);
+
+       /* now we're no longer in the deliver code */
+       clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
+
+       /* The station might have polled and then woken up before we responded,
+        * so clear these flags now to avoid them sticking around.
+        */
+       clear_sta_flag(sta, WLAN_STA_PSPOLL);
+       clear_sta_flag(sta, WLAN_STA_UAPSD);
        spin_unlock(&sta->ps_lock);
 
        atomic_dec(&ps->num_sta_ps);
@@ -1543,10 +1542,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
 
        trace_api_sta_block_awake(sta->local, pubsta, block);
 
-       if (block)
+       if (block) {
                set_sta_flag(sta, WLAN_STA_PS_DRIVER);
-       else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER))
-               ieee80211_queue_work(hw, &sta->drv_unblock_wk);
+               return;
+       }
+
+       if (!test_sta_flag(sta, WLAN_STA_PS_DRIVER))
+               return;
+
+       if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
+               set_sta_flag(sta, WLAN_STA_PS_DELIVER);
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+               ieee80211_queue_work(hw, &sta->drv_deliver_wk);
+       } else if (test_sta_flag(sta, WLAN_STA_PSPOLL) ||
+                  test_sta_flag(sta, WLAN_STA_UAPSD)) {
+               /* must be asleep in this case */
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+               ieee80211_queue_work(hw, &sta->drv_deliver_wk);
+       } else {
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       }
 }
 EXPORT_SYMBOL(ieee80211_sta_block_awake);
 
@@ -1704,3 +1719,140 @@ u8 sta_info_tx_streams(struct sta_info *sta)
        return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
                        >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
 }
+
+void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+       struct rate_control_ref *ref = NULL;
+       struct timespec uptime;
+       u64 packets = 0;
+       u32 thr = 0;
+       int i, ac;
+
+       if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
+               ref = local->rate_ctrl;
+
+       sinfo->generation = sdata->local->sta_generation;
+
+       sinfo->filled = STATION_INFO_INACTIVE_TIME |
+                       STATION_INFO_RX_BYTES64 |
+                       STATION_INFO_TX_BYTES64 |
+                       STATION_INFO_RX_PACKETS |
+                       STATION_INFO_TX_PACKETS |
+                       STATION_INFO_TX_RETRIES |
+                       STATION_INFO_TX_FAILED |
+                       STATION_INFO_TX_BITRATE |
+                       STATION_INFO_RX_BITRATE |
+                       STATION_INFO_RX_DROP_MISC |
+                       STATION_INFO_BSS_PARAM |
+                       STATION_INFO_CONNECTED_TIME |
+                       STATION_INFO_STA_FLAGS |
+                       STATION_INFO_BEACON_LOSS_COUNT;
+
+       ktime_get_ts(&uptime);
+       sinfo->connected_time = uptime.tv_sec - sta->last_connected;
+
+       sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
+       sinfo->tx_bytes = 0;
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               sinfo->tx_bytes += sta->tx_bytes[ac];
+               packets += sta->tx_packets[ac];
+       }
+       sinfo->tx_packets = packets;
+       sinfo->rx_bytes = sta->rx_bytes;
+       sinfo->rx_packets = sta->rx_packets;
+       sinfo->tx_retries = sta->tx_retry_count;
+       sinfo->tx_failed = sta->tx_retry_failed;
+       sinfo->rx_dropped_misc = sta->rx_dropped;
+       sinfo->beacon_loss_count = sta->beacon_loss_count;
+
+       if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
+           (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
+               sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
+               if (!local->ops->get_rssi ||
+                   drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
+                       sinfo->signal = (s8)sta->last_signal;
+               sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
+       }
+       if (sta->chains) {
+               sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
+                                STATION_INFO_CHAIN_SIGNAL_AVG;
+
+               sinfo->chains = sta->chains;
+               for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
+                       sinfo->chain_signal[i] = sta->chain_signal_last[i];
+                       sinfo->chain_signal_avg[i] =
+                               (s8) -ewma_read(&sta->chain_signal_avg[i]);
+               }
+       }
+
+       sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
+       sta_set_rate_info_rx(sta, &sinfo->rxrate);
+
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+#ifdef CONFIG_MAC80211_MESH
+               sinfo->filled |= STATION_INFO_LLID |
+                                STATION_INFO_PLID |
+                                STATION_INFO_PLINK_STATE |
+                                STATION_INFO_LOCAL_PM |
+                                STATION_INFO_PEER_PM |
+                                STATION_INFO_NONPEER_PM;
+
+               sinfo->llid = sta->llid;
+               sinfo->plid = sta->plid;
+               sinfo->plink_state = sta->plink_state;
+               if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
+                       sinfo->filled |= STATION_INFO_T_OFFSET;
+                       sinfo->t_offset = sta->t_offset;
+               }
+               sinfo->local_pm = sta->local_pm;
+               sinfo->peer_pm = sta->peer_pm;
+               sinfo->nonpeer_pm = sta->nonpeer_pm;
+#endif
+       }
+
+       sinfo->bss_param.flags = 0;
+       if (sdata->vif.bss_conf.use_cts_prot)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
+       if (sdata->vif.bss_conf.use_short_preamble)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+       if (sdata->vif.bss_conf.use_short_slot)
+               sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+       sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
+       sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
+
+       sinfo->sta_flags.set = 0;
+       sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
+                               BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
+                               BIT(NL80211_STA_FLAG_WME) |
+                               BIT(NL80211_STA_FLAG_MFP) |
+                               BIT(NL80211_STA_FLAG_AUTHENTICATED) |
+                               BIT(NL80211_STA_FLAG_ASSOCIATED) |
+                               BIT(NL80211_STA_FLAG_TDLS_PEER);
+       if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+       if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+       if (test_sta_flag(sta, WLAN_STA_WME))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
+       if (test_sta_flag(sta, WLAN_STA_MFP))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
+       if (test_sta_flag(sta, WLAN_STA_AUTH))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+       if (test_sta_flag(sta, WLAN_STA_ASSOC))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+       /* check if the driver has a SW RC implementation */
+       if (ref && ref->ops->get_expected_throughput)
+               thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
+       else
+               thr = drv_get_expected_throughput(local, &sta->sta);
+
+       if (thr != 0) {
+               sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
+               sinfo->expected_throughput = thr;
+       }
+}
index 4acc5fc402fa30b11e8f26da93be91eeabcc303a..d411bcc8ef085215b8369fc0f066a49fd65fc6eb 100644 (file)
@@ -47,6 +47,8 @@
  * @WLAN_STA_TDLS_PEER: Station is a TDLS peer.
  * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct
  *     packets. This means the link is enabled.
+ * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
+ *     station.
  * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
  *     keeping station in power-save mode, reply when the driver
  *     unblocks the station.
@@ -58,6 +60,8 @@
  * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
  * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period.
  * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
+ * @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX
+ *     until pending frames are delivered
  */
 enum ieee80211_sta_info_flags {
        WLAN_STA_AUTH,
@@ -74,6 +78,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_PSPOLL,
        WLAN_STA_TDLS_PEER,
        WLAN_STA_TDLS_PEER_AUTH,
+       WLAN_STA_TDLS_INITIATOR,
        WLAN_STA_UAPSD,
        WLAN_STA_SP,
        WLAN_STA_4ADDR_EVENT,
@@ -82,6 +87,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_TOFFSET_KNOWN,
        WLAN_STA_MPSP_OWNER,
        WLAN_STA_MPSP_RECIPIENT,
+       WLAN_STA_PS_DELIVER,
 };
 
 #define ADDBA_RESP_INTERVAL HZ
@@ -149,7 +155,8 @@ struct tid_ampdu_tx {
 /**
  * struct tid_ampdu_rx - TID aggregation information (Rx).
  *
- * @reorder_buf: buffer to reorder incoming aggregated MPDUs
+ * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
+ *     A-MSDU with individually reported subframes.
  * @reorder_time: jiffies when skb was added
  * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
  * @reorder_timer: releases expired frames from the reorder buffer.
@@ -174,7 +181,7 @@ struct tid_ampdu_tx {
 struct tid_ampdu_rx {
        struct rcu_head rcu_head;
        spinlock_t reorder_lock;
-       struct sk_buff **reorder_buf;
+       struct sk_buff_head *reorder_buf;
        unsigned long *reorder_time;
        struct timer_list session_timer;
        struct timer_list reorder_timer;
@@ -265,7 +272,7 @@ struct ieee80211_tx_latency_stat {
  * @last_rx_rate_vht_nss: rx status nss of last data packet
  * @lock: used for locking all fields that require locking, see comments
  *     in the header file.
- * @drv_unblock_wk: used for driver PS unblocking
+ * @drv_deliver_wk: used for delivering frames after driver PS unblocking
  * @listen_interval: listen interval of this station, when we're acting as AP
  * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly
  * @ps_lock: used for powersave (when mac80211 is the AP) related locking
@@ -278,7 +285,6 @@ struct ieee80211_tx_latency_stat {
  * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on
  * @rx_packets: Number of MSDUs received from this STA
  * @rx_bytes: Number of bytes received from this STA
- * @wep_weak_iv_count: number of weak WEP IVs received from this station
  * @last_rx: time (in jiffies) when last frame was received from this STA
  * @last_connected: time (in seconds) when a station got connected
  * @num_duplicates: number of duplicate frames received from this STA
@@ -303,7 +309,6 @@ struct ieee80211_tx_latency_stat {
  * @plid: Peer link ID
  * @reason: Cancel reason on PLINK_HOLDING state
  * @plink_retries: Retries in establishment
- * @ignore_plink_timer: ignore the peer-link timer (used internally)
  * @plink_state: peer link state
  * @plink_timeout: timeout of peer link
  * @plink_timer: peer link watch timer
@@ -345,7 +350,7 @@ struct sta_info {
        void *rate_ctrl_priv;
        spinlock_t lock;
 
-       struct work_struct drv_unblock_wk;
+       struct work_struct drv_deliver_wk;
 
        u16 listen_interval;
 
@@ -367,7 +372,6 @@ struct sta_info {
        /* Updated from RX path only, no locking requirements */
        unsigned long rx_packets;
        u64 rx_bytes;
-       unsigned long wep_weak_iv_count;
        unsigned long last_rx;
        long last_connected;
        unsigned long num_duplicates;
@@ -418,7 +422,6 @@ struct sta_info {
        u16 plid;
        u16 reason;
        u8 plink_retries;
-       bool ignore_plink_timer;
        enum nl80211_plink_state plink_state;
        u32 plink_timeout;
        struct timer_list plink_timer;
@@ -628,6 +631,8 @@ void sta_set_rate_info_tx(struct sta_info *sta,
                          struct rate_info *rinfo);
 void sta_set_rate_info_rx(struct sta_info *sta,
                          struct rate_info *rinfo);
+void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo);
+
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time);
 u8 sta_info_tx_streams(struct sta_info *sta);
index ba29ebc8614121ea90ff5ff1cec44f137ac85a69..aa06dcad336e6d9bcdfff2009d7775b26bee8a6e 100644 (file)
@@ -473,8 +473,6 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
                                            struct sta_info *sta,
                                            struct ieee80211_hdr *hdr)
 {
-       ktime_t skb_dprt;
-       struct timespec dprt_time;
        u32 msrmnt;
        u16 tid;
        u8 *qc;
@@ -506,9 +504,8 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
 
        tx_lat = &sta->tx_lat[tid];
 
-       ktime_get_ts(&dprt_time); /* time stamp completion time */
-       skb_dprt = ktime_set(dprt_time.tv_sec, dprt_time.tv_nsec);
-       msrmnt = ktime_to_ms(ktime_sub(skb_dprt, skb_arv));
+       /* Calculate the latency */
+       msrmnt = ktime_to_ms(ktime_sub(ktime_get(), skb_arv));
 
        if (tx_lat->max < msrmnt) /* update stats */
                tx_lat->max = msrmnt;
index 652813b2d3df6afda3d136f1c8db1fd35f2d3b1d..1b21050be174b29539a954991d0376dab76fc78f 100644 (file)
@@ -8,7 +8,31 @@
  */
 
 #include <linux/ieee80211.h>
+#include <linux/log2.h>
+#include <net/cfg80211.h>
 #include "ieee80211_i.h"
+#include "driver-ops.h"
+
+/* give usermode some time for retries in setting up the TDLS session */
+#define TDLS_PEER_SETUP_TIMEOUT        (15 * HZ)
+
+void ieee80211_tdls_peer_del_work(struct work_struct *wk)
+{
+       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_local *local;
+
+       sdata = container_of(wk, struct ieee80211_sub_if_data,
+                            u.mgd.tdls_peer_del_work.work);
+       local = sdata->local;
+
+       mutex_lock(&local->mtx);
+       if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) {
+               tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer);
+               sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer);
+               eth_zero_addr(sdata->u.mgd.tdls_peer);
+       }
+       mutex_unlock(&local->mtx);
+}
 
 static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
 {
@@ -23,11 +47,16 @@ static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
        *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
 }
 
-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
+static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
+                                       u16 status_code)
 {
        struct ieee80211_local *local = sdata->local;
        u16 capab;
 
+       /* The capability will be 0 when sending a failure code */
+       if (status_code != 0)
+               return 0;
+
        capab = 0;
        if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
                return capab;
@@ -40,19 +69,332 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
        return capab;
 }
 
-static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
-                                      const u8 *peer, const u8 *bssid)
+static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
+                                      struct sk_buff *skb, const u8 *peer,
+                                      bool initiator)
 {
        struct ieee80211_tdls_lnkie *lnkid;
+       const u8 *init_addr, *rsp_addr;
+
+       if (initiator) {
+               init_addr = sdata->vif.addr;
+               rsp_addr = peer;
+       } else {
+               init_addr = peer;
+               rsp_addr = sdata->vif.addr;
+       }
 
        lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
 
        lnkid->ie_type = WLAN_EID_LINK_ID;
        lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
 
-       memcpy(lnkid->bssid, bssid, ETH_ALEN);
-       memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
-       memcpy(lnkid->resp_sta, peer, ETH_ALEN);
+       memcpy(lnkid->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+       memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
+       memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
+}
+
+/* translate numbering in the WMM parameter IE to the mac80211 notation */
+static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac)
+{
+       switch (ac) {
+       default:
+               WARN_ON_ONCE(1);
+       case 0:
+               return IEEE80211_AC_BE;
+       case 1:
+               return IEEE80211_AC_BK;
+       case 2:
+               return IEEE80211_AC_VI;
+       case 3:
+               return IEEE80211_AC_VO;
+       }
+}
+
+static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci)
+{
+       u8 ret;
+
+       ret = aifsn & 0x0f;
+       if (acm)
+               ret |= 0x10;
+       ret |= (aci << 5) & 0x60;
+       return ret;
+}
+
+static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max)
+{
+       return ((ilog2(cw_min + 1) << 0x0) & 0x0f) |
+              ((ilog2(cw_max + 1) << 0x4) & 0xf0);
+}
+
+static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
+                                           struct sk_buff *skb)
+{
+       struct ieee80211_wmm_param_ie *wmm;
+       struct ieee80211_tx_queue_params *txq;
+       int i;
+
+       wmm = (void *)skb_put(skb, sizeof(*wmm));
+       memset(wmm, 0, sizeof(*wmm));
+
+       wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+       wmm->len = sizeof(*wmm) - 2;
+
+       wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+       wmm->oui[1] = 0x50;
+       wmm->oui[2] = 0xf2;
+       wmm->oui_type = 2; /* WME */
+       wmm->oui_subtype = 1; /* WME param */
+       wmm->version = 1; /* WME ver */
+       wmm->qos_info = 0; /* U-APSD not in use */
+
+       /*
+        * Use the EDCA parameters defined for the BSS, or default if the AP
+        * doesn't support it, as mandated by 802.11-2012 section 10.22.4
+        */
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)];
+               wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs,
+                                                              txq->acm, i);
+               wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max);
+               wmm->ac[i].txop_limit = cpu_to_le16(txq->txop);
+       }
+}
+
+static void
+ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
+                                  struct sk_buff *skb, const u8 *peer,
+                                  u8 action_code, bool initiator,
+                                  const u8 *extra_ies, size_t extra_ies_len)
+{
+       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_sta_ht_cap ht_cap;
+       struct sta_info *sta = NULL;
+       size_t offset = 0, noffset;
+       u8 *pos;
+
+       rcu_read_lock();
+
+       /* we should have the peer STA if we're already responding */
+       if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+               sta = sta_info_get(sdata, peer);
+               if (WARN_ON_ONCE(!sta)) {
+                       rcu_read_unlock();
+                       return;
+               }
+       }
+
+       ieee80211_add_srates_ie(sdata, skb, false, band);
+       ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+
+       /* add any custom IEs that go before Extended Capabilities */
+       if (extra_ies_len) {
+               static const u8 before_ext_cap[] = {
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_COUNTRY,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_SUPPORTED_CHANNELS,
+                       WLAN_EID_RSN,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ext_cap,
+                                            ARRAY_SIZE(before_ext_cap),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       ieee80211_tdls_add_ext_capab(skb);
+
+       /* add the QoS element if we support it */
+       if (local->hw.queues >= IEEE80211_NUM_ACS &&
+           action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
+               ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */
+
+       /* add any custom IEs that go before HT capabilities */
+       if (extra_ies_len) {
+               static const u8 before_ht_cap[] = {
+                       WLAN_EID_SUPP_RATES,
+                       WLAN_EID_COUNTRY,
+                       WLAN_EID_EXT_SUPP_RATES,
+                       WLAN_EID_SUPPORTED_CHANNELS,
+                       WLAN_EID_RSN,
+                       WLAN_EID_EXT_CAPABILITY,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_FAST_BSS_TRANSITION,
+                       WLAN_EID_TIMEOUT_INTERVAL,
+                       WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ht_cap,
+                                            ARRAY_SIZE(before_ht_cap),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /*
+        * with TDLS we can switch channels, and HT-caps are not necessarily
+        * the same on all bands. The specification limits the setup to a
+        * single HT-cap, so use the current band for now.
+        */
+       sband = local->hw.wiphy->bands[band];
+       memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+       if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
+            action_code == WLAN_TDLS_SETUP_RESPONSE) &&
+           ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
+               if (action_code == WLAN_TDLS_SETUP_REQUEST) {
+                       ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+                       /* disable SMPS in TDLS initiator */
+                       ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
+                                      << IEEE80211_HT_CAP_SM_PS_SHIFT);
+               } else {
+                       /* disable SMPS in TDLS responder */
+                       sta->sta.ht_cap.cap |=
+                               (WLAN_HT_CAP_SM_PS_DISABLED
+                                << IEEE80211_HT_CAP_SM_PS_SHIFT);
+
+                       /* the peer caps are already intersected with our own */
+                       memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
+               }
+
+               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+               ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
+       }
+
+       rcu_read_unlock();
+
+       /* add any remaining IEs */
+       if (extra_ies_len) {
+               noffset = extra_ies_len;
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+       }
+
+       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+}
+
+static void
+ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
+                                struct sk_buff *skb, const u8 *peer,
+                                bool initiator, const u8 *extra_ies,
+                                size_t extra_ies_len)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       size_t offset = 0, noffset;
+       struct sta_info *sta, *ap_sta;
+       u8 *pos;
+
+       rcu_read_lock();
+
+       sta = sta_info_get(sdata, peer);
+       ap_sta = sta_info_get(sdata, ifmgd->bssid);
+       if (WARN_ON_ONCE(!sta || !ap_sta)) {
+               rcu_read_unlock();
+               return;
+       }
+
+       /* add any custom IEs that go before the QoS IE */
+       if (extra_ies_len) {
+               static const u8 before_qos[] = {
+                       WLAN_EID_RSN,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_qos,
+                                            ARRAY_SIZE(before_qos),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /* add the QoS param IE if both the peer and we support it */
+       if (local->hw.queues >= IEEE80211_NUM_ACS &&
+           test_sta_flag(sta, WLAN_STA_WME))
+               ieee80211_tdls_add_wmm_param_ie(sdata, skb);
+
+       /* add any custom IEs that go before HT operation */
+       if (extra_ies_len) {
+               static const u8 before_ht_op[] = {
+                       WLAN_EID_RSN,
+                       WLAN_EID_QOS_CAPA,
+                       WLAN_EID_FAST_BSS_TRANSITION,
+                       WLAN_EID_TIMEOUT_INTERVAL,
+               };
+               noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+                                            before_ht_op,
+                                            ARRAY_SIZE(before_ht_op),
+                                            offset);
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+               offset = noffset;
+       }
+
+       /* if HT support is only added in TDLS, we need an HT-operation IE */
+       if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+               struct ieee80211_chanctx_conf *chanctx_conf =
+                               rcu_dereference(sdata->vif.chanctx_conf);
+               if (!WARN_ON(!chanctx_conf)) {
+                       pos = skb_put(skb, 2 +
+                                     sizeof(struct ieee80211_ht_operation));
+                       /* send an empty HT operation IE */
+                       ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap,
+                                                  &chanctx_conf->def, 0);
+               }
+       }
+
+       rcu_read_unlock();
+
+       /* add any remaining IEs */
+       if (extra_ies_len) {
+               noffset = extra_ies_len;
+               pos = skb_put(skb, noffset - offset);
+               memcpy(pos, extra_ies + offset, noffset - offset);
+       }
+
+       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+}
+
+static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
+                                  struct sk_buff *skb, const u8 *peer,
+                                  u8 action_code, u16 status_code,
+                                  bool initiator, const u8 *extra_ies,
+                                  size_t extra_ies_len)
+{
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               if (status_code == 0)
+                       ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
+                                                          action_code,
+                                                          initiator,
+                                                          extra_ies,
+                                                          extra_ies_len);
+               break;
+       case WLAN_TDLS_SETUP_CONFIRM:
+               if (status_code == 0)
+                       ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
+                                                        initiator, extra_ies,
+                                                        extra_ies_len);
+               break;
+       case WLAN_TDLS_TEARDOWN:
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               if (extra_ies_len)
+                       memcpy(skb_put(skb, extra_ies_len), extra_ies,
+                              extra_ies_len);
+               if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
+                       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+               break;
+       }
+
 }
 
 static int
@@ -61,7 +403,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                               u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_tdls_data *tf;
 
        tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
@@ -79,11 +420,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                skb_put(skb, sizeof(tf->u.setup_req));
                tf->u.setup_req.dialog_token = dialog_token;
                tf->u.setup_req.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
                tf->category = WLAN_CATEGORY_TDLS;
@@ -93,11 +431,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                tf->u.setup_resp.status_code = cpu_to_le16(status_code);
                tf->u.setup_resp.dialog_token = dialog_token;
                tf->u.setup_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        case WLAN_TDLS_SETUP_CONFIRM:
                tf->category = WLAN_CATEGORY_TDLS;
@@ -134,7 +469,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
                           u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-       enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
        struct ieee80211_mgmt *mgmt;
 
        mgmt = (void *)skb_put(skb, 24);
@@ -155,11 +489,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
                mgmt->u.action.u.tdls_discover_resp.dialog_token =
                        dialog_token;
                mgmt->u.action.u.tdls_discover_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
-
-               ieee80211_add_srates_ie(sdata, skb, false, band);
-               ieee80211_add_ext_srates_ie(sdata, skb, false, band);
-               ieee80211_tdls_add_ext_capab(skb);
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                                                                status_code));
                break;
        default:
                return -EINVAL;
@@ -168,33 +499,28 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
-int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
-                       const u8 *peer, u8 action_code, u8 dialog_token,
-                       u16 status_code, u32 peer_capability,
-                       const u8 *extra_ies, size_t extra_ies_len)
+static int
+ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
+                               const u8 *peer, u8 action_code,
+                               u8 dialog_token, u16 status_code,
+                               u32 peer_capability, bool initiator,
+                               const u8 *extra_ies, size_t extra_ies_len)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb = NULL;
        bool send_direct;
+       struct sta_info *sta;
        int ret;
 
-       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
-               return -ENOTSUPP;
-
-       /* make sure we are in managed mode, and associated */
-       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
-           !sdata->u.mgd.associated)
-               return -EINVAL;
-
-       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n",
-                action_code, peer);
-
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
                            max(sizeof(struct ieee80211_mgmt),
                                sizeof(struct ieee80211_tdls_data)) +
                            50 + /* supported rates */
                            7 + /* ext capab */
+                           26 + /* max(WMM-info, WMM-param) */
+                           2 + max(sizeof(struct ieee80211_ht_cap),
+                                   sizeof(struct ieee80211_ht_operation)) +
                            extra_ies_len +
                            sizeof(struct ieee80211_tdls_lnkie));
        if (!skb)
@@ -227,30 +553,48 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
        if (ret < 0)
                goto fail;
 
-       if (extra_ies_len)
-               memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+       rcu_read_lock();
+       sta = sta_info_get(sdata, peer);
 
-       /* the TDLS link IE is always added last */
+       /* infer the initiator if we can, to support old userspace */
        switch (action_code) {
        case WLAN_TDLS_SETUP_REQUEST:
+               if (sta)
+                       set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+               /* fall-through */
        case WLAN_TDLS_SETUP_CONFIRM:
-       case WLAN_TDLS_TEARDOWN:
        case WLAN_TDLS_DISCOVERY_REQUEST:
-               /* we are the initiator */
-               ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
-                                          sdata->u.mgd.bssid);
+               initiator = true;
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
+               /*
+                * In some testing scenarios, we send a request and response.
+                * Make the last packet sent take effect for the initiator
+                * value.
+                */
+               if (sta)
+                       clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+               /* fall-through */
        case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
-               /* we are the responder */
-               ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
-                                          sdata->u.mgd.bssid);
+               initiator = false;
+               break;
+       case WLAN_TDLS_TEARDOWN:
+               /* any value is ok */
                break;
        default:
                ret = -ENOTSUPP;
-               goto fail;
+               break;
        }
 
+       if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
+               initiator = true;
+
+       rcu_read_unlock();
+       if (ret < 0)
+               goto fail;
+
+       ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
+                              initiator, extra_ies, extra_ies_len);
        if (send_direct) {
                ieee80211_tx_skb(sdata, skb);
                return 0;
@@ -284,11 +628,175 @@ fail:
        return ret;
 }
 
+static int
+ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
+                         const u8 *peer, u8 action_code, u8 dialog_token,
+                         u16 status_code, u32 peer_capability, bool initiator,
+                         const u8 *extra_ies, size_t extra_ies_len)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       int ret;
+
+       mutex_lock(&local->mtx);
+
+       /* we don't support concurrent TDLS peer setups */
+       if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
+           !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       /*
+        * make sure we have a STA representing the peer so we drop or buffer
+        * non-TDLS-setup frames to the peer. We can't send other packets
+        * during setup through the AP path.
+        * Allow error packets to be sent - sometimes we don't even add a STA
+        * before failing the setup.
+        */
+       if (status_code == 0) {
+               rcu_read_lock();
+               if (!sta_info_get(sdata, peer)) {
+                       rcu_read_unlock();
+                       ret = -ENOLINK;
+                       goto exit;
+               }
+               rcu_read_unlock();
+       }
+
+       ieee80211_flush_queues(local, sdata);
+
+       ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+                                             dialog_token, status_code,
+                                             peer_capability, initiator,
+                                             extra_ies, extra_ies_len);
+       if (ret < 0)
+               goto exit;
+
+       memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
+       ieee80211_queue_delayed_work(&sdata->local->hw,
+                                    &sdata->u.mgd.tdls_peer_del_work,
+                                    TDLS_PEER_SETUP_TIMEOUT);
+
+exit:
+       mutex_unlock(&local->mtx);
+       return ret;
+}
+
+static int
+ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
+                            const u8 *peer, u8 action_code, u8 dialog_token,
+                            u16 status_code, u32 peer_capability,
+                            bool initiator, const u8 *extra_ies,
+                            size_t extra_ies_len)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct sta_info *sta;
+       int ret;
+
+       /*
+        * No packets can be transmitted to the peer via the AP during setup -
+        * the STA is set as a TDLS peer, but is not authorized.
+        * During teardown, we prevent direct transmissions by stopping the
+        * queues and flushing all direct packets.
+        */
+       ieee80211_stop_vif_queues(local, sdata,
+                                 IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
+       ieee80211_flush_queues(local, sdata);
+
+       ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+                                             dialog_token, status_code,
+                                             peer_capability, initiator,
+                                             extra_ies, extra_ies_len);
+       if (ret < 0)
+               sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
+                         ret);
+
+       /*
+        * Remove the STA AUTH flag to force further traffic through the AP. If
+        * the STA was unreachable, it was already removed.
+        */
+       rcu_read_lock();
+       sta = sta_info_get(sdata, peer);
+       if (sta)
+               clear_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+       rcu_read_unlock();
+
+       ieee80211_wake_vif_queues(local, sdata,
+                                 IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
+
+       return 0;
+}
+
+int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                       const u8 *peer, u8 action_code, u8 dialog_token,
+                       u16 status_code, u32 peer_capability,
+                       bool initiator, const u8 *extra_ies,
+                       size_t extra_ies_len)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       int ret;
+
+       if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+               return -ENOTSUPP;
+
+       /* make sure we are in managed mode, and associated */
+       if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+           !sdata->u.mgd.associated)
+               return -EINVAL;
+
+       switch (action_code) {
+       case WLAN_TDLS_SETUP_REQUEST:
+       case WLAN_TDLS_SETUP_RESPONSE:
+               ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, action_code,
+                                               dialog_token, status_code,
+                                               peer_capability, initiator,
+                                               extra_ies, extra_ies_len);
+               break;
+       case WLAN_TDLS_TEARDOWN:
+               ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer,
+                                                  action_code, dialog_token,
+                                                  status_code,
+                                                  peer_capability, initiator,
+                                                  extra_ies, extra_ies_len);
+               break;
+       case WLAN_TDLS_DISCOVERY_REQUEST:
+               /*
+                * Protect the discovery so we can hear the TDLS discovery
+                * response frame. It is transmitted directly and not buffered
+                * by the AP.
+                */
+               drv_mgd_protect_tdls_discover(sdata->local, sdata);
+               /* fall-through */
+       case WLAN_TDLS_SETUP_CONFIRM:
+       case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+               /* no special handling */
+               ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
+                                                     action_code,
+                                                     dialog_token,
+                                                     status_code,
+                                                     peer_capability,
+                                                     initiator, extra_ies,
+                                                     extra_ies_len);
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n",
+                action_code, peer, ret);
+       return ret;
+}
+
 int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                        const u8 *peer, enum nl80211_tdls_operation oper)
 {
        struct sta_info *sta;
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       int ret;
 
        if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
                return -ENOTSUPP;
@@ -296,6 +804,18 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
        if (sdata->vif.type != NL80211_IFTYPE_STATION)
                return -EINVAL;
 
+       switch (oper) {
+       case NL80211_TDLS_ENABLE_LINK:
+       case NL80211_TDLS_DISABLE_LINK:
+               break;
+       case NL80211_TDLS_TEARDOWN:
+       case NL80211_TDLS_SETUP:
+       case NL80211_TDLS_DISCOVERY_REQ:
+               /* We don't support in-driver setup/teardown/discovery */
+               return -ENOTSUPP;
+       }
+
+       mutex_lock(&local->mtx);
        tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
 
        switch (oper) {
@@ -304,22 +824,60 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                sta = sta_info_get(sdata, peer);
                if (!sta) {
                        rcu_read_unlock();
-                       return -ENOLINK;
+                       ret = -ENOLINK;
+                       break;
                }
 
                set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
                rcu_read_unlock();
+
+               WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
+                            !ether_addr_equal(sdata->u.mgd.tdls_peer, peer));
+               ret = 0;
                break;
        case NL80211_TDLS_DISABLE_LINK:
-               return sta_info_destroy_addr(sdata, peer);
-       case NL80211_TDLS_TEARDOWN:
-       case NL80211_TDLS_SETUP:
-       case NL80211_TDLS_DISCOVERY_REQ:
-               /* We don't support in-driver setup/teardown/discovery */
-               return -ENOTSUPP;
+               /*
+                * The teardown message in ieee80211_tdls_mgmt_teardown() was
+                * created while the queues were stopped, so it might still be
+                * pending. Before flushing the queues we need to be sure the
+                * message is handled by the tasklet handling pending messages,
+                * otherwise we might start destroying the station before
+                * sending the teardown packet.
+                * Note that this only forces the tasklet to flush pendings -
+                * not to stop the tasklet from rescheduling itself.
+                */
+               tasklet_kill(&local->tx_pending_tasklet);
+               /* flush a potentially queued teardown packet */
+               ieee80211_flush_queues(local, sdata);
+
+               ret = sta_info_destroy_addr(sdata, peer);
+               break;
        default:
-               return -ENOTSUPP;
+               ret = -ENOTSUPP;
+               break;
        }
 
-       return 0;
+       if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
+               cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work);
+               eth_zero_addr(sdata->u.mgd.tdls_peer);
+       }
+
+       mutex_unlock(&local->mtx);
+       return ret;
+}
+
+void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
+                                enum nl80211_tdls_operation oper,
+                                u16 reason_code, gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) {
+               sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n",
+                         oper);
+               return;
+       }
+
+       cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
 }
+EXPORT_SYMBOL(ieee80211_tdls_oper_request);
index cfe1a0688b5ce2846eb906b3f196c5cf51a4590b..02ac535d1274217653e2f9a48c6f6e60ed6d0927 100644 (file)
@@ -1330,6 +1330,13 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
        TP_ARGS(local, sdata)
 );
 
+DEFINE_EVENT(local_sdata_evt, drv_mgd_protect_tdls_discover,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+
+       TP_ARGS(local, sdata)
+);
+
 DECLARE_EVENT_CLASS(local_chanctx,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_chanctx *ctx),
index 1a252c606ad014d8d2e7558e57e0335f9f055ad7..464106c023d8c7d9cb1bd65b39ad642bd1090bc1 100644 (file)
@@ -250,7 +250,8 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
        if (local->hw.conf.flags & IEEE80211_CONF_PS) {
                ieee80211_stop_queues_by_reason(&local->hw,
                                                IEEE80211_MAX_QUEUE_MAP,
-                                               IEEE80211_QUEUE_STOP_REASON_PS);
+                                               IEEE80211_QUEUE_STOP_REASON_PS,
+                                               false);
                ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
                ieee80211_queue_work(&local->hw,
                                     &local->dynamic_ps_disable_work);
@@ -473,7 +474,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                return TX_CONTINUE;
 
        if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) ||
-                     test_sta_flag(sta, WLAN_STA_PS_DRIVER)) &&
+                     test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
+                     test_sta_flag(sta, WLAN_STA_PS_DELIVER)) &&
                     !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {
                int ac = skb_get_queue_mapping(tx->skb);
 
@@ -496,7 +498,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                 * ahead and Tx the packet.
                 */
                if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&
-                   !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
+                   !test_sta_flag(sta, WLAN_STA_PS_DRIVER) &&
+                   !test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
                        spin_unlock(&sta->ps_lock);
                        return TX_CONTINUE;
                }
@@ -1618,12 +1621,12 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct ieee80211_chanctx_conf *chanctx_conf;
-       struct ieee80211_channel *chan;
        struct ieee80211_radiotap_header *prthdr =
                (struct ieee80211_radiotap_header *)skb->data;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr;
        struct ieee80211_sub_if_data *tmp_sdata, *sdata;
+       struct cfg80211_chan_def *chandef;
        u16 len_rthdr;
        int hdrlen;
 
@@ -1721,9 +1724,9 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
        }
 
        if (chanctx_conf)
-               chan = chanctx_conf->def.chan;
+               chandef = &chanctx_conf->def;
        else if (!local->use_chanctx)
-               chan = local->_oper_chandef.chan;
+               chandef = &local->_oper_chandef;
        else
                goto fail_rcu;
 
@@ -1743,10 +1746,11 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
         * radar detection by itself. We can do that later by adding a
         * monitor flag interfaces used for AP support.
         */
-       if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)))
+       if (!cfg80211_reg_can_beacon(local->hw.wiphy, chandef,
+                                    sdata->vif.type))
                goto fail_rcu;
 
-       ieee80211_xmit(sdata, skb, chan->band);
+       ieee80211_xmit(sdata, skb, chandef->chan->band);
        rcu_read_unlock();
 
        return NETDEV_TX_OK;
@@ -1767,15 +1771,12 @@ fail:
 static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
                                              struct sk_buff *skb)
 {
-       struct timespec skb_arv;
        struct ieee80211_tx_latency_bin_ranges *tx_latency;
 
        tx_latency = rcu_dereference(local->tx_latency);
        if (!tx_latency)
                return;
-
-       ktime_get_ts(&skb_arv);
-       skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec);
+       skb->tstamp = ktime_get();
 }
 
 /**
@@ -1810,7 +1811,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        int nh_pos, h_pos;
        struct sta_info *sta = NULL;
        bool wme_sta = false, authorized = false, tdls_auth = false;
-       bool tdls_direct = false;
+       bool tdls_peer = false, tdls_setup_frame = false;
        bool multicast;
        u32 info_flags = 0;
        u16 info_id = 0;
@@ -1952,34 +1953,35 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
 #endif
        case NL80211_IFTYPE_STATION:
                if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
-                       bool tdls_peer = false;
-
                        sta = sta_info_get(sdata, skb->data);
                        if (sta) {
                                authorized = test_sta_flag(sta,
                                                        WLAN_STA_AUTHORIZED);
                                wme_sta = test_sta_flag(sta, WLAN_STA_WME);
                                tdls_peer = test_sta_flag(sta,
-                                                        WLAN_STA_TDLS_PEER);
+                                                         WLAN_STA_TDLS_PEER);
                                tdls_auth = test_sta_flag(sta,
                                                WLAN_STA_TDLS_PEER_AUTH);
                        }
 
-                       /*
-                        * If the TDLS link is enabled, send everything
-                        * directly. Otherwise, allow TDLS setup frames
-                        * to be transmitted indirectly.
-                        */
-                       tdls_direct = tdls_peer && (tdls_auth ||
-                                !(ethertype == ETH_P_TDLS && skb->len > 14 &&
-                                  skb->data[14] == WLAN_TDLS_SNAP_RFTYPE));
+                       if (tdls_peer)
+                               tdls_setup_frame =
+                                       ethertype == ETH_P_TDLS &&
+                                       skb->len > 14 &&
+                                       skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
                }
 
-               if (tdls_direct) {
-                       /* link during setup - throw out frames to peer */
-                       if (!tdls_auth)
-                               goto fail_rcu;
+               /*
+                * TDLS link during setup - throw out frames to peer. We allow
+                * TDLS-setup frames to unauthorized peers for the special case
+                * of a link teardown after a TDLS sta is removed due to being
+                * unreachable.
+                */
+               if (tdls_peer && !tdls_auth && !tdls_setup_frame)
+                       goto fail_rcu;
 
+               /* send direct packets to authorized TDLS peers */
+               if (tdls_peer && tdls_auth) {
                        /* DA SA BSSID */
                        memcpy(hdr.addr1, skb->data, ETH_ALEN);
                        memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
@@ -2423,7 +2425,7 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
        u8 *beacon_data;
        size_t beacon_data_len;
        int i;
-       u8 count = sdata->csa_current_counter;
+       u8 count = beacon->csa_current_counter;
 
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_AP:
@@ -2442,46 +2444,53 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
+       rcu_read_lock();
        for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; ++i) {
-               u16 counter_offset_beacon =
-                       sdata->csa_counter_offset_beacon[i];
-               u16 counter_offset_presp = sdata->csa_counter_offset_presp[i];
+               resp = rcu_dereference(sdata->u.ap.probe_resp);
 
-               if (counter_offset_beacon) {
-                       if (WARN_ON(counter_offset_beacon >= beacon_data_len))
-                               return;
-
-                       beacon_data[counter_offset_beacon] = count;
-               }
-
-               if (sdata->vif.type == NL80211_IFTYPE_AP &&
-                   counter_offset_presp) {
-                       rcu_read_lock();
-                       resp = rcu_dereference(sdata->u.ap.probe_resp);
-
-                       /* If nl80211 accepted the offset, this should
-                        * not happen.
-                        */
-                       if (WARN_ON(!resp)) {
+               if (beacon->csa_counter_offsets[i]) {
+                       if (WARN_ON_ONCE(beacon->csa_counter_offsets[i] >=
+                                        beacon_data_len)) {
                                rcu_read_unlock();
                                return;
                        }
-                       resp->data[counter_offset_presp] = count;
-                       rcu_read_unlock();
+
+                       beacon_data[beacon->csa_counter_offsets[i]] = count;
                }
+
+               if (sdata->vif.type == NL80211_IFTYPE_AP && resp)
+                       resp->data[resp->csa_counter_offsets[i]] = count;
        }
+       rcu_read_unlock();
 }
 
 u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct beacon_data *beacon = NULL;
+       u8 count = 0;
+
+       rcu_read_lock();
 
-       sdata->csa_current_counter--;
+       if (sdata->vif.type == NL80211_IFTYPE_AP)
+               beacon = rcu_dereference(sdata->u.ap.beacon);
+       else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               beacon = rcu_dereference(sdata->u.ibss.presp);
+       else if (ieee80211_vif_is_mesh(&sdata->vif))
+               beacon = rcu_dereference(sdata->u.mesh.beacon);
+
+       if (!beacon)
+               goto unlock;
+
+       beacon->csa_current_counter--;
 
        /* the counter should never reach 0 */
-       WARN_ON(!sdata->csa_current_counter);
+       WARN_ON_ONCE(!beacon->csa_current_counter);
+       count = beacon->csa_current_counter;
 
-       return sdata->csa_current_counter;
+unlock:
+       rcu_read_unlock();
+       return count;
 }
 EXPORT_SYMBOL(ieee80211_csa_update_counter);
 
@@ -2491,7 +2500,6 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
        struct beacon_data *beacon = NULL;
        u8 *beacon_data;
        size_t beacon_data_len;
-       int counter_beacon = sdata->csa_counter_offset_beacon[0];
        int ret = false;
 
        if (!ieee80211_sdata_running(sdata))
@@ -2529,10 +2537,13 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
                goto out;
        }
 
-       if (WARN_ON(counter_beacon > beacon_data_len))
+       if (!beacon->csa_counter_offsets[0])
                goto out;
 
-       if (beacon_data[counter_beacon] == 1)
+       if (WARN_ON_ONCE(beacon->csa_counter_offsets[0] > beacon_data_len))
+               goto out;
+
+       if (beacon_data[beacon->csa_counter_offsets[0]] == 1)
                ret = true;
  out:
        rcu_read_unlock();
@@ -2548,6 +2559,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                       bool is_template)
 {
        struct ieee80211_local *local = hw_to_local(hw);
+       struct beacon_data *beacon = NULL;
        struct sk_buff *skb = NULL;
        struct ieee80211_tx_info *info;
        struct ieee80211_sub_if_data *sdata = NULL;
@@ -2569,10 +2581,10 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
 
        if (sdata->vif.type == NL80211_IFTYPE_AP) {
                struct ieee80211_if_ap *ap = &sdata->u.ap;
-               struct beacon_data *beacon = rcu_dereference(ap->beacon);
 
+               beacon = rcu_dereference(ap->beacon);
                if (beacon) {
-                       if (sdata->vif.csa_active) {
+                       if (beacon->csa_counter_offsets[0]) {
                                if (!is_template)
                                        ieee80211_csa_update_counter(vif);
 
@@ -2613,37 +2625,37 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
        } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
                struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
                struct ieee80211_hdr *hdr;
-               struct beacon_data *presp = rcu_dereference(ifibss->presp);
 
-               if (!presp)
+               beacon = rcu_dereference(ifibss->presp);
+               if (!beacon)
                        goto out;
 
-               if (sdata->vif.csa_active) {
+               if (beacon->csa_counter_offsets[0]) {
                        if (!is_template)
                                ieee80211_csa_update_counter(vif);
 
-                       ieee80211_set_csa(sdata, presp);
+                       ieee80211_set_csa(sdata, beacon);
                }
 
-               skb = dev_alloc_skb(local->tx_headroom + presp->head_len +
+               skb = dev_alloc_skb(local->tx_headroom + beacon->head_len +
                                    local->hw.extra_beacon_tailroom);
                if (!skb)
                        goto out;
                skb_reserve(skb, local->tx_headroom);
-               memcpy(skb_put(skb, presp->head_len), presp->head,
-                      presp->head_len);
+               memcpy(skb_put(skb, beacon->head_len), beacon->head,
+                      beacon->head_len);
 
                hdr = (struct ieee80211_hdr *) skb->data;
                hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                                 IEEE80211_STYPE_BEACON);
        } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
                struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
-               struct beacon_data *bcn = rcu_dereference(ifmsh->beacon);
 
-               if (!bcn)
+               beacon = rcu_dereference(ifmsh->beacon);
+               if (!beacon)
                        goto out;
 
-               if (sdata->vif.csa_active) {
+               if (beacon->csa_counter_offsets[0]) {
                        if (!is_template)
                                /* TODO: For mesh csa_counter is in TU, so
                                 * decrementing it by one isn't correct, but
@@ -2652,40 +2664,42 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
                                 */
                                ieee80211_csa_update_counter(vif);
 
-                       ieee80211_set_csa(sdata, bcn);
+                       ieee80211_set_csa(sdata, beacon);
                }
 
                if (ifmsh->sync_ops)
-                       ifmsh->sync_ops->adjust_tbtt(sdata, bcn);
+                       ifmsh->sync_ops->adjust_tbtt(sdata, beacon);
 
                skb = dev_alloc_skb(local->tx_headroom +
-                                   bcn->head_len +
+                                   beacon->head_len +
                                    256 + /* TIM IE */
-                                   bcn->tail_len +
+                                   beacon->tail_len +
                                    local->hw.extra_beacon_tailroom);
                if (!skb)
                        goto out;
                skb_reserve(skb, local->tx_headroom);
-               memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
+               memcpy(skb_put(skb, beacon->head_len), beacon->head,
+                      beacon->head_len);
                ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb, is_template);
 
                if (offs) {
-                       offs->tim_offset = bcn->head_len;
-                       offs->tim_length = skb->len - bcn->head_len;
+                       offs->tim_offset = beacon->head_len;
+                       offs->tim_length = skb->len - beacon->head_len;
                }
 
-               memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
+               memcpy(skb_put(skb, beacon->tail_len), beacon->tail,
+                      beacon->tail_len);
        } else {
                WARN_ON(1);
                goto out;
        }
 
        /* CSA offsets */
-       if (offs) {
+       if (offs && beacon) {
                int i;
 
                for (i = 0; i < IEEE80211_MAX_CSA_COUNTERS_NUM; i++) {
-                       u16 csa_off = sdata->csa_counter_offset_beacon[i];
+                       u16 csa_off = beacon->csa_counter_offsets[i];
 
                        if (!csa_off)
                                continue;
index a6cda52ed9203e55047841f1b4a62ab301ecb26e..725af7a468d233b502a6de8a07e30d7a25d60e66 100644 (file)
@@ -317,7 +317,8 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
 }
 
 static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
-                                  enum queue_stop_reason reason)
+                                  enum queue_stop_reason reason,
+                                  bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
@@ -329,7 +330,13 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
        if (!test_bit(reason, &local->queue_stop_reasons[queue]))
                return;
 
-       __clear_bit(reason, &local->queue_stop_reasons[queue]);
+       if (!refcounted)
+               local->q_stop_reasons[queue][reason] = 0;
+       else
+               local->q_stop_reasons[queue][reason]--;
+
+       if (local->q_stop_reasons[queue][reason] == 0)
+               __clear_bit(reason, &local->queue_stop_reasons[queue]);
 
        if (local->queue_stop_reasons[queue] != 0)
                /* someone still has this queue stopped */
@@ -344,25 +351,28 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
-                                   enum queue_stop_reason reason)
+                                   enum queue_stop_reason reason,
+                                   bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-       __ieee80211_wake_queue(hw, queue, reason);
+       __ieee80211_wake_queue(hw, queue, reason, refcounted);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
 void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
 {
        ieee80211_wake_queue_by_reason(hw, queue,
-                                      IEEE80211_QUEUE_STOP_REASON_DRIVER);
+                                      IEEE80211_QUEUE_STOP_REASON_DRIVER,
+                                      false);
 }
 EXPORT_SYMBOL(ieee80211_wake_queue);
 
 static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
-                                  enum queue_stop_reason reason)
+                                  enum queue_stop_reason reason,
+                                  bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
@@ -373,10 +383,13 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
        if (WARN_ON(queue >= hw->queues))
                return;
 
-       if (test_bit(reason, &local->queue_stop_reasons[queue]))
-               return;
+       if (!refcounted)
+               local->q_stop_reasons[queue][reason] = 1;
+       else
+               local->q_stop_reasons[queue][reason]++;
 
-       __set_bit(reason, &local->queue_stop_reasons[queue]);
+       if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue]))
+               return;
 
        if (local->hw.queues < IEEE80211_NUM_ACS)
                n_acs = 1;
@@ -398,20 +411,22 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
 }
 
 void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
-                                   enum queue_stop_reason reason)
+                                   enum queue_stop_reason reason,
+                                   bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-       __ieee80211_stop_queue(hw, queue, reason);
+       __ieee80211_stop_queue(hw, queue, reason, refcounted);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
 void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue)
 {
        ieee80211_stop_queue_by_reason(hw, queue,
-                                      IEEE80211_QUEUE_STOP_REASON_DRIVER);
+                                      IEEE80211_QUEUE_STOP_REASON_DRIVER,
+                                      false);
 }
 EXPORT_SYMBOL(ieee80211_stop_queue);
 
@@ -429,9 +444,11 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
        }
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-       __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+                              false);
        __skb_queue_tail(&local->pending[queue], skb);
-       __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+       __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+                              false);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
@@ -455,20 +472,23 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
                queue = info->hw_queue;
 
                __ieee80211_stop_queue(hw, queue,
-                               IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+                               IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+                               false);
 
                __skb_queue_tail(&local->pending[queue], skb);
        }
 
        for (i = 0; i < hw->queues; i++)
                __ieee80211_wake_queue(hw, i,
-                       IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
+                       IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
+                       false);
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
 
 void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
-                                    enum queue_stop_reason reason)
+                                    enum queue_stop_reason reason,
+                                    bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
@@ -477,7 +497,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
        for_each_set_bit(i, &queues, hw->queues)
-               __ieee80211_stop_queue(hw, i, reason);
+               __ieee80211_stop_queue(hw, i, reason, refcounted);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
@@ -485,7 +505,8 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
 void ieee80211_stop_queues(struct ieee80211_hw *hw)
 {
        ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_DRIVER);
+                                       IEEE80211_QUEUE_STOP_REASON_DRIVER,
+                                       false);
 }
 EXPORT_SYMBOL(ieee80211_stop_queues);
 
@@ -508,7 +529,8 @@ EXPORT_SYMBOL(ieee80211_queue_stopped);
 
 void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
                                     unsigned long queues,
-                                    enum queue_stop_reason reason)
+                                    enum queue_stop_reason reason,
+                                    bool refcounted)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        unsigned long flags;
@@ -517,7 +539,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
        for_each_set_bit(i, &queues, hw->queues)
-               __ieee80211_wake_queue(hw, i, reason);
+               __ieee80211_wake_queue(hw, i, reason, refcounted);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
@@ -525,17 +547,16 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 void ieee80211_wake_queues(struct ieee80211_hw *hw)
 {
        ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_DRIVER);
+                                       IEEE80211_QUEUE_STOP_REASON_DRIVER,
+                                       false);
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
-void ieee80211_flush_queues(struct ieee80211_local *local,
-                           struct ieee80211_sub_if_data *sdata)
+static unsigned int
+ieee80211_get_vif_queues(struct ieee80211_local *local,
+                        struct ieee80211_sub_if_data *sdata)
 {
-       u32 queues;
-
-       if (!local->ops->flush)
-               return;
+       unsigned int queues;
 
        if (sdata && local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) {
                int ac;
@@ -551,13 +572,46 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
                queues = BIT(local->hw.queues) - 1;
        }
 
-       ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_FLUSH);
+       return queues;
+}
+
+void ieee80211_flush_queues(struct ieee80211_local *local,
+                           struct ieee80211_sub_if_data *sdata)
+{
+       unsigned int queues;
+
+       if (!local->ops->flush)
+               return;
+
+       queues = ieee80211_get_vif_queues(local, sdata);
+
+       ieee80211_stop_queues_by_reason(&local->hw, queues,
+                                       IEEE80211_QUEUE_STOP_REASON_FLUSH,
+                                       false);
 
        drv_flush(local, sdata, queues, false);
 
-       ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_FLUSH);
+       ieee80211_wake_queues_by_reason(&local->hw, queues,
+                                       IEEE80211_QUEUE_STOP_REASON_FLUSH,
+                                       false);
+}
+
+void ieee80211_stop_vif_queues(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata,
+                              enum queue_stop_reason reason)
+{
+       ieee80211_stop_queues_by_reason(&local->hw,
+                                       ieee80211_get_vif_queues(local, sdata),
+                                       reason, true);
+}
+
+void ieee80211_wake_vif_queues(struct ieee80211_local *local,
+                              struct ieee80211_sub_if_data *sdata,
+                              enum queue_stop_reason reason)
+{
+       ieee80211_wake_queues_by_reason(&local->hw,
+                                       ieee80211_get_vif_queues(local, sdata),
+                                       reason, true);
 }
 
 static void __iterate_active_interfaces(struct ieee80211_local *local,
@@ -1166,14 +1220,17 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
        }
 }
 
-int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
-                            size_t buffer_len, const u8 *ie, size_t ie_len,
-                            enum ieee80211_band band, u32 rate_mask,
-                            struct cfg80211_chan_def *chandef)
+static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
+                                        u8 *buffer, size_t buffer_len,
+                                        const u8 *ie, size_t ie_len,
+                                        enum ieee80211_band band,
+                                        u32 rate_mask,
+                                        struct cfg80211_chan_def *chandef,
+                                        size_t *offset)
 {
        struct ieee80211_supported_band *sband;
        u8 *pos = buffer, *end = buffer + buffer_len;
-       size_t offset = 0, noffset;
+       size_t noffset;
        int supp_rates_len, i;
        u8 rates[32];
        int num_rates;
@@ -1181,6 +1238,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
        int shift;
        u32 rate_flags;
 
+       *offset = 0;
+
        sband = local->hw.wiphy->bands[band];
        if (WARN_ON_ONCE(!sband))
                return 0;
@@ -1219,12 +1278,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_extrates,
                                             ARRAY_SIZE(before_extrates),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        ext_rates_len = num_rates - supp_rates_len;
@@ -1258,12 +1317,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                };
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_ht, ARRAY_SIZE(before_ht),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        if (sband->ht_cap.ht_supported) {
@@ -1298,12 +1357,12 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                };
                noffset = ieee80211_ie_split(ie, ie_len,
                                             before_vht, ARRAY_SIZE(before_vht),
-                                            offset);
-               if (end - pos < noffset - offset)
+                                            *offset);
+               if (end - pos < noffset - *offset)
                        goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-               offset = noffset;
+               memcpy(pos, ie + *offset, noffset - *offset);
+               pos += noffset - *offset;
+               *offset = noffset;
        }
 
        if (sband->vht_cap.vht_supported) {
@@ -1313,21 +1372,54 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                                                 sband->vht_cap.cap);
        }
 
-       /* add any remaining custom IEs */
-       if (ie && ie_len) {
-               noffset = ie_len;
-               if (end - pos < noffset - offset)
-                       goto out_err;
-               memcpy(pos, ie + offset, noffset - offset);
-               pos += noffset - offset;
-       }
-
        return pos - buffer;
  out_err:
        WARN_ONCE(1, "not enough space for preq IEs\n");
        return pos - buffer;
 }
 
+int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+                            size_t buffer_len,
+                            struct ieee80211_scan_ies *ie_desc,
+                            const u8 *ie, size_t ie_len,
+                            u8 bands_used, u32 *rate_masks,
+                            struct cfg80211_chan_def *chandef)
+{
+       size_t pos = 0, old_pos = 0, custom_ie_offset = 0;
+       int i;
+
+       memset(ie_desc, 0, sizeof(*ie_desc));
+
+       for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+               if (bands_used & BIT(i)) {
+                       pos += ieee80211_build_preq_ies_band(local,
+                                                            buffer + pos,
+                                                            buffer_len - pos,
+                                                            ie, ie_len, i,
+                                                            rate_masks[i],
+                                                            chandef,
+                                                            &custom_ie_offset);
+                       ie_desc->ies[i] = buffer + old_pos;
+                       ie_desc->len[i] = pos - old_pos;
+                       old_pos = pos;
+               }
+       }
+
+       /* add any remaining custom IEs */
+       if (ie && ie_len) {
+               if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset,
+                             "not enough space for preq custom IEs\n"))
+                       return pos;
+               memcpy(buffer + pos, ie + custom_ie_offset,
+                      ie_len - custom_ie_offset);
+               ie_desc->common_ies = buffer + pos;
+               ie_desc->common_ie_len = ie_len - custom_ie_offset;
+               pos += ie_len - custom_ie_offset;
+       }
+
+       return pos;
+};
+
 struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
                                          u8 *dst, u32 ratemask,
                                          struct ieee80211_channel *chan,
@@ -1340,6 +1432,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
        int ies_len;
+       u32 rate_masks[IEEE80211_NUM_BANDS] = {};
+       struct ieee80211_scan_ies dummy_ie_desc;
 
        /*
         * Do not send DS Channel parameter for directed probe requests
@@ -1357,10 +1451,11 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
        if (!skb)
                return NULL;
 
+       rate_masks[chan->band] = ratemask;
        ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
-                                          skb_tailroom(skb),
-                                          ie, ie_len, chan->band,
-                                          ratemask, &chandef);
+                                          skb_tailroom(skb), &dummy_ie_desc,
+                                          ie, ie_len, BIT(chan->band),
+                                          rate_masks, &chandef);
        skb_put(skb, ies_len);
 
        if (dst) {
@@ -1604,7 +1699,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        if (local->use_chanctx) {
                mutex_lock(&local->chanctx_mtx);
                list_for_each_entry(ctx, &local->chanctx_list, list)
-                       WARN_ON(drv_add_chanctx(local, ctx));
+                       if (ctx->replace_state !=
+                           IEEE80211_CHANCTX_REPLACES_OTHER)
+                               WARN_ON(drv_add_chanctx(local, ctx));
                mutex_unlock(&local->chanctx_mtx);
 
                list_for_each_entry(sdata, &local->interfaces, list) {
@@ -1798,7 +1895,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        }
 
        ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP,
-                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+                                       IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+                                       false);
 
        /*
         * Reconfigure sched scan if it was interrupted by FW restart or
@@ -2836,6 +2934,35 @@ void ieee80211_recalc_dtim(struct ieee80211_local *local,
        ps->dtim_count = dtim_count;
 }
 
+static u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local,
+                                        struct ieee80211_chanctx *ctx)
+{
+       struct ieee80211_sub_if_data *sdata;
+       u8 radar_detect = 0;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED))
+               return 0;
+
+       list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list)
+               if (sdata->reserved_radar_required)
+                       radar_detect |= BIT(sdata->reserved_chandef.width);
+
+       /*
+        * An in-place reservation context should not have any assigned vifs
+        * until it replaces the other context.
+        */
+       WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER &&
+               !list_empty(&ctx->assigned_vifs));
+
+       list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list)
+               if (sdata->radar_required)
+                       radar_detect |= BIT(sdata->vif.bss_conf.chandef.width);
+
+       return radar_detect;
+}
+
 int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
                                 const struct cfg80211_chan_def *chandef,
                                 enum ieee80211_chanctx_mode chanmode,
@@ -2877,8 +3004,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
                num[iftype] = 1;
 
        list_for_each_entry(ctx, &local->chanctx_list, list) {
-               if (ctx->conf.radar_enabled)
-                       radar_detect |= BIT(ctx->conf.def.width);
+               if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                       continue;
+               radar_detect |= ieee80211_chanctx_radar_detect(local, ctx);
                if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
                        num_different_channels++;
                        continue;
@@ -2935,10 +3063,12 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
        lockdep_assert_held(&local->chanctx_mtx);
 
        list_for_each_entry(ctx, &local->chanctx_list, list) {
+               if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
+                       continue;
+
                num_different_channels++;
 
-               if (ctx->conf.radar_enabled)
-                       radar_detect |= BIT(ctx->conf.def.width);
+               radar_detect |= ieee80211_chanctx_radar_detect(local, ctx);
        }
 
        list_for_each_entry_rcu(sdata, &local->interfaces, list)
@@ -2953,3 +3083,18 @@ int ieee80211_max_num_channels(struct ieee80211_local *local)
 
        return max_num_different_channels;
 }
+
+u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo)
+{
+       *buf++ = WLAN_EID_VENDOR_SPECIFIC;
+       *buf++ = 7; /* len */
+       *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */
+       *buf++ = 0x50;
+       *buf++ = 0xf2;
+       *buf++ = 2; /* WME */
+       *buf++ = 0; /* WME info */
+       *buf++ = 1; /* WME ver */
+       *buf++ = qosinfo; /* U-APSD no in use */
+
+       return buf;
+}
index 9265adfdabfcf99acbdf1598067884188c500a49..671ce0d27a80b01bfa5d457ef6134a19d45ee208 100644 (file)
@@ -129,6 +129,10 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
        if (!vht_cap_ie || !sband->vht_cap.vht_supported)
                return;
 
+       /* don't support VHT for TDLS peers for now */
+       if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
+               return;
+
        /*
         * A VHT STA must support 40 MHz, but if we verify that here
         * then we break a few things - some APs (e.g. Netgear R6300v2
index 6ee2b586357275aa33c13454b37d15b970cf1181..9181fb6d643786788abfed37d3ed3d5130e3cb3e 100644 (file)
@@ -271,22 +271,6 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,
        return ret;
 }
 
-
-static bool ieee80211_wep_is_weak_iv(struct sk_buff *skb,
-                                    struct ieee80211_key *key)
-{
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       unsigned int hdrlen;
-       u8 *ivpos;
-       u32 iv;
-
-       hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       ivpos = skb->data + hdrlen;
-       iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2];
-
-       return ieee80211_wep_weak_iv(iv, key->conf.keylen);
-}
-
 ieee80211_rx_result
 ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
 {
@@ -301,16 +285,12 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
        if (!(status->flag & RX_FLAG_DECRYPTED)) {
                if (skb_linearize(rx->skb))
                        return RX_DROP_UNUSABLE;
-               if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
-                       rx->sta->wep_weak_iv_count++;
                if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))
                        return RX_DROP_UNUSABLE;
        } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
                if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) +
                                            IEEE80211_WEP_IV_LEN))
                        return RX_DROP_UNUSABLE;
-               if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
-                       rx->sta->wep_weak_iv_count++;
                ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
                /* remove ICV */
                if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
index 9b3dcc201145dd3942bf30771e1638ea973759f7..f7d4ca4c46e0bf68c6232d339d4b54f7934798ba 100644 (file)
@@ -811,7 +811,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
 ieee80211_rx_result
 ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx)
 {
-       if (rx->sta->cipher_scheme)
+       if (rx->sta && rx->sta->cipher_scheme)
                return ieee80211_crypto_cs_decrypt(rx);
 
        return RX_DROP_UNUSABLE;
index 405f3c4cf70ca3617a4101e1bad278b93d7ae1b7..29c8675f9a1189db65c185f2ad04f96a67702989 100644 (file)
@@ -162,6 +162,12 @@ config CFG80211_INTERNAL_REGDB
          and includes code to query that database.  This is an alternative
          to using CRDA for defining regulatory rules for the kernel.
 
+         Using this option requires some parsing of the db.txt at build time,
+         the parser will be upkept with the latest wireless-regdb updates but
+         older wireless-regdb formats will be ignored. The parser may later
+         be replaced to avoid issues with conflicts on versions of
+         wireless-regdb.
+
          For details see:
 
          http://wireless.kernel.org/en/developers/Regulatory
index a1c40654dd9b1ca9b47bbaef6682ee6d49147e66..afee5e0455ea460b9b3d2e39c27e7eb0d881fa9f 100644 (file)
@@ -25,7 +25,6 @@
 #include "sysfs.h"
 #include "debugfs.h"
 #include "wext-compat.h"
-#include "ethtool.h"
 #include "rdev-ops.h"
 
 /* name for sysfs, %d is appended */
@@ -927,8 +926,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                /* allow mac80211 to determine the timeout */
                wdev->ps_timeout = -1;
 
-               netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops);
-
                if ((wdev->iftype == NL80211_IFTYPE_STATION ||
                     wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
                     wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
index d4860bfc020e5a1c43758e8cca6e9508908344be..e9e91298c70de7bdab61b31b795df213c5a71461 100644 (file)
@@ -1,11 +1,9 @@
 #include <linux/utsname.h>
 #include <net/cfg80211.h>
 #include "core.h"
-#include "ethtool.h"
 #include "rdev-ops.h"
 
-static void cfg80211_get_drvinfo(struct net_device *dev,
-                                       struct ethtool_drvinfo *info)
+void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
 
@@ -23,84 +21,4 @@ static void cfg80211_get_drvinfo(struct net_device *dev,
        strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)),
                sizeof(info->bus_info));
 }
-
-static int cfg80211_get_regs_len(struct net_device *dev)
-{
-       /* For now, return 0... */
-       return 0;
-}
-
-static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs,
-                       void *data)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-
-       regs->version = wdev->wiphy->hw_version;
-       regs->len = 0;
-}
-
-static void cfg80211_get_ringparam(struct net_device *dev,
-                                  struct ethtool_ringparam *rp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-
-       memset(rp, 0, sizeof(*rp));
-
-       if (rdev->ops->get_ringparam)
-               rdev_get_ringparam(rdev, &rp->tx_pending, &rp->tx_max_pending,
-                                  &rp->rx_pending, &rp->rx_max_pending);
-}
-
-static int cfg80211_set_ringparam(struct net_device *dev,
-                                 struct ethtool_ringparam *rp)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-
-       if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
-               return -EINVAL;
-
-       if (rdev->ops->set_ringparam)
-               return rdev_set_ringparam(rdev, rp->tx_pending, rp->rx_pending);
-
-       return -ENOTSUPP;
-}
-
-static int cfg80211_get_sset_count(struct net_device *dev, int sset)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-       if (rdev->ops->get_et_sset_count)
-               return rdev_get_et_sset_count(rdev, dev, sset);
-       return -EOPNOTSUPP;
-}
-
-static void cfg80211_get_stats(struct net_device *dev,
-                              struct ethtool_stats *stats, u64 *data)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-       if (rdev->ops->get_et_stats)
-               rdev_get_et_stats(rdev, dev, stats, data);
-}
-
-static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
-{
-       struct wireless_dev *wdev = dev->ieee80211_ptr;
-       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-       if (rdev->ops->get_et_strings)
-               rdev_get_et_strings(rdev, dev, sset, data);
-}
-
-const struct ethtool_ops cfg80211_ethtool_ops = {
-       .get_drvinfo = cfg80211_get_drvinfo,
-       .get_regs_len = cfg80211_get_regs_len,
-       .get_regs = cfg80211_get_regs,
-       .get_link = ethtool_op_get_link,
-       .get_ringparam = cfg80211_get_ringparam,
-       .set_ringparam = cfg80211_set_ringparam,
-       .get_strings = cfg80211_get_strings,
-       .get_ethtool_stats = cfg80211_get_stats,
-       .get_sset_count = cfg80211_get_sset_count,
-};
+EXPORT_SYMBOL(cfg80211_get_drvinfo);
diff --git a/net/wireless/ethtool.h b/net/wireless/ethtool.h
deleted file mode 100644 (file)
index 695ecad..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __CFG80211_ETHTOOL__
-#define __CFG80211_ETHTOOL__
-
-extern const struct ethtool_ops cfg80211_ethtool_ops;
-
-#endif /* __CFG80211_ETHTOOL__ */
index 40c37fc5b67cb76325dcd4f0041c1ad3cf5ebf66..baf2426b555a31c7aeb2fbaa48205c9b422f0cbf 100644 (file)
@@ -51,32 +51,41 @@ function parse_country_head() {
 
 function parse_reg_rule()
 {
+       flag_starts_at = 7
+
        start = $1
        sub(/\(/, "", start)
        end = $3
        bw = $5
        sub(/\),/, "", bw)
-       gain = $6
-       sub(/\(/, "", gain)
-       sub(/,/, "", gain)
-       power = $7
-       sub(/\)/, "", power)
-       sub(/,/, "", power)
+       gain = 0
+       power = $6
        # power might be in mW...
-       units = $8
+       units = $7
+       dfs_cac = 0
+
+       sub(/\(/, "", power)
+       sub(/\),/, "", power)
+       sub(/\),/, "", units)
        sub(/\)/, "", units)
-       sub(/,/, "", units)
-       dfs_cac = $9
+
        if (units == "mW") {
+               flag_starts_at = 8
                power = 10 * log(power)/log(10)
+               if ($8 ~ /[[:digit:]]/) {
+                       flag_starts_at = 9
+                       dfs_cac = $8
+               }
        } else {
-               dfs_cac = $8
+               if ($7 ~ /[[:digit:]]/) {
+                       flag_starts_at = 8
+                       dfs_cac = $7
+               }
        }
-       sub(/,/, "", dfs_cac)
        sub(/\(/, "", dfs_cac)
-       sub(/\)/, "", dfs_cac)
+       sub(/\),/, "", dfs_cac)
        flagstr = ""
-       for (i=8; i<=NF; i++)
+       for (i=flag_starts_at; i<=NF; i++)
                flagstr = flagstr $i
        split(flagstr, flagarray, ",")
        flags = ""
index 6668daf6932667bee1f80f6d4c7bdcefef36346c..df7b1332a1ec2fa80834b94c233f38a427a0f350 100644 (file)
@@ -337,6 +337,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
        [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
        [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
+       [NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG },
        [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
        [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
                                      .len = IEEE80211_MAX_DATA_LEN },
@@ -3813,7 +3814,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
 {
        if (params->listen_interval != -1)
                return -EINVAL;
-       if (params->aid)
+       if (params->aid &&
+           !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
                return -EINVAL;
 
        /* When you run into this, adjust the code below for the new flag */
@@ -6011,17 +6013,6 @@ skip_beacons:
                params.radar_required = true;
        }
 
-       /* TODO: I left this here for now.  With channel switch, the
-        * verification is a bit more complicated, because we only do
-        * it later when the channel switch really happens.
-        */
-       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
-                                          params.chandef.chan,
-                                          CHAN_MODE_SHARED,
-                                          radar_detect_width);
-       if (err)
-               return err;
-
        if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
                params.block_tx = true;
 
@@ -7364,6 +7355,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
        u32 peer_capability = 0;
        u16 status_code;
        u8 *peer;
+       bool initiator;
 
        if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
            !rdev->ops->tdls_mgmt)
@@ -7380,12 +7372,14 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
        action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
        status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
        dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
+       initiator = nla_get_flag(info->attrs[NL80211_ATTR_TDLS_INITIATOR]);
        if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
                peer_capability =
                        nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
 
        return rdev_tdls_mgmt(rdev, dev, peer, action_code,
                              dialog_token, status_code, peer_capability,
+                             initiator,
                              nla_data(info->attrs[NL80211_ATTR_IE]),
                              nla_len(info->attrs[NL80211_ATTR_IE]));
 }
index d95bbe34813833d04ed618fcd6ca85700718216b..56c2240c30cefcc5a386b4da67a0e49686e47e4a 100644 (file)
@@ -714,25 +714,6 @@ static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
-static inline int rdev_set_ringparam(struct cfg80211_registered_device *rdev,
-                                    u32 tx, u32 rx)
-{
-       int ret;
-       trace_rdev_set_ringparam(&rdev->wiphy, tx, rx);
-       ret = rdev->ops->set_ringparam(&rdev->wiphy, tx, rx);
-       trace_rdev_return_int(&rdev->wiphy, ret);
-       return ret;
-}
-
-static inline void rdev_get_ringparam(struct cfg80211_registered_device *rdev,
-                                     u32 *tx, u32 *tx_max, u32 *rx,
-                                     u32 *rx_max)
-{
-       trace_rdev_get_ringparam(&rdev->wiphy);
-       rdev->ops->get_ringparam(&rdev->wiphy, tx, tx_max, rx, rx_max);
-       trace_rdev_return_void_tx_rx(&rdev->wiphy, *tx, *tx_max, *rx, *rx_max);
-}
-
 static inline int
 rdev_sched_scan_start(struct cfg80211_registered_device *rdev,
                      struct net_device *dev,
@@ -770,15 +751,15 @@ static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
                                 struct net_device *dev, u8 *peer,
                                 u8 action_code, u8 dialog_token,
                                 u16 status_code, u32 peer_capability,
-                                const u8 *buf, size_t len)
+                                bool initiator, const u8 *buf, size_t len)
 {
        int ret;
        trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
                             dialog_token, status_code, peer_capability,
-                            buf, len);
+                            initiator, buf, len);
        ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
                                   dialog_token, status_code, peer_capability,
-                                  buf, len);
+                                  initiator, buf, len);
        trace_rdev_return_int(&rdev->wiphy, ret);
        return ret;
 }
@@ -815,35 +796,6 @@ static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev,
        return ret;
 }
 
-static inline int
-rdev_get_et_sset_count(struct cfg80211_registered_device *rdev,
-                      struct net_device *dev, int sset)
-{
-       int ret;
-       trace_rdev_get_et_sset_count(&rdev->wiphy, dev, sset);
-       ret = rdev->ops->get_et_sset_count(&rdev->wiphy, dev, sset);
-       trace_rdev_return_int(&rdev->wiphy, ret);
-       return ret;
-}
-
-static inline void rdev_get_et_stats(struct cfg80211_registered_device *rdev,
-                                    struct net_device *dev,
-                                    struct ethtool_stats *stats, u64 *data)
-{
-       trace_rdev_get_et_stats(&rdev->wiphy, dev);
-       rdev->ops->get_et_stats(&rdev->wiphy, dev, stats, data);
-       trace_rdev_return_void(&rdev->wiphy);
-}
-
-static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev,
-                                      struct net_device *dev, u32 sset,
-                                      u8 *data)
-{
-       trace_rdev_get_et_strings(&rdev->wiphy, dev, sset);
-       rdev->ops->get_et_strings(&rdev->wiphy, dev, sset, data);
-       trace_rdev_return_void(&rdev->wiphy);
-}
-
 static inline int
 rdev_get_channel(struct cfg80211_registered_device *rdev,
                 struct wireless_dev *wdev,
index 7cc887f9da11d74e3dd5ac5592ccb7f65b900771..0c524cd76c837ccf23a070a802e1ab405fd235f4 100644 (file)
@@ -298,11 +298,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_return_void,
        TP_ARGS(wiphy)
 );
 
-DEFINE_EVENT(wiphy_only_evt, rdev_get_ringparam,
-       TP_PROTO(struct wiphy *wiphy),
-       TP_ARGS(wiphy)
-);
-
 DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna,
        TP_PROTO(struct wiphy *wiphy),
        TP_ARGS(wiphy)
@@ -580,11 +575,6 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap,
        TP_ARGS(wiphy, netdev)
 );
 
-DEFINE_EVENT(wiphy_netdev_evt, rdev_get_et_stats,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
-       TP_ARGS(wiphy, netdev)
-);
-
 DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop,
        TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
        TP_ARGS(wiphy, netdev)
@@ -1439,11 +1429,6 @@ DECLARE_EVENT_CLASS(tx_rx_evt,
                  WIPHY_PR_ARG, __entry->tx, __entry->rx)
 );
 
-DEFINE_EVENT(tx_rx_evt, rdev_set_ringparam,
-       TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
-       TP_ARGS(wiphy, rx, tx)
-);
-
 DEFINE_EVENT(tx_rx_evt, rdev_set_antenna,
        TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
        TP_ARGS(wiphy, rx, tx)
@@ -1469,9 +1454,9 @@ TRACE_EVENT(rdev_tdls_mgmt,
        TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
                 u8 *peer, u8 action_code, u8 dialog_token,
                 u16 status_code, u32 peer_capability,
-                const u8 *buf, size_t len),
+                bool initiator, const u8 *buf, size_t len),
        TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
-               peer_capability, buf, len),
+               peer_capability, initiator, buf, len),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                NETDEV_ENTRY
@@ -1480,6 +1465,7 @@ TRACE_EVENT(rdev_tdls_mgmt,
                __field(u8, dialog_token)
                __field(u16, status_code)
                __field(u32, peer_capability)
+               __field(bool, initiator)
                __dynamic_array(u8, buf, len)
        ),
        TP_fast_assign(
@@ -1490,13 +1476,16 @@ TRACE_EVENT(rdev_tdls_mgmt,
                __entry->dialog_token = dialog_token;
                __entry->status_code = status_code;
                __entry->peer_capability = peer_capability;
+               __entry->initiator = initiator;
                memcpy(__get_dynamic_array(buf), buf, len);
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
-                 "dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ",
+                 "dialog_token: %u, status_code: %u, peer_capability: %u "
+                 "initiator: %s buf: %#.2x ",
                  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
                  __entry->action_code, __entry->dialog_token,
                  __entry->status_code, __entry->peer_capability,
+                 BOOL_TO_STR(__entry->initiator),
                  ((u8 *)__get_dynamic_array(buf))[0])
 );
 
@@ -1725,40 +1714,6 @@ TRACE_EVENT(rdev_set_noack_map,
                  WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map)
 );
 
-TRACE_EVENT(rdev_get_et_sset_count,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int sset),
-       TP_ARGS(wiphy, netdev, sset),
-       TP_STRUCT__entry(
-               WIPHY_ENTRY
-               NETDEV_ENTRY
-               __field(int, sset)
-       ),
-       TP_fast_assign(
-               WIPHY_ASSIGN;
-               NETDEV_ASSIGN;
-               __entry->sset = sset;
-       ),
-       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d",
-                 WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
-);
-
-TRACE_EVENT(rdev_get_et_strings,
-       TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 sset),
-       TP_ARGS(wiphy, netdev, sset),
-       TP_STRUCT__entry(
-               WIPHY_ENTRY
-               NETDEV_ENTRY
-               __field(u32, sset)
-       ),
-       TP_fast_assign(
-               WIPHY_ASSIGN;
-               NETDEV_ASSIGN;
-               __entry->sset = sset;
-       ),
-       TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u",
-                 WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
-);
-
 DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel,
        TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
        TP_ARGS(wiphy, wdev)