Merge tag 'master-2014-07-25' of git://git.kernel.org/pub/scm/linux/kernel/git/linvil...
authorDavid S. Miller <davem@davemloft.net>
Tue, 29 Jul 2014 00:36:25 +0000 (17:36 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 29 Jul 2014 00:36:25 +0000 (17:36 -0700)
John W. Linville says:

====================
pull request: wireless-next 2014-07-25

Please pull this batch of updates intended for the 3.17 stream!

For the mac80211 bits, Johannes says:

"We have a lot of TDLS patches, among them a fix that should make hwsim
tests happy again. The rest, this time, is mostly small fixes."

For the Bluetooth bits, Gustavo says:

"Some more patches for 3.17. The most important change here is the move of
the 6lowpan code to net/6lowpan. It has been agreed with Davem that this
change will go through the bluetooth tree. The rest are mostly clean up and
fixes."

and,

"Here follows some more patches for 3.17. These are mostly fixes to what
we've sent to you before for next merge window."

For the iwlwifi bits, Emmanuel says:

"I have the usual amount of BT Coex stuff. Arik continues to work
on TDLS and Ariej contributes a few things for HS2.0. I added a few
more things to the firmware debugging infrastructure. Eran fixes a
small bug - pretty normal content."

And for the Atheros bits, Kalle says:

"For ath6kl me and Jessica added support for ar6004 hw3.0, our latest
version of ar6004.

For ath10k Janusz added a printout so that it's easier to check what
ath10k kconfig options are enabled. He also added a debugfs file to
configure maximum amsdu and ampdu values. Also we had few fixes as
usual."

On top of that is the usual large batch of various driver updates --
brcmfmac, mwifiex, the TI drivers, and wil6210 all get some action.
RafaƂ has also been very busy with b43 and related updates.

Also, I pulled the wireless tree into this in order to resolve a
merge conflict...

P.S.  The change to fs/compat_ioctl.c reflects a name change in a
Bluetooth header file...
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
179 files changed:
MAINTAINERS
drivers/bcma/driver_chipcommon_pmu.c
drivers/bcma/host_pci.c
drivers/bcma/sprom.c
drivers/bluetooth/ath3k.c
drivers/bluetooth/btmrvl_drv.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_uart.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/pci.c
drivers/net/wireless/ath/ath10k/pci.h
drivers/net/wireless/ath/ath10k/wmi.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/ar9002_mac.c
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/spectral.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/wil6210/debugfs.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43/main.h
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/xmit.c
drivers/net/wireless/brcm80211/brcmfmac/Makefile
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/chip.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
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/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmsmac/main.c
drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/iwlwifi/Kconfig
drivers/net/wireless/iwlwifi/iwl-8000.c
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/coex.c
drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/fw-api-coex.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/sta.c
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/11n.c
drivers/net/wireless/mwifiex/11n_rxreorder.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/join.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/pcie.c
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwifiex/tdls.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/ti/wl12xx/main.c
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/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/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/ieee80211.h
include/linux/ssb/ssb_regs.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/sco.h
include/net/mac80211.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/Kconfig
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/ieee802154/6lowpan_iphc.c [deleted file]
net/ieee802154/Kconfig
net/ieee802154/Makefile
net/mac80211/agg-rx.c
net/mac80211/chan.c
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/tdls.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wpa.c
net/wireless/Kconfig
net/wireless/genregdb.awk
net/wireless/nl80211.c
net/wireless/trace.h

index 62bf15f6954fb5222d3cd5a2a87382ef5a48954d..9faf35c9f899ea46293b6d601d3773130664bae5 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
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 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 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 230c552daf91db15f38c089b9a64fe7c8a4a045f..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 */
@@ -349,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);
@@ -370,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) {
@@ -378,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 caf684119a4e38d5da089fcebf9289f0acb89c0d..38ad66289ad6206204138a31d4d4bda83a4297c9 100644 (file)
@@ -91,6 +91,7 @@ struct btmrvl_private {
 
 /* 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
index cc65fd2fe856cb992b81da7c9f45e3f509757a54..bae8e6a0ecf684b64d71d42b730ba96c58da327c 100644 (file)
@@ -539,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.
@@ -632,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;
 
index efff06438b024cc2829777e2df7d3788cc9566ed..3e683b1532598231dc156bbc69887dd2e836d4da 100644 (file)
@@ -1169,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)) {
@@ -1176,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;
 
@@ -1221,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 ed7b33b06b43929503dd9e17517988aad4142a79..292c38e8aa1760c000cdcce9ee6d223e9f819559 100644 (file)
@@ -48,6 +48,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_INTEL            0x100
 #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 */
@@ -113,6 +114,9 @@ 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) },
 
@@ -242,6 +246,10 @@ static const struct usb_device_id blacklist_table[] = {
        { 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 */
 };
 
@@ -1455,6 +1463,29 @@ static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
        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)
@@ -1766,6 +1797,9 @@ static int btusb_probe(struct usb_interface *intf,
                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);
 
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 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 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 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 1c4ce8e3eebee3cd587d938de99e342024f8d7f4..e535807c3d8912863da3604dc9e698ee664f0a48 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 8ee7097f0b259da5f0604d389f5003aa892053af..fffd52355123f31e7bae15561fd867297d73f924 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) {
@@ -1229,7 +1268,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;
 }
@@ -1360,7 +1405,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;
@@ -1566,6 +1615,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 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 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 11b5e4dd629491179809aa89ef23aa9e57f5c6c4..7fc13a8da6757347e4daff1d1ede61da93648d3d 100644 (file)
@@ -182,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;
index ce073e995dfe3c50bfe128e5f3eadb1d5c9f5663..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;
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 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 d4927c9a6bae9a7cf86315bfbd7e8a1374260ca3..704fcbcbe20b5a59723063c77ac0d98ee482a99f 100644 (file)
@@ -157,15 +157,14 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
                             struct sk_buff *skb)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       int q, hw_queue;
-
-       q = skb_get_queue_mapping(skb);
-       if (txq == sc->tx.uapsdq)
-               txq = sc->tx.txq_map[q];
+       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;
 
@@ -898,6 +897,15 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
 
                tx_info = IEEE80211_SKB_CB(skb);
                tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT;
+
+               /*
+                * No aggregation session is running, but there may be frames
+                * from a previous session or a failed attempt in the queue.
+                * Send them out as normal data frames
+                */
+               if (!tid->active)
+                       tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
                if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
                        bf->bf_state.bf_type = 0;
                        return bf;
@@ -2036,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)
@@ -2187,6 +2196,7 @@ 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;
@@ -2216,11 +2226,13 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *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, hw_queue);
-               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);
index a868c5eebe37d73a0c4e140473fb2f3e02169278..8f66186adb8c59d67103ad75ddb759063736bbaa 100644 (file)
@@ -448,8 +448,10 @@ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
        char *kbuf = kmalloc(len + 1, GFP_KERNEL);
        if (!kbuf)
                return -ENOMEM;
-       if (copy_from_user(kbuf, buf, len))
+       if (copy_from_user(kbuf, buf, len)) {
+               kfree(kbuf);
                return -EIO;
+       }
 
        kbuf[len] = '\0';
        rc = kstrtol(kbuf, 0, &channel);
@@ -963,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,
@@ -986,6 +1008,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
                           &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));
@@ -998,6 +1022,9 @@ 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);
 
@@ -1010,34 +1037,7 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
        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 53a689ed7c7d0ba053d652c4b696639639b617d7..3704d2a434f340b4374f26e5bde083b9830e09a3 100644 (file)
@@ -314,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))
@@ -328,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 */
 
@@ -371,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);
index 77b6272d93fb24c11e9d703549e03929f623c6ca..d3fbfa28db62c8af2c95f824f9801b516635abff 100644 (file)
@@ -122,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) {
@@ -175,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 */
@@ -225,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 af4b93e4beb5e80e9412785d60a17ebbe95e790a..d3467943d39db60972bae0ce0413b4e5eaa04585 100644 (file)
@@ -1108,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;
index 424906635f05dc503b5718c8e8b0f3863e500ae0..67e9624f7111b8842492d81d07665c5ec0973705 100644 (file)
 
 #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
@@ -78,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)
@@ -93,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)
@@ -121,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 */
@@ -135,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
@@ -365,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 */
@@ -415,12 +434,7 @@ struct wil6210_priv {
        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)
index a136dab560e22c94e7c30682d56cbfbb9144745c..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 */
-       {0x88b000, 0x88c000, 0x88b000}, /* Pcie_ext_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
index 15aaeb132a327eededc5cd9cee7f782a231d9048..d7055febe1195406e4a6f8823e64ec7bba2bfb76 100644 (file)
@@ -210,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) {                             \
@@ -287,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),
@@ -319,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,
@@ -335,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);
@@ -2953,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;
@@ -4335,8 +4401,9 @@ static int b43_phy_versioning(struct b43_wldev *dev)
        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 */
@@ -4360,7 +4427,7 @@ static int b43_phy_versioning(struct b43_wldev *dev)
 #endif
 #ifdef CONFIG_B43_PHY_N
        case B43_PHYTYPE_N:
-               if (phy_rev > 9)
+               if (phy_rev >= 19)
                        unsupported = 1;
                break;
 #endif
@@ -4402,7 +4469,9 @@ static int b43_phy_versioning(struct b43_wldev *dev)
                radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA);
 
                b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1);
-               radio_ver = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+               radio_id = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+
+               radio_ver = 0; /* Is there version somewhere? */
        } else if (core_rev >= 24) {
                u16 radio24[3];
 
@@ -4411,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)
@@ -4435,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;
@@ -4451,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;
@@ -5095,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;
@@ -5248,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 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 50ca6f87d5e80be2ed716ae931384650a4729e0e..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)
 {
@@ -669,10 +747,63 @@ static void b43_radio_2057_setup(struct b43_wldev *dev,
                        b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8);
                }
                break;
-       /* TODO */
+       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;
        }
 
-       /* TODO */
+       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);
 
@@ -690,13 +821,62 @@ static void b43_radio_2057_setup(struct b43_wldev *dev,
 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 */
@@ -720,14 +900,30 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
        /* 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;
@@ -749,7 +945,7 @@ static u16 b43_radio_2057_rccal(struct b43_wldev *dev)
                b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0);
        } else {
                b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61);
-               b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE1);
+               b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE9);
        }
        b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
 
@@ -829,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);
@@ -1386,6 +1585,7 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
                                 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;
@@ -1393,6 +1593,26 @@ 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;
@@ -1520,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)
 {
@@ -1589,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 :
@@ -1682,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);
@@ -1726,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);
@@ -1825,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;
@@ -1877,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);
@@ -1908,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);
@@ -1939,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);
@@ -2009,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);
@@ -2209,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);
@@ -2222,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;
 
@@ -2419,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 = b43_is_40mhz(dev) ? 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);
@@ -2478,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);
@@ -2490,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));
@@ -2500,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 && b43_is_40mhz(dev)) ||
-                   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 && b43_is_40mhz(dev)) {
-                               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);
 
@@ -2625,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,
@@ -2651,7 +3036,9 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
                                                                0x3E);
                                        }
                                }
-                       } else if (phy->radio_rev == 7 || phy->radio_rev == 8) {
+                               break;
+                       case 7:
+                       case 8:
                                if (!b43_is_40mhz(dev)) {
                                        b43_radio_write(dev, 0x5F, 0x14);
                                        b43_radio_write(dev, 0xE8, 0x12);
@@ -2659,6 +3046,21 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
                                        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->chandef->chan->center_freq;
@@ -2706,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);
@@ -2718,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 (!b43_is_40mhz(dev)) {
-               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);
 
@@ -3059,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)
@@ -3120,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;
 
@@ -3140,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);
 }
@@ -3149,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 {
@@ -3199,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;
@@ -3268,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) {
@@ -3290,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);
@@ -3331,6 +3769,7 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
        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) {
@@ -3433,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) {
@@ -3516,13 +3957,16 @@ 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);
 
@@ -3531,14 +3975,20 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
        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 {
@@ -3730,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,
@@ -3793,24 +4245,30 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev)
        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);
        }
 }
 
@@ -3827,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
@@ -4274,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]);
@@ -4298,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;
@@ -4415,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]);
+               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)) {
-               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, 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 */
@@ -4471,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;
@@ -4498,7 +5053,13 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
                        if (!table)
                                break;
 
-                       if (dev->phy.rev >= 3) {
+                       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;
@@ -4547,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;
 
@@ -4578,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);
 
@@ -4586,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);
@@ -4614,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;
@@ -4638,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);
@@ -4665,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];
@@ -4712,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]);
@@ -4789,7 +5424,13 @@ 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 (!b43_is_40mhz(dev))
                freq = 2500;
@@ -5183,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
@@ -5269,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;
@@ -5344,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;
@@ -5353,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 {
@@ -5390,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) {
@@ -5422,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);
@@ -5548,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);
        }
 
@@ -5589,31 +6254,45 @@ static void b43_nphy_channel_setup(struct b43_wldev *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_is_40mhz(dev)) {
-                       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,
@@ -5644,7 +6323,10 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
 
        u8 tmp;
 
-       if (phy->rev >= 7) {
+       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)
@@ -5681,7 +6363,9 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
                        b43_phy_mask(dev, 0x310, (u16)~0x8000);
        }
 
-       if (phy->rev >= 7) {
+       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);
 
@@ -5834,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 */
@@ -5848,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);
@@ -5858,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);
@@ -5884,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);
@@ -5901,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 df3574545819d7f34249f59206ef0d3b1f40a473..ff1e026a61a131ffce5b0d51b22aaf79619b6be2 100644 (file)
@@ -105,6 +105,27 @@ static u16 r2057_rev8_init[][2] = {
 };
 */
 
+/* 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) \
@@ -137,6 +158,27 @@ static u16 r2057_rev8_init[][2] = {
        .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,   \
@@ -145,6 +187,353 @@ static u16 r2057_rev8_init[][2] = {
        .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)
 {
        struct b43_phy *phy = &dev->phy;
@@ -171,6 +560,18 @@ void r2057_upload_inittabs(struct b43_wldev *dev)
                        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);
@@ -193,8 +594,25 @@ void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq,
        *tabent_r7 = NULL;
        *tabent_r7_2g = NULL;
 
-       /* TODO */
        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;
        }
index 675d1bb64429d432762e50af57daed10a38d1d31..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
index b28dce950e1f864c93f30e5cafe75e095410600f..4b5885077b01bd5b223a3d45868adfc52a744882 100644 (file)
@@ -2408,6 +2408,41 @@ static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = {
        0x1041003c, 0x1041003b, 0x10410039, 0x10410037,
 };
 
+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[] = {
@@ -2480,6 +2515,41 @@ static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = {
        0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034,
 };
 
+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,
@@ -2622,6 +2692,42 @@ static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = {
        0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025,
 };
 
+/* 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,
@@ -2658,6 +2764,42 @@ static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = {
        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[] = {
@@ -2967,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 },
 };
 
@@ -3477,9 +3619,18 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev)
 
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
                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;
@@ -3489,23 +3640,24 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev)
                case 4:
                case 3:
                        return b43_ntab_tx_gain_ipa_rev3_2g;
-               default:
-                       b43err(dev->wl,
-                              "No 2GHz IPA gain table available for this device\n");
-                       return NULL;
                }
+
+               b43err(dev->wl,
+                      "No 2GHz IPA gain table available for this device\n");
+               return NULL;
        } else {
                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;
-               default:
-                       b43err(dev->wl,
-                              "No 5GHz IPA gain table available for this device\n");
-                       return NULL;
                }
+
+               b43err(dev->wl,
+                      "No 5GHz IPA gain table available for this device\n");
+               return NULL;
        }
 }
 
@@ -3530,7 +3682,7 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
                case 4:
                        return sprom->fem.ghz5.extpa_gain == 3 ?
                                b43_ntab_tx_gain_epa_rev4_5g :
-                               b43_ntab_tx_gain_epa_rev4_5g; /* FIXME */
+                               b43_ntab_tx_gain_epa_rev4_hi_pwr_5g;
                case 3:
                        return b43_ntab_tx_gain_epa_rev3_5g;
                default:
@@ -3543,7 +3695,7 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
                case 6:
                case 5:
                        if (sprom->fem.ghz5.extpa_gain == 3)
-                               return b43_ntab_tx_gain_epa_rev3_2g; /* FIXME */
+                               return b43_ntab_tx_gain_epa_rev3_hi_pwr_2g;
                        /* fall through */
                case 4:
                case 3:
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 4cffb2ee36738106d5543c4687f8406f4b3e1847..de0cff3df38950bd3194180d3f964990ce36d723 100644 (file)
@@ -34,6 +34,7 @@ brcmfmac-objs += \
                dhd_common.o \
                dhd_linux.o \
                firmware.o \
+               feature.o \
                btcoex.o \
                vendor.o
 brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
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 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 a8998eb60d22166eef9c51f027c5b7d41d2ae020..7da6441bcfa8e8cb519cf19379b9553a431fe50a 100644 (file)
@@ -103,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
@@ -175,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 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 2699441d4f41220537ea13c268a88276fdcc8dbd..347b4260f45ba3440f17982807f9f0ceb6bb88f8 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");
@@ -937,6 +938,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;
@@ -1074,16 +1077,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 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 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 b732a99e402cb8f494d40cd91ed865d58ec27dc6..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>
@@ -913,16 +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 43566:
-       case 43569:
+       case BRCM_CC_43566_CHIP_ID:
+       case BRCM_CC_43569_CHIP_ID:
                return true;
        default:
                break;
@@ -1016,16 +1017,16 @@ 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 43566:
-       case 43569:
+       case BRCM_CC_43566_CHIP_ID:
+       case BRCM_CC_43569_CHIP_ID:
                return BRCMF_USB_43569_FW_NAME;
        default:
                return NULL;
@@ -1366,21 +1367,17 @@ 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_43569      0xbd27
-#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) },
-       { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) },
+       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);
index 9682cf213ec46394c14d759d84ffdfc4794eea3c..48078a321716787069c7491880c0e783316acb3a 100644 (file)
@@ -33,6 +33,7 @@
 #include "p2p.h"
 #include "btcoex.h"
 #include "wl_cfg80211.h"
+#include "feature.h"
 #include "fwil.h"
 #include "vendor.h"
 
@@ -102,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) \
        {                                                               \
@@ -148,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,
 };
@@ -592,7 +534,7 @@ 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_get_chip_info(ifp) >> 4) == 0x4329)
+       if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
                brcmf_set_mpc(ifp, mpc);
 }
 
@@ -1619,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;
 }
@@ -4284,122 +4219,6 @@ static struct cfg80211_ops wl_cfg80211_ops = {
        .tdls_oper = brcmf_cfg80211_tdls_oper,
 };
 
-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);
-
-       /* vendor commands/events support */
-       wiphy->vendor_commands = brcmf_vendor_cmds;
-       wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
-
-       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)
@@ -4943,138 +4762,6 @@ 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)
-{
-       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);
-
-       vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
-       if (IS_ERR(vif)) {
-               wiphy_free(wiphy);
-               return NULL;
-       }
-
-       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);
-               goto cfg80211_attach_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);
-       if (err) {
-               brcmf_err("BT-coex initialisation failed (%d)\n", err);
-               brcmf_p2p_detach(&cfg->p2p);
-               goto cfg80211_p2p_attach_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);
-       }
-       /* clear for now and rely on update later */
-       wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false;
-       wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0;
-
-       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)
 {
@@ -5167,42 +4854,128 @@ dongle_scantime_out:
        return err;
 }
 
-
-static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
-                                  u32 bw_cap[])
+/* 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[])
 {
-       struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
-       struct ieee80211_channel *band_chan_arr;
-       struct brcmf_chanspec_list *list;
+       u32 total = le32_to_cpu(chlist->count);
        struct brcmu_chan ch;
-       s32 err;
-       u8 *pbuf;
-       u32 i, j;
-       u32 total;
-       enum ieee80211_band band;
-       u32 channel;
-       u32 *n_cnt;
-       u32 index;
-       u32 ht40_flag;
-       bool update;
-       u32 array_size;
+       int i;
 
-       pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+       for (i = 0; i <= total; i++) {
+               ch.chspec = (u16)le32_to_cpu(chlist->element[i]);
+               cfg->d11inf.decchspec(&ch);
 
-       if (pbuf == NULL)
-               return -ENOMEM;
+               /* 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;
+               }
 
-       list = (struct brcmf_chanspec_list *)pbuf;
+               if (ch.band == BRCMU_CHAN_BAND_2G)
+                       chcnt[0] += 1;
+               else if (ch.band == BRCMU_CHAN_BAND_5G)
+                       chcnt[1] += 1;
+       }
+}
 
-       err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
-                                      BRCMF_DCMD_MEDLEN);
+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_supported_band *band;
+       struct ieee80211_channel *channel;
+       struct wiphy *wiphy;
+       struct brcmf_chanspec_list *list;
+       struct brcmu_chan ch;
+       int err;
+       u8 *pbuf;
+       u32 i, j;
+       u32 total;
+       u32 chaninfo;
+       u32 chcnt[2] = { 0, 0 };
+       u32 index;
+
+       pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
+
+       if (pbuf == NULL)
+               return -ENOMEM;
+
+       list = (struct brcmf_chanspec_list *)pbuf;
+
+       err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
+                                      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++) {
@@ -5210,100 +4983,151 @@ 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;
+}
+
 static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
 {
        u32 band, mimo_bwcap;
@@ -5394,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) {
@@ -5453,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 s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
+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 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)
@@ -5528,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);
 
@@ -5658,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 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 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 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 51486cc9d943d21d79608980cf04e1fde494267f..44b19e015102096e39fd086539794ab608c4c359 100644 (file)
@@ -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,
@@ -129,6 +132,7 @@ const struct iwl_cfg iwl8260_2ac_sdio_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 034c2fc4b69ffcbb3505b8e96b283ae1593310ad..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;
 };
 
 /*
index c39a0b899e83aef7b780786b01f90e29039d9259..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,
 };
@@ -162,6 +165,16 @@ struct iwl_fw_error_dump_fw_mon {
        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_fw_error_next_data - advance fw error dump data pointer
  * @data: previous data block
index f2d39cb011fc10442ce4dc6342602e5130b7c4d4..71507cf490e6c2bb18ba666f45e3dc052cee8634 100644 (file)
@@ -99,7 +99,7 @@ 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
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 8110fe00bf5512635e5a050655c4d5ea7d011d68..2291bbcaaeab8bfe57c3aa20f9409bab41ba4cef 100644 (file)
 
 #define BT_ANTENNA_COUPLING_THRESHOLD          (30)
 
-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 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_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_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 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] = {
@@ -611,54 +651,43 @@ send_cmd:
        return ret;
 }
 
-static int iwl_mvm_bt_udpate_sw_boost(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_sw_boost_update_cmd cmd = {};
        struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
+       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;
+       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));
 
-       cmd.boost_values[0].kill_ack_msk =
-               cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
-       cmd.boost_values[0].kill_cts_msk =
-               cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
+       BUILD_BUG_ON(ARRAY_SIZE(ack_kill_msk) < ARRAY_SIZE(cmd.boost_values));
 
-       cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
-       cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
-       cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
-       cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_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]);
+       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]]);
+       }
 
        return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0,
                                    sizeof(cmd), &cmd);
@@ -700,8 +729,6 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
 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;
@@ -737,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;
@@ -763,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);
@@ -779,9 +795,7 @@ 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)
@@ -795,7 +809,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                       "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)) {
@@ -846,7 +862,6 @@ 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 ||
            le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
-               data->reduced_tx_power = false;
                iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
                iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
@@ -861,23 +876,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        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 < -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 */
@@ -889,7 +890,6 @@ 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;
@@ -959,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_sw_boost(mvm, data.reduced_tx_power))
+       if (iwl_mvm_bt_udpate_sw_boost(mvm))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
@@ -1035,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,
@@ -1053,7 +1036,6 @@ 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;
 
@@ -1100,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_sw_boost(mvm, data.reduced_tx_power))
+       if (iwl_mvm_bt_udpate_sw_boost(mvm))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
@@ -1150,7 +1125,7 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
        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);
+               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;
index ce50363d314bffea1f87a3d670fd42fa9fc751b7..a3be3335992766348a670028d3bfa7f2a390ea60 100644 (file)
@@ -649,10 +649,6 @@ int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
               sizeof(iwl_bt_prio_boost));
        memcpy(&bt_cmd->bt4_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_old, 0, sizeof(mvm->last_bt_notif_old));
@@ -664,12 +660,13 @@ send_cmd:
        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_ctrl_kill_msk(struct iwl_mvm *mvm)
 {
-       enum iwl_bt_kill_msk bt_kill_msk;
-       struct iwl_bt_coex_cmd_old *bt_cmd;
        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,
@@ -680,31 +677,15 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
 
        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;
-       }
-
-       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 = iwl_bt_ack_kill_msk[ag][primary_lut];
+       cts_kill_msk = iwl_bt_cts_kill_msk[ag][primary_lut];
 
-       /* Don't send HCMD if there is no update */
-       if (bt_kill_msk == mvm->bt_kill_msk)
+       if (mvm->bt_ack_kill_msk[0] == ack_kill_msk &&
+           mvm->bt_cts_kill_msk[0] == cts_kill_msk)
                return 0;
 
-       mvm->bt_kill_msk = bt_kill_msk;
+       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)
@@ -712,16 +693,12 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
        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_ack_kill_msk[bt_kill_msk]);
-       bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
+       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);
 
-       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]);
-
        ret = iwl_mvm_send_cmd(mvm, &cmd);
 
        kfree(bt_cmd);
@@ -777,8 +754,6 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
 struct iwl_bt_iterator_data {
        struct iwl_bt_coex_profile_notif_old *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;
@@ -814,22 +789,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;
@@ -840,11 +805,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);
@@ -869,7 +833,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
                       mvmvif->id, data->notif->bt_status, 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)) {
@@ -920,7 +886,6 @@ 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;
                iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
                iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
                return;
@@ -935,23 +900,9 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
        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 < -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 */
@@ -963,7 +914,6 @@ 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,
-               .reduced_tx_power = true,
        };
        struct iwl_bt_coex_ci_cmd_old cmd = {};
        u8 ci_bw_idx;
@@ -1037,14 +987,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
                memcpy(&mvm->last_bt_ci_cmd_old, &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_ctrl_kill_msk(mvm))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
@@ -1115,16 +1058,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_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -1133,7 +1066,6 @@ void iwl_mvm_bt_rssi_event_old(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;
 
@@ -1175,14 +1107,7 @@ void iwl_mvm_bt_rssi_event_old(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_ctrl_kill_msk(mvm))
                IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
 }
 
index f131ef0ec5b30a482311a3ce9b7dec331965534b..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;
 }
@@ -514,9 +544,9 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
 
                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]);
+                                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_cts_kill_msk[mvm->bt_kill_msk]);
+                                iwl_bt_ctl_kill_msk[mvm->bt_cts_kill_msk[0]]);
 
        } else {
                struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
@@ -531,10 +561,19 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
                               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_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]);
+               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);
@@ -830,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;
 }
 
@@ -1115,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,
@@ -1127,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;
 
-       pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%lx\n",
-                        mvm->ref_bitmap[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%x\n",
+                        refs);
 
        PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN);
        PRINT_MVM_REF(IWL_MVM_REF_SCAN);
@@ -1158,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)
@@ -1194,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);
 }
 
@@ -1211,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 */
@@ -1221,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;
 }
index ab12aaa43034765e2c48cad43de2813b7f6ace7f..69875716dcdb80abeb688949d826267eb391ccf0 100644 (file)
@@ -385,6 +385,8 @@ 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 {
index b8e4e78d601b98b6e67a444f5f7cb09985a7bef1..95f5b3274efb516f280eee74eb8f9649c51d599a 100644 (file)
@@ -133,6 +133,7 @@ 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,
@@ -910,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 96b9cf8137e7f158df7e7db16cc32a779ca1f2be..0e523e28cabfc15b8beb63af3659cdc20d30b401 100644 (file)
@@ -1074,8 +1074,12 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm,
        /* Fill the common data for all mac context types */
        iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
 
-       /* Also enable probe requests to pass */
-       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST);
+       /*
+        * pass probe requests and beacons from other APs (needed
+        * for ht protection)
+        */
+       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST |
+                                       MAC_FILTER_IN_BEACON);
 
        /* Fill the data specific for ap mode */
        iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap,
@@ -1096,6 +1100,13 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm,
        /* Fill the common data for all mac context types */
        iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action);
 
+       /*
+        * pass probe requests and beacons from other APs (needed
+        * for ht protection)
+        */
+       cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST |
+                                       MAC_FILTER_IN_BEACON);
+
        /* Fill the data specific for GO mode */
        iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap,
                                     action == FW_CTXT_ACTION_ADD);
index 2eb6ebee446708308168da65d926f394e743597d..0d6a8b768a686fb4630528682635c7ee865ce97c 100644 (file)
@@ -211,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);
 }
 
@@ -221,29 +223,35 @@ 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);
 }
 
-static int iwl_mvm_ref_sync(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)
 {
        iwl_mvm_ref(mvm, ref_type);
 
@@ -321,13 +329,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
        }
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
-           !iwlwifi_mod_params.uapsd_disable) {
-               hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
-               hw->uapsd_queues = IWL_UAPSD_AC_INFO;
-               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;
 
@@ -660,6 +661,7 @@ 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
@@ -668,11 +670,11 @@ 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;
-       u32 trans_len;
        int reg_val;
 
        lockdep_assert_held(&mvm->mutex);
@@ -680,6 +682,10 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        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;
@@ -697,18 +703,15 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                   rxf_len +
                   sizeof(*dump_info);
 
-       trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
-       if (trans_len)
-               file_len += trans_len;
-
        dump_file = vzalloc(file_len);
-       if (!dump_file)
+       if (!dump_file) {
+               kfree(fw_error_dump);
                return;
+       }
 
-       mvm->fw_error_dump = dump_file;
+       fw_error_dump->op_mode_ptr = 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_DEV_FW_INFO);
@@ -749,14 +752,12 @@ static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
                                 sram_len);
 
-       if (trans_len) {
-               void *buf = iwl_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);
-       }
+       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
 
@@ -788,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);
 
@@ -1399,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,
@@ -1494,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);
@@ -1533,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 */
@@ -1581,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;
 
@@ -1594,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;
 }
 
@@ -1671,6 +1717,14 @@ 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)
@@ -1690,8 +1744,50 @@ 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 ieee80211_scan_request *hw_req)
@@ -1704,19 +1800,13 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
            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_scan_offload_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;
        }
@@ -1732,8 +1822,6 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
                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;
 }
 
@@ -1885,28 +1973,6 @@ static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm,
                iwl_mvm_power_update_mac(mvm);
 }
 
-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 int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_sta *sta,
@@ -2065,10 +2131,19 @@ 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,
@@ -2077,10 +2152,19 @@ static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
        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,
@@ -2091,6 +2175,10 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
        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)) {
@@ -2098,26 +2186,7 @@ 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;
        }
@@ -2145,8 +2214,6 @@ 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;
 }
 
@@ -2266,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,
@@ -2281,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;
        }
 
@@ -2661,6 +2850,10 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
                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:
index 785e5232c757f4e4647f723fab5729bd141368eb..2e73d3bd7757605e2483fa00f147086abe32b09c 100644 (file)
@@ -82,6 +82,8 @@
 /* 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"
@@ -126,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;
@@ -249,6 +266,15 @@ enum iwl_mvm_ref_type {
        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,
@@ -327,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;
 
@@ -606,14 +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;
+       struct iwl_mvm_dump_ptrs *fw_error_dump;
 
 #ifdef CONFIG_IWLWIFI_LEDS
        struct led_classdev led;
@@ -647,7 +675,8 @@ 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;
@@ -659,6 +688,9 @@ struct iwl_mvm {
        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;
        s32 temperature;        /* Celsius */
@@ -697,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)
@@ -988,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);
 
@@ -1029,12 +1063,14 @@ int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
 
 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
index b04805ccb443d1a7b1532a96881a3d739933fd96..cfdd314fdd5decd15b57bf02b3ad63eb1ab16cd0 100644 (file)
@@ -265,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 {
@@ -273,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 */
index 7d7b2fbe7cd1b3906c2378059b99776ccaddf65a..610dbcb0dc279d97dda32ded4df2b387a4577333 100644 (file)
@@ -289,6 +289,7 @@ 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),
@@ -391,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;
 
@@ -416,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);
 
@@ -425,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);
 
@@ -539,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;
 
@@ -567,7 +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);
+       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;
 
index 81281396484724a9fc6cab21bb59b91b12ad52c1..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;
index ae52613b97f2da706a72bae3d04ce9015e8495c8..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
@@ -262,6 +264,60 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
        }
 }
 
+/*
+ * 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
  */
@@ -278,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;
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 e9ff38635c21914fa8d4174c0582df61cf1c4779..dbc870713882937c381a90ea2a1c31a2a9f60af8 100644 (file)
@@ -310,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
index 5b5b0d8c6f6051f7dd0311cf1437090d36a1d2f2..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"
@@ -1773,28 +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;
 
-       len = sizeof(*data) +
+       /* 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;
 
-       if (!buf)
-               return len;
+       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);
@@ -1820,11 +2000,15 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
 
        data->len = cpu_to_le32(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 = iwl_fw_error_next_data(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));
@@ -1852,7 +2036,9 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
                        trans_pcie->fw_mon_size;
        }
 
-       return len;
+       dump_data->len = len;
+
+       return dump_data;
 }
 #else
 static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
index 5ea65fce0b83224bc3628138da76a8ff518d0d9f..1326f61218351b275060e524e32086f9d125fb32 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,
index 9d6d8d9f01e39bfe2716e28fed9b4d7b7e61b140..62f5dbe602d3de46f8daf6408f776670e024fe05 100644 (file)
@@ -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 b22bae3d1205bd0c22000fc9e66a50795e1f3eb9..06a2c215ef5e13129a53e72add714c05cbb77489 100644 (file)
@@ -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 ca87f923c61eaa251908add3f2bc81c95cdb9132..e2e6bf13c2d8c0858656f494c839da81d0731fa5 100644 (file)
@@ -1604,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;
@@ -1744,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);
 
index 5899eee87fb1a082a593ac833ccd58236006584f..baf0aab63c04d17bb1d01501e0c29f524b4a5f57 100644 (file)
@@ -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));
 
@@ -273,7 +270,6 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
                                (struct mwifiex_opt_sleep_confirm *)
                                                adapter->sleep_cfm->data;
        struct sk_buff *sleep_cfm_tmp;
-       struct timeval ts;
        __le32 tmp;
 
        priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
@@ -284,10 +280,9 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
                                        (adapter->seq_num, priv->bss_num,
                                         priv->bss_type)));
 
-       do_gettimeofday(&ts);
        dev_dbg(adapter->dev,
-               "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d, seqno %#x\n",
-               ts.tv_sec, ts.tv_usec, le16_to_cpu(sleep_cfm_buf->command),
+               "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));
@@ -442,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 */
@@ -467,9 +461,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
                rx_info->bss_type = priv->bss_type;
        }
 
-       do_gettimeofday(&tstamp);
-       dev_dbg(adapter->dev, "EVENT: %lu.%lu: cause: %#x\n",
-               tstamp.tv_sec, tstamp.tv_usec, eventcause);
+       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);
@@ -781,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 */
@@ -839,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");
@@ -903,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) {
@@ -916,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);
 
@@ -1237,18 +1224,15 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
        uint16_t result = le16_to_cpu(cmd->result);
        uint16_t command = le16_to_cpu(cmd->command);
        uint16_t seq_num = le16_to_cpu(cmd->seq_num);
-       struct timeval ts;
 
        if (!upld_len) {
                dev_err(adapter->dev, "%s: cmd size is 0\n", __func__);
                return;
        }
 
-       do_gettimeofday(&ts);
        dev_dbg(adapter->dev,
-               "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d, len %d, seqno 0x%x\n",
-               ts.tv_sec, ts.tv_usec, command, result, le16_to_cpu(cmd->size),
-               seq_num);
+               "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),
index 5561573452bb4a27cc92a6ae490303448ea0e103..49da2d53d29455830fb9958bc4ab7dbbcd5d5666 100644 (file)
@@ -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 fc135649b85f4ebc63edfb0b2b2f7c4d9871ef98..8d6c25908b6d012fc9f2ef3d9f9211fd5f09b862 100644 (file)
@@ -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 3e5194fb0b0fd44c5314e8bbe5fb950325bb759c..dfa37eadc4db566ecc75db890c838d8d04600917 100644 (file)
@@ -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);
index 5f7afffdd34e73dcdc020555afecd1e84c7a08ed..c16dd2cc81987791d760cf9bdff9aeadcda36579 100644 (file)
@@ -2238,7 +2238,6 @@ 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;
-       struct timeval t;
        u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
        enum rdwr_status stat;
        u32 memory_size;
@@ -2257,9 +2256,7 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
                entry->mem_size = 0;
        }
 
-       do_gettimeofday(&t);
-       dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n",
-                (u32)t.tv_sec, (u32)t.tv_usec);
+       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);
@@ -2303,9 +2300,8 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
                end_ptr = dbg_ptr + memory_size;
 
                doneflag = entry->done_flag;
-               do_gettimeofday(&t);
-               dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n",
-                        entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec);
+               dev_info(adapter->dev, "Start %s output, please wait...\n",
+                        entry->mem_name);
 
                do {
                        stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
@@ -2331,9 +2327,7 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
                        break;
                } while (true);
        }
-       do_gettimeofday(&t);
-       dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n",
-                (u32)t.tv_sec, (u32)t.tv_usec);
+       dev_info(adapter->dev, "== mwifiex firmware dump end ==\n");
 
        kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
 
index 1da04a086bd955b3e64fd5556bee8e56284c8898..1770fa3fc1e621fc2965cde14581c337c1691d98 100644 (file)
@@ -1954,6 +1954,7 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
        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);
 }
 
@@ -2012,7 +2013,6 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
        int ret = 0;
        unsigned int reg, reg_start, reg_end;
        u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
-       struct timeval t;
        enum rdwr_status stat;
        u32 memory_size;
        static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL };
@@ -2033,9 +2033,7 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
        mwifiex_pm_wakeup_card(adapter);
        sdio_claim_host(card->func);
 
-       do_gettimeofday(&t);
-       dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n",
-                (u32)t.tv_sec, (u32)t.tv_usec);
+       dev_info(adapter->dev, "== mwifiex firmware dump start ==\n");
 
        stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
        if (stat == RDWR_STATUS_FAILURE)
@@ -2087,9 +2085,8 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
                end_ptr = dbg_ptr + memory_size;
 
                doneflag = entry->done_flag;
-               do_gettimeofday(&t);
-               dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n",
-                        entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec);
+               dev_info(adapter->dev, "Start %s output, please wait...\n",
+                        entry->mem_name);
 
                do {
                        stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
@@ -2120,9 +2117,7 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
                        break;
                } while (1);
        }
-       do_gettimeofday(&t);
-       dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n",
-                (u32)t.tv_sec, (u32)t.tv_usec);
+       dev_info(adapter->dev, "== mwifiex firmware dump end ==\n");
 
        kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
 
index 0f077aaadab6be43441f3f5be535490e2059b72e..733de92a4c611e39da93fb580a272203d269e54f 100644 (file)
@@ -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 822357b7b0bbb04818192457cab3df6e682ffb10..08b78baeb846d65ef18680e462991c226c94d5e3 100644 (file)
@@ -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 1a03d4d8b418453e52ed3618501c3c64da66db52..caae9738100aa732087e2d03477b063bc1534fe2 100644 (file)
@@ -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);
index a414161c6064d783ed482c087ff7d7f1a10c013e..4c5fd953893dcb22712f60c618f68499351c1dff 100644 (file)
@@ -781,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))
@@ -792,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))
@@ -811,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;
@@ -821,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;
@@ -833,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 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 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 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 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 e269c0a57017a270b0c724abf5426f105fa7343a..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;
index 6788d7356ca5a8d6d4aab0c6d2404985f4a072e1..ca6a28b03f8f31b5b47b23b8db982df4dbf78f84 100644 (file)
@@ -170,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 48f83868f9cbee35a34871ce078f3615e80827e1..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);
        }
 
@@ -4703,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--;
@@ -4915,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 ||
@@ -5149,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;
@@ -5164,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;
@@ -5619,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;
@@ -5764,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 |
@@ -5833,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 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 71320509b56d5bc3133d602888092a6e566cb89e..df78cf12ef1574b475751aca21ab5995c397af95 100644 (file)
@@ -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 452286a38b2b9f751600fb2d96a9dc13bafcd36a..969af0f2bdf92cdaf14d5e040a6b76f70dd32984 100644 (file)
@@ -158,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
index 75d17e15da338c1bcb05d1b27283ea577050b3aa..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;
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 a01236e2df1327673946899f0a93f22e2672195f..f0a3d88907605eab81264a8a9ee10a07ef32cce1 100644 (file)
@@ -202,33 +202,6 @@ enum {
 #define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \
                              BIT(HCI_FAST_CONNECTABLE) | BIT(HCI_LE_ADV))
 
-/* 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)
-
 /* HCI timeouts */
 #define HCI_DISCONN_TIMEOUT    msecs_to_jiffies(2000)  /* 2 seconds */
 #define HCI_PAIRING_TIMEOUT    msecs_to_jiffies(60000) /* 60 seconds */
@@ -356,6 +329,7 @@ enum {
 #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
 
@@ -427,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 */
@@ -1739,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;
@@ -1871,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 e69c2b08c0c6194aa898ad820cfc6e746f61c9c0..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
@@ -82,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 {
@@ -370,6 +372,7 @@ struct hci_conn {
        __u16           state;
        __u8            mode;
        __u8            type;
+       __u8            role;
        bool            out;
        __u8            attempt;
        __u8            dev_class[3];
@@ -539,12 +542,12 @@ enum {
        HCI_CONN_POWER_SAVE,
        HCI_CONN_REMOTE_OOB,
        HCI_CONN_FLUSH_KEY,
-       HCI_CONN_MASTER,
        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)
@@ -574,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:
@@ -598,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:
@@ -694,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);
@@ -706,14 +714,15 @@ 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, u16 conn_timeout,
-                               bool master);
+                               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);
 
@@ -880,12 +889,12 @@ 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);
@@ -1239,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);
@@ -1286,10 +1296,8 @@ 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_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,
@@ -1350,34 +1358,6 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr,
 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)
-
 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,
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 e0c6a9abdb6282e805f46556223085a191f7810d..8df15ad0d43fadc17f82aed2cb2b9115ff6a425f 100644 (file)
@@ -134,9 +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_ATT           0x10
+#define L2CAP_FC_SIG_LE                0x20
+#define L2CAP_FC_SMP_LE                0x40
 
 /* L2CAP Control Field bit masks */
 #define L2CAP_CTRL_SAR                 0xC000
@@ -622,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;
@@ -903,7 +905,7 @@ 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);
 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 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 9ce5cb17ed826a83979d787b9a43e0f19d14f383..dae2e24616e16bf3051af3dd31414f91f4220af9 100644 (file)
@@ -4552,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 */
 
 /**
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..a1b7117
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * 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;
+               }
+
+               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 f5afaa22f6ecda644d31551cb32d7a3758de31a3..600fb29288f47c48ce17583d769d502746854bd1 100644 (file)
@@ -41,8 +41,7 @@ menuconfig BT
 
 config BT_6LOWPAN
        tristate "Bluetooth 6LoWPAN support"
-       depends on BT && IPV6
-       select 6LOWPAN_IPHC if BT_6LOWPAN
+       depends on BT && 6LOWPAN
        help
          IPv6 compression over Bluetooth Low Energy.
 
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 490ee8846d9e21e910220df687907243dd855feb..b50dabb3f86ab49667cb939af29cef53a03b2ac2 100644 (file)
@@ -66,8 +66,7 @@ static void hci_acl_create_connection(struct hci_conn *conn)
 
        conn->state = BT_CONNECT;
        conn->out = true;
-
-       set_bit(HCI_CONN_MASTER, &conn->flags);
+       conn->role = HCI_ROLE_MASTER;
 
        conn->attempt++;
 
@@ -335,7 +334,7 @@ static void hci_conn_timeout(struct work_struct *work)
                         * event handling and hci_clock_offset_evt function.
                         */
                        if (conn->type == ACL_LINK &&
-                           test_bit(HCI_CONN_MASTER, &conn->flags)) {
+                           conn->role == HCI_ROLE_MASTER) {
                                struct hci_dev *hdev = conn->hdev;
                                struct hci_cp_read_clock_offset cp;
 
@@ -422,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;
 
@@ -436,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;
@@ -448,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;
@@ -698,7 +702,7 @@ 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, u16 conn_timeout,
-                               bool master)
+                               u8 role)
 {
        struct hci_conn_params *params;
        struct hci_conn *conn;
@@ -747,7 +751,7 @@ 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);
 
@@ -771,7 +775,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
        }
 
        /* If requested to connect as slave use directed advertising */
-       if (!master) {
+       if (conn->role == HCI_ROLE_SLAVE) {
                /* If we're active scanning most controllers are unable
                 * to initiate advertising. Simply reject the attempt.
                 */
@@ -786,9 +790,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
                goto create_conn;
        }
 
-       conn->out = true;
-       set_bit(HCI_CONN_MASTER, &conn->flags);
-
        params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
        if (params) {
                conn->le_conn_min_interval = params->conn_min_interval;
@@ -833,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);
        }
@@ -866,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);
@@ -972,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);
 
@@ -1025,6 +1027,9 @@ 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;
 
@@ -1076,7 +1081,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
 {
        BT_DBG("hcon %p", conn);
 
-       if (!role && test_bit(HCI_CONN_MASTER, &conn->flags))
+       if (role == conn->role)
                return 1;
 
        if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) {
@@ -1151,7 +1156,7 @@ static u32 get_link_mode(struct hci_conn *conn)
 {
        u32 link_mode = 0;
 
-       if (test_bit(HCI_CONN_MASTER, &conn->flags))
+       if (conn->role == HCI_ROLE_MASTER)
                link_mode |= HCI_LM_MASTER;
 
        if (test_bit(HCI_CONN_ENCRYPT, &conn->flags))
@@ -1277,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 84431b86af963d423dd19d2ffcf957508aa3a1f0..cfcb6055ced8f21df5f559484ac910d0e25e965e 100644 (file)
@@ -54,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)
@@ -1339,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);
 
@@ -1416,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))
@@ -1476,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.
         */
@@ -1605,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.
@@ -1635,7 +1644,10 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                u8 events[8];
 
                memset(events, 0, sizeof(events));
-               events[0] = 0x1f;
+               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.
@@ -1648,6 +1660,11 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                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);
        }
 
@@ -2071,7 +2088,7 @@ u32 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);
+       ie = kzalloc(sizeof(*ie), GFP_KERNEL);
        if (!ie) {
                flags |= MGMT_DEV_FOUND_CONFIRM_NAME;
                goto done;
@@ -2435,6 +2452,16 @@ 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:
@@ -2655,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;
@@ -2716,22 +2779,11 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
                err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt,
                                   HCI_INIT_TIMEOUT);
 
-               /* Ensure that the connectable state gets correctly
-                * notified if the whitelist is in use.
+               /* Ensure that the connectable and discoverable states
+                * get correctly modified as this was a non-mgmt change.
                 */
-               if (!err && !list_empty(&hdev->whitelist)) {
-                       bool changed;
-
-                       if ((dr.dev_opt & SCAN_PAGE))
-                               changed = !test_and_set_bit(HCI_CONNECTABLE,
-                                                           &hdev->dev_flags);
-                       else
-                               changed = test_and_set_bit(HCI_CONNECTABLE,
-                                                          &hdev->dev_flags);
-
-                       if (changed)
-                               mgmt_new_settings(hdev);
-               }
+               if (!err)
+                       hci_update_scan_state(hdev, dr.dev_opt);
                break;
 
        case HCISETLINKPOL:
@@ -2792,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;
@@ -2819,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)))
@@ -2828,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;
@@ -3062,13 +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)
 {
-       return (type == SMP_LTK);
+       if (type == SMP_LTK)
+               return HCI_ROLE_MASTER;
+
+       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;
 
@@ -3076,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;
@@ -3086,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;
@@ -3188,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 {
@@ -3430,7 +3492,7 @@ int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 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;
 
@@ -3835,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;
 
@@ -4390,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)
 {
@@ -5398,8 +5465,7 @@ void hci_update_background_scan(struct hci_dev *hdev)
 
        hci_req_init(&req, hdev);
 
-       if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags) &&
-           list_empty(&hdev->pend_le_conns) &&
+       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
index c8ae9ee3cb12bfcd3f08c980283e2158db29eba0..4c41774aa5565cbd7aa0c99ad74adbacea87e8ce 100644 (file)
@@ -101,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)
-                       clear_bit(HCI_CONN_MASTER, &conn->flags);
-               else
-                       set_bit(HCI_CONN_MASTER, &conn->flags);
-       }
+       if (conn)
+               conn->role = rp->role;
 
        hci_dev_unlock(hdev);
 }
@@ -296,7 +292,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
 {
        __u8 status = *((__u8 *) skb->data);
        __u8 param;
-       int old_pscan, old_iscan;
        void *sent;
 
        BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -310,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);
@@ -1432,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;
-                               set_bit(HCI_CONN_MASTER, &conn->flags);
-                       } else
+                       conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr,
+                                           HCI_ROLE_MASTER);
+                       if (!conn)
                                BT_ERR("No memory for new connection");
                }
        }
@@ -1665,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);
@@ -2149,18 +2131,17 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
                return;
        }
 
-       if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) {
-               if (hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr,
-                                          BDADDR_BREDR)) {
-                       hci_reject_conn(hdev, &ev->bdaddr);
-                       return;
-               }
-       } else {
-               if (!hci_bdaddr_list_lookup(&hdev->whitelist, &ev->bdaddr,
-                                           BDADDR_BREDR)) {
-                       hci_reject_conn(hdev, &ev->bdaddr);
-                       return;
-               }
+       if (hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr,
+                                  BDADDR_BREDR)) {
+               hci_reject_conn(hdev, &ev->bdaddr);
+               return;
+       }
+
+       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;
        }
 
        /* Connection accepted */
@@ -2174,7 +2155,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        conn = hci_conn_hash_lookup_ba(hdev, ev->link_type,
                        &ev->bdaddr);
        if (!conn) {
-               conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr);
+               conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr,
+                                   HCI_ROLE_SLAVE);
                if (!conn) {
                        BT_ERR("No memory for new connection");
                        hci_dev_unlock(hdev);
@@ -2407,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);
        }
@@ -2938,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)
-                               clear_bit(HCI_CONN_MASTER, &conn->flags);
-                       else
-                               set_bit(HCI_CONN_MASTER, &conn->flags);
-               }
+               if (!ev->status)
+                       conn->role = ev->role;
 
                clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
 
@@ -3137,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)
@@ -3666,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;
 
@@ -3678,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;
@@ -3761,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);
@@ -3777,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;
@@ -3893,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
@@ -4123,7 +4113,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        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;
@@ -4131,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;
-                       set_bit(HCI_CONN_MASTER, &conn->flags);
-               }
-
                /* 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
@@ -4202,14 +4187,14 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        else
                addr_type = BDADDR_LE_RANDOM;
 
-       /* Drop the connection if he device is blocked */
-       if (hci_bdaddr_list_lookup(&hdev->blacklist, &conn->dst, addr_type)) {
-               hci_conn_drop(conn);
+       if (ev->status) {
+               hci_le_conn_failed(conn, ev->status);
                goto unlock;
        }
 
-       if (ev->status) {
-               hci_le_conn_failed(conn, ev->status);
+       /* 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;
        }
 
@@ -4275,6 +4260,12 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
        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.
+        */
+       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)
@@ -4287,9 +4278,8 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
                return;
 
 connect:
-       /* Request connection in master = true role */
        conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
-                             HCI_LE_AUTOCONN_TIMEOUT, true);
+                             HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER);
        if (!IS_ERR(conn))
                return;
 
@@ -4329,14 +4319,11 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
         * device found events.
         */
        if (hdev->le_scan_type == LE_SCAN_PASSIVE) {
-               struct hci_conn_params *param;
-
                if (type == LE_ADV_DIRECT_IND)
                        return;
 
-               param = hci_pend_le_action_lookup(&hdev->pend_le_reports,
-                                                 bdaddr, bdaddr_type);
-               if (!param)
+               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)
@@ -4470,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;
 
@@ -4545,7 +4532,7 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev,
                return send_conn_param_neg_reply(hdev, handle,
                                                 HCI_ERROR_INVALID_LL_PARAMS);
 
-       if (test_bit(HCI_CONN_MASTER, &hcon->flags)) {
+       if (hcon->role == HCI_ROLE_MASTER) {
                struct hci_conn_params *params;
                u8 store_hint;
 
@@ -4638,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 c64728d571ae7648711702552a77425437094fdd..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 */
index 8680aae678ce012a0b62a206d14dc96936122a0e..46547b920f88edf7e6df5b6c5d10ae30daa47d67 100644 (file)
@@ -46,7 +46,7 @@
 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);
@@ -775,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;
@@ -785,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)
@@ -798,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;
 }
@@ -1278,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);
                }
@@ -1357,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;
@@ -1379,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);
@@ -1487,7 +1488,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
         * been configured for this connection. If not, then trigger
         * the connection update procedure.
         */
-       if (!test_bit(HCI_CONN_MASTER, &hcon->flags) &&
+       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;
@@ -3849,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;
@@ -5227,7 +5228,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
        u16 min, max, latency, to_multiplier;
        int err;
 
-       if (!test_bit(HCI_CONN_MASTER, &hcon->flags))
+       if (hcon->role != HCI_ROLE_MASTER)
                return -EINVAL;
 
        if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
@@ -6984,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;
@@ -7016,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);
@@ -7093,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;
        }
 
@@ -7128,7 +7129,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        chan->dcid = cid;
 
        if (bdaddr_type_is_le(dst_type)) {
-               bool master;
+               u8 role;
 
                /* Convert from L2CAP channel address type to HCI address type
                 */
@@ -7137,10 +7138,13 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
                else
                        dst_type = ADDR_LE_DEV_RANDOM;
 
-               master = !test_bit(HCI_ADVERTISING, &hdev->dev_flags);
+               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,
-                                     HCI_LE_CONN_TIMEOUT, master);
+                                     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);
@@ -7188,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);
index 9bb4d1b3a48382301ba9af14c392b7754f4198e8..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;
        }
 
@@ -797,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);
@@ -1112,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);
        }
index 91b1f92c681e0372ab1276d99b7e89750b2e0076..190668367e422b7ace0e04946ae59c35d34ba6c6 100644 (file)
@@ -906,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;
@@ -1743,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);
 
@@ -1760,15 +1770,23 @@ 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);
        }
 
@@ -1882,8 +1900,8 @@ 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) &&
-           !test_bit(HCI_LE_ADV, &hdev->dev_flags))
+       /* 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);
@@ -3136,9 +3154,9 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                 */
                hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type);
 
-               /* Request a connection with master = true role */
                conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type,
-                                     sec_level, HCI_LE_CONN_TIMEOUT, true);
+                                     sec_level, HCI_LE_CONN_TIMEOUT,
+                                     HCI_ROLE_MASTER);
        }
 
        if (IS_ERR(conn)) {
@@ -3184,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;
@@ -6031,88 +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 something else than mgmt changed the page scan state we
-        * can't differentiate this from a change triggered by adding
-        * the first element to the whitelist. Therefore, avoid
-        * incorrectly setting HCI_CONNECTABLE.
-        */
-       if (connectable && !list_empty(&hdev->whitelist))
-               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_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)
 {
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 55c41de2f5a03c1a1f6de41ca722c7568d511b3d..e49c83d8b95709d7ce602869dffde715df730b58 100644 (file)
@@ -391,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];
 }
@@ -414,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);
@@ -443,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 (test_bit(HCI_CONN_MASTER, &hcon->flags))
+               if (hcon->role == HCI_ROLE_MASTER)
                        method = CFM_PASSKEY;
                else
                        method = REQ_PASSKEY;
@@ -674,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;
@@ -683,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 (test_bit(HCI_CONN_MASTER, &conn->hcon->flags))
+       if (conn->hcon->role != HCI_ROLE_SLAVE)
                return SMP_CMD_NOTSUPP;
 
        if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
@@ -694,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));
@@ -733,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;
 }
 
@@ -750,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 (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags))
+       if (conn->hcon->role != HCI_ROLE_MASTER)
                return SMP_CMD_NOTSUPP;
 
        skb_pull(skb, sizeof(*rsp));
@@ -844,7 +853,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        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 false;
 
@@ -871,9 +880,12 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
        /* 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.
+        * 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))
+       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)
@@ -895,7 +907,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
        if (skb->len < sizeof(*rp))
                return SMP_INVALID_PARAMS;
 
-       if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags))
+       if (hcon->role != HCI_ROLE_MASTER)
                return SMP_CMD_NOTSUPP;
 
        sec_level = authreq_to_seclevel(rp->auth_req);
@@ -911,6 +923,10 @@ 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;
@@ -925,8 +941,6 @@ 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;
 }
 
@@ -951,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 (test_bit(HCI_CONN_MASTER, &hcon->flags))
+       if (hcon->role == HCI_ROLE_MASTER)
                if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
                        return 0;
 
@@ -971,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 (test_bit(HCI_CONN_MASTER, &hcon->flags)) {
+       if (hcon->role == HCI_ROLE_MASTER) {
                struct smp_cmd_pairing cp;
 
                build_pairing_cmd(conn, &cp, NULL, authreq);
@@ -1175,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;
        }
@@ -1193,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) {
diff --git a/net/ieee802154/6lowpan_iphc.c b/net/ieee802154/6lowpan_iphc.c
deleted file mode 100644 (file)
index a1b7117..0000000
+++ /dev/null
@@ -1,791 +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;
-               }
-
-               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 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 c3fd4d275bf42dbd77ebd12f6b7212fcc5f9817c..6d537f03c0baa0450eefc5c22f496be085072cfd 100644 (file)
@@ -66,7 +66,7 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
 static struct ieee80211_chanctx *
 ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_local *local __maybe_unused = sdata->local;
        struct ieee80211_chanctx_conf *conf;
 
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
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 713485f9effc01000d33369b056236307822f838..9713dc54ea4bb385abf1d48ae891ff5fceb7440f 100644 (file)
@@ -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 9e025e1184cc3b95fb9adfcdc768f0cecf5e35ee..ef7a089ac54647eb763b8bbb0793bef98454b48d 100644 (file)
@@ -345,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),
@@ -503,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 {
@@ -815,9 +817,6 @@ struct ieee80211_sub_if_data {
        bool radar_required;
        struct delayed_work dfs_cac_timer_work;
 
-       u8 tdls_peer[ETH_ALEN] __aligned(2);
-       struct delayed_work tdls_peer_del_work;
-
        /*
         * AP this belongs to: self in AP mode and
         * corresponding AP in VLAN mode, NULL for
@@ -926,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 {
@@ -1578,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,
@@ -1730,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);
@@ -1824,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,
index 4edfc7c1524ff9d162e493122a58a2a98bc0ce32..01eede7406a55610e771c7cd906cbedcfff1fa6a 100644 (file)
@@ -1140,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;
@@ -1167,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;
@@ -1672,8 +1701,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                          ieee80211_dfs_cac_timer_work);
        INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
                          ieee80211_delayed_tailroom_dec);
-       INIT_DELAYED_WORK(&sdata->tdls_peer_del_work,
-                         ieee80211_tdls_peer_del_work);
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                struct ieee80211_supported_band *sband;
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 931330bbe00c589c33393c6b3869737641c9b307..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 */
@@ -1005,8 +996,6 @@ static void ieee80211_chswitch_work(struct work_struct *work)
                sdata->csa_block_tx = false;
        }
 
-       ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
-
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_sta_reset_conn_monitor(sdata);
 
@@ -1064,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;
@@ -1091,8 +1080,6 @@ 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,
@@ -2108,8 +2095,6 @@ 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 (sdata->csa_block_tx) {
@@ -3722,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,
@@ -4585,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 5f572bed176100d634a64ba5fcc8af181ce543be..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);
@@ -3129,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 f41177f58b30c483f95596bc3e9d5acf90b00512..c6ee2139fbc579bab35e8cc5bfb6f3b4994930d2 100644 (file)
@@ -1724,12 +1724,15 @@ 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 = local->rate_ctrl;
+       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 |
index 2a04361b2162308358dc5f06796b94fd34aa5c29..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.
@@ -76,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,
@@ -152,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.
@@ -177,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;
index f7185338a0fad33508f17837041cbdb2c2d90d68..1b21050be174b29539a954991d0376dab76fc78f 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/ieee80211.h>
+#include <linux/log2.h>
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "driver-ops.h"
@@ -21,14 +22,14 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
        struct ieee80211_local *local;
 
        sdata = container_of(wk, struct ieee80211_sub_if_data,
-                            tdls_peer_del_work.work);
+                            u.mgd.tdls_peer_del_work.work);
        local = sdata->local;
 
        mutex_lock(&local->mtx);
-       if (!is_zero_ether_addr(sdata->tdls_peer)) {
-               tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
-               sta_info_destroy_addr(sdata, sdata->tdls_peer);
-               eth_zero_addr(sdata->tdls_peer);
+       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);
 }
@@ -46,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;
@@ -63,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
@@ -84,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));
@@ -102,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;
@@ -116,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;
@@ -157,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);
@@ -178,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;
@@ -202,7 +510,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb = NULL;
        bool send_direct;
-       const u8 *init_addr, *rsp_addr;
+       struct sta_info *sta;
        int ret;
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
@@ -210,6 +518,9 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
                                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)
@@ -242,45 +553,48 @@ ieee80211_tdls_prep_mgmt_packet(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);
 
-       /* sanity check for initiator */
+       /* 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_DISCOVERY_REQUEST:
-               if (!initiator) {
-                       ret = -EINVAL;
-                       goto fail;
-               }
+               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:
-               if (initiator) {
-                       ret = -EINVAL;
-                       goto fail;
-               }
+               initiator = false;
                break;
        case WLAN_TDLS_TEARDOWN:
                /* any value is ok */
                break;
        default:
                ret = -ENOTSUPP;
-               goto fail;
+               break;
        }
 
-       if (initiator) {
-               init_addr = sdata->vif.addr;
-               rsp_addr = peer;
-       } else {
-               init_addr = peer;
-               rsp_addr = sdata->vif.addr;
-       }
+       if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
+               initiator = true;
 
-       ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
-                                  sdata->u.mgd.bssid);
+       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;
@@ -327,8 +641,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        mutex_lock(&local->mtx);
 
        /* we don't support concurrent TDLS peer setups */
-       if (!is_zero_ether_addr(sdata->tdls_peer) &&
-           !ether_addr_equal(sdata->tdls_peer, peer)) {
+       if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) &&
+           !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) {
                ret = -EBUSY;
                goto exit;
        }
@@ -336,15 +650,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        /*
         * 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
+        * during setup through the AP path.
+        * Allow error packets to be sent - sometimes we don't even add a STA
+        * before failing the setup.
         */
-       rcu_read_lock();
-       if (!sta_info_get(sdata, peer)) {
+       if (status_code == 0) {
+               rcu_read_lock();
+               if (!sta_info_get(sdata, peer)) {
+                       rcu_read_unlock();
+                       ret = -ENOLINK;
+                       goto exit;
+               }
                rcu_read_unlock();
-               ret = -ENOLINK;
-               goto exit;
        }
-       rcu_read_unlock();
 
        ieee80211_flush_queues(local, sdata);
 
@@ -355,9 +673,9 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        if (ret < 0)
                goto exit;
 
-       memcpy(sdata->tdls_peer, peer, ETH_ALEN);
+       memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN);
        ieee80211_queue_delayed_work(&sdata->local->hw,
-                                    &sdata->tdls_peer_del_work,
+                                    &sdata->u.mgd.tdls_peer_del_work,
                                     TDLS_PEER_SETUP_TIMEOUT);
 
 exit:
@@ -513,11 +831,22 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
                rcu_read_unlock();
 
-               WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
-                            !ether_addr_equal(sdata->tdls_peer, peer));
+               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:
+               /*
+                * 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);
 
@@ -528,9 +857,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
                break;
        }
 
-       if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
-               cancel_delayed_work(&sdata->tdls_peer_del_work);
-               eth_zero_addr(sdata->tdls_peer);
+       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);
index 865bdaf06ff16d8a94f57c79f6d11d7f84a29620..464106c023d8c7d9cb1bd65b39ad642bd1090bc1 100644 (file)
@@ -415,6 +415,9 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
        if (ieee80211_has_order(hdr->frame_control))
                return TX_CONTINUE;
 
+       if (ieee80211_is_probe_req(hdr->frame_control))
+               return TX_CONTINUE;
+
        if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
                info->hw_queue = tx->sdata->vif.cab_queue;
 
@@ -464,6 +467,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
 {
        struct sta_info *sta = tx->sta;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
        struct ieee80211_local *local = tx->local;
 
        if (unlikely(!sta))
@@ -475,6 +479,12 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                     !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {
                int ac = skb_get_queue_mapping(tx->skb);
 
+               if (ieee80211_is_mgmt(hdr->frame_control) &&
+                   !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
+                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
+                       return TX_CONTINUE;
+               }
+
                ps_dbg(sta->sdata, "STA %pM aid %d: PS buffer for AC %d\n",
                       sta->sta.addr, sta->sta.aid, ac);
                if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
@@ -534,19 +544,9 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
 {
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
-
        if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
                return TX_CONTINUE;
 
-       if (ieee80211_is_mgmt(hdr->frame_control) &&
-           !ieee80211_is_bufferable_mmpdu(hdr->frame_control)) {
-               if (tx->flags & IEEE80211_TX_UNICAST)
-                       info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
-               return TX_CONTINUE;
-       }
-
        if (tx->flags & IEEE80211_TX_UNICAST)
                return ieee80211_tx_h_unicast_ps_buf(tx);
        else
index df1bb7e16cfe8e93560eaefef4aa6d69a0b55214..725af7a468d233b502a6de8a07e30d7a25d60e66 100644 (file)
@@ -3083,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 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 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 082f5c62b8cf3c280414bb65b703ca52af13cb1b..df7b1332a1ec2fa80834b94c233f38a427a0f350 100644 (file)
@@ -3814,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 */
index 85474ee501ebbe95599f8ca3f14ba037be690c74..0c524cd76c837ccf23a070a802e1ab405fd235f4 100644 (file)
@@ -2049,7 +2049,8 @@ TRACE_EVENT(cfg80211_michael_mic_failure,
                MAC_ASSIGN(addr, addr);
                __entry->key_type = key_type;
                __entry->key_id = key_id;
-               memcpy(__entry->tsc, tsc, 6);
+               if (tsc)
+                       memcpy(__entry->tsc, tsc, 6);
        ),
        TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm",
                  NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->key_type,